MySQL NDB Cluster API Developer Guide

4.2.2.4 ClusterJ Basic Operations

In this section, we describe how to perform operations basic to ClusterJ applications, including the following:

Creating new rows.  To insert a new row into the table, first create a new instance of Employee. This can be accomplished by calling the Session method newInstance(), as shown here:

Employee newEmployee = session.newInstance(Employee.class);

Set the Employee instance properties corresponding with the desired employee table columns. For example, the following sets the id, firstName, lastName, and started properties.

emp.setId(988);

newEmployee.setFirstName("John");
newEmployee.setLastName("Jones");

newEmployee.setStarted(new Date());

Once you are satisfied with the changes, you can persist the Employee instance, causing a new row containing the desired values to be inserted into the employee table, like this:

session.persist(newEmployee);

If autocommit is on, and a row with the same id as this instance of Employee already exists in the database, the persist() method fails. If autocommit is off and a row with the same id as this Employee instance already exists in the database, the persist() method succeeds but a subsequent commit() fails.

If you want the data to be saved even though the row already exists, use the savePersistent() method instead of the persist() method. The savePersistent() method updates an existing instance or creates a new instance as needed, without throwing an exception.

Values that you have not specified are stored with their Java default values (0 for integral types, 0.0 for numeric types, and null for reference types).

Primary key lookups.  You can find an existing row in an NDB table using the Session's find() method, like this:

Employee theEmployee = session.find(Employee.class, 988);

This is equivalent to the primary key lookup query SELECT * FROM employee WHERE id = 988.

ClusterJ also supports compound primary keys. The find() method can take an object array as a key, where the components of the object array are used to represent the primary key columns in the order they were declared. In addition, queries are optimized to detect whether columns of the primary key are specified as part of the query criteria, and if so, a primary key lookup or scan is executed as a strategy to implement the query.

Note

ClusterJ also supports multiple column ordered btree and unique hash indexes. As with primary keys, if a query specifies values for ordered or unique index fields, ClusterJ optimizes the query to use the index for scanning the table.

NDB Cluster automatically spreads table data across multiple data nodes. For some operations—find, insert, delete, and update—it is more efficient to tell the cluster on which data node the data is physically located, and to have the transaction execute on that data node. ClusterJ automatically detects the partition key; if the operation can be optimized for a specific data node, ClusterJ automatically starts the transaction on that node.

Update and save a row.  To update the value of a given column in the row that we just obtained as theEmployee, use the set*() method whose name corresponds to the name of that column. For example, to update the started date for this Employee, use the Employee's setStarted() method, as shown here:

theEmployee.setStarted(new Date(getMillisFor(2010, 01, 04)));
Note

For convenience, we use in this example a method getMillisFor(), which is defined as shown here, in the file AbstractClusterJModelTest.java (found in the storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj directory of the NDB Cluster source tree):

      
/** Convert year, month, day into milliseconds after the Epoch, UTC.
* Set hours, minutes, seconds, and milliseconds to zero.
* @param year the year
* @param month the month (0 for January)
* @param day the day of the month
* @return
*/

protected static long getMillisFor(int year, int month, int day) {
  Calendar calendar = Calendar.getInstance();
  calendar.clear();
  calendar.set(Calendar.YEAR, year);
  calendar.set(Calendar.MONTH, month);
  calendar.set(Calendar.DATE, day);
  calendar.set(Calendar.HOUR, 0);
  calendar.set(Calendar.MINUTE, 0);
  calendar.set(Calendar.SECOND, 0);
  calendar.set(Calendar.MILLISECOND, 0);
  long result = calendar.getTimeInMillis();
  return result;
}

See the indicated file for further information.

You can update additional columns by invoking other Employee setter methods, like this:

theEmployee.setDepartment(3);

To save the changed row back to the NDB Cluster database, use the Session's updatePersistent() method, like this:

session.updatePersistent(theEmployee);

Deleting rows.  You can delete a single row easily using the deletePersistent() method of Session. In this example, we find the employee whose ID is 13, then delete this row from the employee table:

Employee exEmployee = session.find(Employee.class, 13);

session.deletePersistent(exEmployee);'

System.out.println("Deleted employee named " + exEmployee.getFirst()
                 + " " + exEmployee.getLast() + ".");

There also exists a method for deleting multiple rows, which provides two options:

  1. Delete all rows from a table.

  2. Delete an arbitrary collection of rows.

Both kinds of multi-row delete can be performed using the deletePersistentAll() method. The first variant of this method acts on a Class. For example, the following statement deletes all rows from the employee table and returns the number of rows deleted, as shown here:

int numberDeleted = session.deletePersistentAll(Employee);

System.out.println("There used to be "  + numberDeleted + " employees, but now there are none.");

The call to deletePersistentAll() just shown is equivalent to issuing the SQL statement DELETE FROM employee in the mysql client.

deletePersistentAll() can also be used to delete a collection of rows, as shown in this example:

//  Assemble the collection of rows to be deleted...

List<Employee> redundancies = new ArrayList<Employee>();

for (int i = 1000; i < 2000; i += 100) {
  Employee redundant = session.newInstance(Employee.class);
  redundant.setId(i);
  redundancies.add(redundant);
}

numberDeleted = session.deletePersistentAll(redundancies);

System.out.println("Deleted " + numberDeleted + " rows.");

It is not necessary to find the instances in the database before deleting them.

Writing queries.  The ClusterJ QueryBuilder interface is used to instantiate queries. The process begins with obtaining an instance of QueryBuilder, which is supplied by the current Session; we can then obtain a QueryDefinition, as shown here:

QueryBuilder builder = session.getQueryBuilder();

QueryDomainType<Employee> domain = builder.createQueryDefinition(Employee.class);

This is then used to set a column for comparison by the query. Here, we show how to prepare a query that compares the value of the employee table's department column with the constant value 8.

domain.where( domain.get("department").equal(domain.param("department") );

Query<Employee> query = session.createQuery(domain);

query.setParameter("department", 8);

To obtain the results from the query, invoke the Query's getResultList() method, as shown here;

List<Employee> results = query.getResultList();

The return value is a List that you can iterate over to retrieve and process the rows in the usual manner.

Transactions.  The Transaction interface can optionally be used to bound transactions, via the following methods:

It is also possible using Transaction to check whether the transaction is active (via the isActive() method, and to get and set a rollback-only flag (using getRollbackOnly() and setRollbackOnly(), respectively).

If you do not use the Transaction interface, methods in Session that affect the database—such as persist(), deletePersistent(), updatePersistent(), and so on—are automatically enclosed in a database transaction.