In this section, we describe how to perform operations basic to ClusterJ applications, including the following:
Creating new instances, setting their properties, and saving them to the database
Performing primary key lookups (reads)
Updating existing rows and saving the changes to the database
Deleting rows from the database
Constructing and executing queries to fetch a set of rows meeting certain criteria from the database
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.
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)));
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:
Delete all rows from a table.
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:
begin()
: Begin a transaction.commit()
: Commit a transaction.rollback()
: Roll back a transaction.
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.