JavaSpaces API Write-Through

  GigaSpaces 5.X

Documentation Home
Quick Start Guide
Release Notes

Previous release

  Search Here
Searching GigaSpaces Platform 5.X Documentation

                                               

Summary: Client applications that use the JavaSpace API to write Entry data to a space and require the space to write the data to a different data source, which can be a database or any other type of external application.

This page is specific to:
GigaSpaces 5.x

If you're interested in another version, click it below:
GigaSpaces 6.0
[GigaSpaces 6.5]

Overview

This section discusses client applications that use the JavaSpace API to write Entry data to a space and require the space to write the data to a different data source, which can be a database or any other type of external application. The JavaSpace API write space operation results in a call to the CacheStore Interface user implementation.
For each space write method of the JavaSpace API called in the client application (relevant for the write, update, writeMultiple, updateMultiple, take, takeMultiple operations), there must be a corresponding user implementation of CacheStore Interface methods. The following sections describe in detail how to code these implementations with examples of each.

In case the external data source is a database, the HibernateCacheLoaderImpl/HibernateCacheStoreImpl driver may be used as an alternative that does not require any user implementation.

This section also includes client applications that use ExternalEntry when accessing the space. This is mostly relevant for C++ , C# , JMS and JDBC applications that implicitly use ExternalEntry objects.
The examples of the CacheStore method implementation in this section use the JDBC API to access RDBMS.
In case you want to write an Entry object to a space without persisting it to a data source, mark the Entry object as transient and the CacheStore interface will not be called on a write operation. To mark an Entry object as transient, either have the Entry implement IMetaDataEntry (com.j_spaces.core.client.IMetaDataEntry; see Javadoc) and have isTransient() return true, or have it extend MetaDataEntry and call makeTransient() on the Entry before writing it to space. For POJO objects use annotation or gs.xml.

  • A full running example demonstrating the CacheStore implemented using JDBC is located under: <GigaSpaces Root>\examples\Advanced\Data_Grid\Database-Integration\jdbcCaheStore.
  • A full running example demonstrating the CacheStore implemented using Hibernate is located under: <GigaSpaces Root>\examples\Advanced\Data_Grid\Database-Integration\HibernateCaheStore.
To enable logging for the CacheLoader\Store, edit the <GigaSpaces Root>\config\gs_logging.properties file and set the persistent level to CONFIG or FINER.

For more details, refer to the Settings & Configuration section.

Writing a Single Object

This section describes how to write a single Entry object to a space and to a data source.

The figure illustrates how a client application writes a single Entry object to a space and persists the data in a database or other external application.
The operation proceeds in two stages:

  1. The client application calls the JavaSpace.write method or the IJSpaces.update method, passing the Entry object as first parameter.
    An example for he client application call:
    Lease lease = JavaSpace.write(person, null, FOREVER);
  2. The JavaSpace API writes the Entry object data as a space Entry, and calls the CacheStore interface method store, passing the Entry object data as an IGSEntry object. The store method, which is implemented by the user, uses the JDBC API to verify if the data already exists and performs either an Insert or an Update operation on the database, transforming the IGSEntry object data into the corresponding record format.
The current release only supports the FOREVER lease time for space write operations when using the CacheStore.

CacheStore.store Implementation

The CacheStore interface method store is called by the space and has the following signature key:

store (IGSEntry key, IGSEntry value)

When using the JavaSpace API, the two parameters in the store method are identical, both carry the object data to be written as an IGSEntry object.
Value – See description for key, above.
The object data is extracted from the parameter by using the conversion methods:

Object entry = getConverter.toObject(IGSEntry);

The data from the individual properties of the Entry object can now be extracted and placed into record format for writing to the database using the JDBC API.

Sample Code

In client application: JavaSpaces API Using Entry object:

Lease lease = JavaSpaces.write(person(first,last, id), tnx, lease);

In driver CacheStore implementation: method store:

