-
Notifications
You must be signed in to change notification settings - Fork 3
Getting Started
Before starting to implement an application using JACIS the steps described in the Installation chapter of this manual have to be done.
Having this done we create a simple example class of objects that shall be stored in the store. The class implements the JacisCloneable
interface to enable the store to clone the object without using reflection to call the clone method.
public class Account implements JacisCloneable<Account> {
private final String name;
private long balance;
public Account(String name) {
this.name = name;
}
@Override
public Account clone() {
try {
return (Account) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError("Could not clone " + this.getClass().getName());
}
}
public Account deposit(long amount) {
balance += amount;
return this;
}
public Account withdraw(long amount) {
balance -= amount;
return this;
}
public String getName() {
return name;
}
public long getBalance() {
return balance;
}
}
As already mentioned the JACIS container is the starting point for all interactions with the JACIS. Therefore we First initialize a JACIS container:
JacisContainer container = new JacisContainer();
Now we create a store for our example object:
JacisObjectTypeSpec<String, Account, Account> objectTypeSpec = //
new JacisObjectTypeSpec<>(String.class, Account.class, new JacisCloningObjectAdapter<>());
JacisStore<String, Account> store = container.createStore(objectTypeSpec).getStore();
The object type spec (class: JacisObjectTypeSpec
) contains all needed information about the objects to store:
-
The first type parameter declares the class of the keys (here:
String
). -
The second type parameter declares the class of the stored objects visible to the outside (here:
Account
). This is the class of the objects in the TX-Views of the store which are also used by the application. -
Internally the store could (depending on the cloning mechanism) store the objects in a different format (see next point). The class used internally to store the objects inside the store is given by the third type parameter.
-
The object adapter defining how the objects are cloned between the store and the TX-Views (see chapter Object Adapter). The default implementation uses Java cloning.
-
An optional extension defining how to do a dirty check for the objects (see chapter Dirty Check).
-
An optional extension defining how persist the modified objects on commit to have a durable representation (see chapter Persistence Adapter).
Now we start a local transaction on the JACIS container (that means on all stores of this container):
JacisLocalTransaction tx = container.beginLocalTransaction();
Now we can work with the store (inside a transaction) and create and update objects. First we create a new Account:
Account account1 = new Account("account1");
All modifications of objects have to be notified to the store explicitly (there is no automatic dirty checking by default).
This is true for modifications as well as for creating new objects. In both cases the store is notified by calling the update
method:
store.update(account1.getName(), account1);
Enough for our first transaction. Now we commit the transaction. Afterwards all other transactions can see our new Account
tx.commit();
For our next transaction we use a helper method executing the passed lambda expression inside a transaction. We use this to deposit some money on our account. Note that the call of the update
is necessary again. If we omit this call the change would be lost after commit (try it).
container.withLocalTx(() -> {
Account acc = store.get("account1");
acc.deposit(100);
store.update("account1", acc);
});
Now we use another transaction to check the balance of the Account:
container.withLocalTx(() -> {
Account acc = store.get("account1");
System.out.println("balance of " + acc.getName() + ": " + acc.getBalance());
});
Finally we withdraw some money and simulate an exception causing the transaction to be rolled back:
try {
container.withLocalTx(() -> {
Account acc = store.get("account1");
acc.withdraw(10);
store.update("account1", acc);
throw new RuntimeException("Error in transaction!");
});
} catch (RuntimeException e) {
System.out.println("Expected exception " + e);
// expected
}
Again we check the balance of the Account to see that the transaction failed and nothing is withdrawn:
container.withLocalTx(() -> {
Account acc = store.get("account1");
System.out.println("balance of " + acc.getName() + ": " + acc.getBalance());
});
Finally we would like to examine if modification done by one transaction can not be seen by another transaction as long as the first transaction is not committed. Since the active transaction is associated with the thread we have to start a second thread to start the second transaction ():
JacisLocalTransaction tx1 = container.beginLocalTransaction();
Account acc = store.get("account1");
System.out.println("balance of " + acc.getName() + " before TX: " + acc.getBalance());
acc.deposit(1000);
store.update(acc.getName(), acc);
System.out.println("balance of " + acc.getName() + " in TX-1 " + acc.getBalance());
Thread thread = new Thread() {
@Override
public void run() {
JacisLocalTransaction tx2 = container.beginLocalTransaction();
System.out.println("balance of " + acc.getName() + " in TX-2: " + store.get("account1").getBalance());
tx2.commit();
}
};
thread.start();
thread.join();
tx1.commit();
}
Next Chapter: Accessing Objects