This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Originally transactions were all imperatively controlledDirect calls to start, stop, and invalidate the transactionYou have to be very careful to handle every return (especially Exceptions)
Managing transactions this way is verbose and error proneThis lead to the development of declarative transactions
Declarative Transactions use metadata to define the transaction boundaryApplication code is freed from boilerplate, and mistakes
Obviously Declarative Transactions must still be started somehowSome container code must run to manage the transaction lifecycle
Typically transactions are run as intercepting “aspects”These run before and after every intercepted method call
Aspects like this run in one of two ways:The container “weaves” extra bytecode instructions into your classThe container creates a delegating proxy, which wrappers your class
The two different implementations have different advantages and drawbacks
Proxying works best when you can proxy by interfaceFinal classes, final methods and private methods cannot be proxiedCasting to instance types does not workObject identity may be affected
Weaving avoids these drawbacks, but…It either needs a pre-deployment step, or a custom class loader It messes up the Java debugger (there’s a lot of code at line 0)It provides inconsistent behaviour compared to proxying!
Aspects provided by proxies can only run when the proxy is calledIn our method the proxy is not called!@Transactional(SUPPORTS)public void doCheckAndUpdate(String foo) {
// Do some non-transactional work…// Do an updatedoUpdate(foo);
}
As a result the doUpdate method may not always run in a transactionSwitching to weaving will make sure the right boundary is in placeBut your application should not need to care about this!
A vital behaviour for Transactions is rolling back when things go wrongThe most obvious reason for rollback is if the method throws an Exception
Java EE decided to do something very strange (copied by Spring)Unchecked Exceptions trigger rollbackChecked Exceptions do not trigger rollback
The rationale for this is that a checked Exception is part of the APIThis is a horrible thing to do to your users!Exceptions should never be used as control flowAlso SQLException is a checked Exception…
The use of declarative transactions is not 100% positiveYes our code is simpler, but it’s still not always correctWe have traded visible complexity for invisible complexity!
In a modular system we must do betterWe need to express dependencies on the features we needWe need to be able to rely on consistent behaviours using contractsWe need to be able to cope when no central container exists
The OSGi Transaction Control service has two main goalsSimple, explicit management of transactional workModular support for resources with managed lifecycles
What does this mean in practice?You tell Transaction Control what transaction scope you wantYou tell your resources which Transaction Control service to use
From there all the transaction and resource lifecycle is handled for you.
Transaction Control uses a functional API Your business logic is passed as a function to be run within a “scope”A scope can be transactional, or non-transactional
Transaction control provides four well-known options for scoping your workrequired - ensures that a transactional scope is runningrequiresNew - begins a new transactional scopesupports - ensures that a scope (with or without a transaction) is runningnotSupported - ensures that a non-transactional scope is running
It should be obvious that a scope is more than just a transaction
A scope provides a context for your workA place to store stateA place to register completion callbacksAn access boundary for your resources
Scopes provide a safe way to access resources without leaksThe resource is lazily retrieved the first time you use it in a scopeThe same resource is the available throughout the scopeThe resource is automatically released when the scope ends
A scope ends once the work function has returnedResource clean up happens regardless of whether the work is successfulThe value returned by the work is returned by the scope
Transactional work has additional rules for the transaction lifecycle:If the scope has been marked for rollback it always rolls backIf the work exits with an Exception then the transaction rolls backIf the work exits with a normal return then the transaction commits
Specific Exception types can be marked not to cause rollback
Transaction Control works best when used with scoped resources
A Scoped Resource is created from a ResourceProviderResourceProvider#getResource(TransactionControl)
Typically a more specialised sub-interface provides type-safetyJDBCConnectionProvider -> java.sql.ConnectionJPAEntityManagerProvider -> javax.persistence.EntityManager
The returned object is a thread-safe proxyJust use it in your methods whenever you need it!
Connection Pooling in OSGi can be challenging…Most pooling implementations try to directly load the driverThe client often ends up coupled to the pooling implementation
The Resource Provider offers an excellent solution
The JDBCConnectionProvider has built-in support for connection poolingFully configurable using a standard factory serviceProviders can also be defined using Configuration Admin
Transaction Control Scopes can be easily nestedCommitting a quick status update part way through a batchQuerying for sequence numbers outside the current transaction
Each scope is isolated using separate resource instancesThe scopes also have separate contexts and lifecycle callbacks
The Transaction Control service can be used to query the current scopeWhat type of scope is it?Querying for sequence numbers outside the current transaction
Each scope is isolated using separate resource instancesThe scopes also have separate contexts and lifecycle callbacks