public class MyCacheStoreSpaceAPIImpl extends AbstractCacheLoader implements CacheStore{
	public void store(Object key, Object value) {
		try {
			Object myObject = getObject((IGSEntry) value);
			Person obj = null;
			Connection con = getConnection();
			if (myObject instanceof Person) {
				obj = (Person) myObject;
				int keyValue = obj.getId().intValue();
				storePerson(keyValue, obj,con );
			}
			con.close();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	private void storePerson(int key, Person person, Connection con)
			throws SQLException {
		PreparedStatement stPd = con.prepareStatement("select ID from "
				+ tableNames.get(Person.class.getName()) + " where ID = ? ");
		stPd.setInt(1, key);

		ResultSet rs = stPd.executeQuery();
		// check if we need to perform update or insert
		if (rs.next()) {
			stPd.close();
			stPd = con.prepareStatement("update "
					+ tableNames.get(Person.class.getName())
					+ " set FirstName =? , LastName =? where ID = ? ");
			stPd.setString(1, person.getFirstName());
			stPd.setString(2, person.getLastName());
			stPd.setInt(3, key);
			stPd.executeUpdate();
			stPd.close();
		} else {
			stPd.close();
			stPd = con.prepareStatement("insert into "
					+ tableNames.get(Person.class.getName())
					+ " (FirstName, LastName, ID) values(?,?,?) ");
			stPd.setString(1, person.getFirstName());
			stPd.setString(2, person.getLastName());
			stPd.setInt(3, key);
			stPd.executeUpdate();
			stPd.close();
		}
	}
}

Writing Multiple Objects

This section describes how to write multiple Entry objects to a space and to a data source.

The figure illustrates how a client application writes an Entry array object data to a space and persists the data in a database or other external application.
The operation proceeds in two stages:

  1. The client application calls the JavaSpace API writeMultiple or updateMultiple methods, passing the Entry array object as first parameter.
    An example for the client application call:
    Lease leases[] = JavaSpace.writeMultiple (entries, null,FOREVER)
  2. The JavaSpace API writes the Entry array object as multiple space Entries, and calls the CacheStore Interface method storeAll, passing the Entry object data as a Map<IGSEntry> object, which is a map of key-value pairs of IGSEntry objects. The storeAll method, which is implemented by the user, uses the JDBC API to verify if the data already exists in the database and performs either an Insert or an Update operation on the database, transforming the IGSEntry object data into the corresponding record format.

CacheStore.storeAll Implementation

The CacheStore Interface method storeAll is called by the space and has the following signature:

storeAll (Map<IGSEntry,IGSEntry> map)

Where the argument is a map of key-value pairs of IGSEntry.
The object data is extracted from the parameter by using the conversion methods:

Object entry = getConverter.toObject(IGSEntry)

The data from the individual properties of the Entry object can now be extracted and placed into record format for writing to the database using the JDBC API.

Sample code

public void storeAll(Map map) {
	Connection connection = null;
	try {
		Object key = null;
		Object myObject = null;
		Person obj = null;
		connection = getConnection();
		connection.setAutoCommit(false);
		// All objects will be stored in one transaction
		Iterator keyIter = map.keySet().iterator();
		while (keyIter.hasNext()) {
			key = keyIter.next();
			myObject = getObject((IGSEntry) key);
			if (myObject instanceof Person) {
				obj = (Person) myObject;
				int keyValue = obj.getId().intValue();
				storePerson(keyValue, obj, connection);
			}
		}
		// commit transaction
		connection.commit();
		connection.close();
		
	} catch (Exception e) {
		try {
			connection.rollback();
			connection.close();
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		throw new RuntimeException(e);
	}
}

Writing Objects as Part of a Transaction

This section describes how to write multiple Entry objects to a space and to a data source as part of a transaction.

The figure illustrates how a client application writes and takes a sequence of Entry object data as part of a transaction to a space and persists the data in a database or other external application.
The operation proceeds in two stages:

  1. The client application calls a sequence of the JavaSpace API operations, possibly including the operations write, take, update, writeMultiple, takeMultiple or updateMultiple, passing the transaction object as second parameter of each call.
    An example for the client application call:
    Lease lease1 = JavaSpace.write ( entry1, txn,FOREVER));
    Lease lease2 = JavaSpace.write ( entry2, txn,FOREVER));
    Entry entry3 = JavaSpace.take(template1 , txn,FOREVER));
    Entry entry4 = JavaSpace.take(template2 , txn,FOREVER));
    txn1.commit(10000);

    Where:
    txn -Non-null transaction

  2. For each operation in the transaction, the JavaSpace API writes the Entry object data as a space Entry, and calls the CacheBulk Interface method store, passing the set of transaction object data as a group of CacheBulk.BulkEntry objects, expressed as List <CacheBulk.BulkEntry>. Each BulkEntry object represents one operation of the transaction and carries the IGSEntry object and the type of the required operation. The store method, which is implemented by the user, extracts the Entry and operation data and uses the JDBC API to perform the required operation on the database, transforming the IGSEntry object data into the corresponding record format.
    In case the transaction is cancelled, both operations on the database and operations on the space are rolled back.

CacheBulk.store Implementation

