Example
To understand the concepts of JTX its best to see an example. To make things simpler, our transactional resource will be a simple
String
value. Let's create one such class: public class WorkSession {
static String persistedValue = "jodd";
String sessionValue;
boolean readOnly;
int txno; // transaction number
public WorkSession() { // start session in non-tx mode
}
public WorkSession(int txno) { // start tx session
this.txno = txno;
}
public void writeValue(String value) {
if (txno == 0) { // no transaction
persistedValue = value;
return;
}
// under transaction
if (readOnly == true) {
throw new UncheckedException();
}
sessionValue = value;
}
public String readValue() {
if (sessionValue != null) {
return sessionValue;
}
return persistedValue;
}
// commit
public void done() {
if (sessionValue != null) {
persistedValue = sessionValue;
}
sessionValue = null;
}
// rollback
public void back() {
sessionValue = null;
}
Now we are ready to begin our journey:)
The first thing is to create a
JtxResourceManager
for our resource. The main method to implement is beginTransaction()
. It starts a transaction on our resource depending on transaction mode.Since we will use
JtxTransactionManager
, propagation behavior and timeout will be already supported! Therefore, our resource manager has only to deal with isolation and read-only attribute. We will ignore isolation to make things simpler. Here is how resource manager may look like: public class WorkResourceManager implements JtxResourceManager<WorkSession> {
int txno = 1;
public Class<WorkSession> getResourceType() {
return WorkSession.class;
}
public WorkSession beginTransaction(JtxTransactionMode jtxMode, boolean active) {
if (active == false) {
return new WorkSession();
}
WorkSession work = new WorkSession(txno++);
work.readOnly = jtxMode.isReadOnly();
return work;
}
public void commitTransaction(WorkSession resource) {
resource.done();
txno--;
}
public void rollbackTransaction(WorkSession resource) {
resource.back();
txno--;
}
public void close() {
}
}
Quick overview of what we have done in
beginTransaction()
\: the active
flag tells us if real transaction should be started or we are working in auto-commit mode. When it is set, we create a transaction-aware resource. Since isolation is ignored, we only need to pass read-only flag.Now we are ready to use JTX\:
// [1] create jtx manager and register our resource manager
JtxTransactionManager jtxManager = new JtxTransactionManager();
jtxManager.registerResourceManager(new WorkResourceManager());
// [2] request jtx
JtxTransaction jtx = manager.requestTransaction(
new JtxTransactionMode().propagationRequired().readOnly(true));
// [3] requrest resource i.e. start jtx
WorkSession work = jtx.requestResource(WorkSession.class);
// [4] work
work.writeValue("new value");
// [5] done
jtx.commit();
// [6] cleanup
manager.close();
The most important thing to remember is that in step #2 we are just requesting a jtx transaction. Not until the next step, #3, the real transaction will be started. Again, we are requesting a resource, therefore, if we call it several time in a row, the same resource instance will be returned.
JTX also provides
LeanJtxWorker
, a class that utilizes JtxTransactionManager
and makes it more convenient for use when transaction is requested over different context, i.e. with transaction nesting. Basically, everything stays the same, except LeanJtxWorker
would return null
when new transaction is not created on its request, meaning that current transaction matches the requested transaction attributes (mostly propagation).Last modified 2yr ago