The CacheStore Interface method store is called by the space and has the following signature:

store (List <BulkEntry> bulk )

The object data and the operation is extracted from the BulkEntry argument using:

Entry entry = BulkEntry.getEntry();
Operation operation = BulkEntry.getOperation();

The Entry and operation data for each transaction operation can now be extracted and placed into record format for the appropriate operation to the database using the JDBC API.

Sample code

public class MyCacheStoreSpaceAPIBulkImpl extends AbstractCacheLoader implements CacheBulk{
	public void store(List bulk) {
		Connection bulkdbConnection = null;
		try {
			bulkdbConnection = getConnection();
			bulkdbConnection.setAutoCommit(false);
			// operations done in one transaction
		} catch (SQLException e1) {
			e1.printStackTrace();
			throw new RuntimeException(e1);
		}
		Iterator iter = bulk.iterator();
		Person person = null;
		try {
			while (iter.hasNext()) {
				BulkEntry entry = (BulkEntry) iter.next();
				IGSEntry e = (IGSEntry) entry.getEntry();
				if (e.getClassName().equals(Person.class.getName())) {
					person = (Person) getObject((IGSEntry) e);
					if (entry.getOperation() == CacheBulk.ERASE) {
						erasePerson(person.id.intValue(), bulkdbConnection);
					}
					if (entry.getOperation() == CacheBulk.STORE) {
						storePerson(person.id.intValue(), person,
								bulkdbConnection);
					}
				}
			}
			// commit transaction
			bulkdbConnection.commit();
			bulkdbConnection.close();
		} catch (Exception e) {
			try {
				bulkdbConnection.rollback();
				bulkdbConnection.close();
			} catch (SQLException e1) {
				throw new RuntimeException(e1);
			}
		}
	}	
	
		private void storePerson(int key, Person person, Connection con)
			throws SQLException {
		PreparedStatement stPd = con.prepareStatement("select ID from "
				+ tableNames.get(Person.class.getName()) + " where ID = ? ");
		stPd.setInt(1, key);

		ResultSet rs = stPd.executeQuery();
		// check if we need to perform update or insert
		if (rs.next()) {
			stPd.close();
			stPd = con.prepareStatement("update "
					+ tableNames.get(Person.class.getName())
					+ " set FirstName =? , LastName =? where ID = ? ");
			stPd.setString(1, person.getFirstName());
			stPd.setString(2, person.getLastName());
			stPd.setInt(3, key);
			stPd.executeUpdate();
			stPd.close();
		} else {
			stPd.close();
			stPd = con.prepareStatement("insert into "
					+ tableNames.get(Person.class.getName())
					+ " (FirstName, LastName, ID) values(?,?,?) ");
			stPd.setString(1, person.getFirstName());
			stPd.setString(2, person.getLastName());
			stPd.setInt(3, key);
			stPd.executeUpdate();
			stPd.close();
		}
	}

	private void erasePerson(int keyValue, Connection con) throws SQLException {
		PreparedStatement stPd = con.prepareStatement("delete from "
				+ tableNames.get(Person.class.getName()) + " where ID = ? ");
		stPd.setInt(1, keyValue);
		stPd.executeUpdate();
		stPd.close();
	}
}
It is recommended to implement the CacheBulk interface when using transactions.

CacheStore.store, erase Implementation

If the CacheBulk interface is not implemented, the CacheStore.store() and CacheStore.erase() corresponding to the appropriate operations are called once the transaction is committed:

CacheStore.store (Map<IGSEntry>)
CacheStore.erase (Map<IGSEntry>)

Example:

public void erase(Object key) {
	try {
		Object myObject = getObject((IGSEntry) key);
		Connection con = getConnection();
		if (myObject instanceof Person) {
			Person obj = (Person) myObject;
			int keyValue = obj.getId().intValue();
			erasePerson(keyValue, con);
		}
		con.close();
	} catch (Exception e) {
		e.printStackTrace();
		throw new RuntimeException(e);
	}
}

Transient Entries

When using the CacheStore, you might not want to persist some Entries to the underlaying database. The CacheStore does not persist Entries that has been identified as transient.

A transient entry can be constructed by extending your Entry class from com.j_spaces.core.client.MetaDataEntry, and calling the MataDataEntry.makeTransient() method when constructing the Entry.


Wiki Content Tree


Your Feedback Needed!

We need your help to improve this wiki site. If you have any suggestions or corrections, write to us at techw@gigaspaces.com. Please provide a link to the wiki page you are referring to.

Labels

 
(None)