Commit failed silently with Spring JpaTransactionManager - java

I work on a project developed by a team mate but we are facing a strange issue that drive me crazy: database is never updated and there's no exception in the logs. Here's the code:
#Service
#Transactional
public class InterventionProjectResultIntegrator implements IInterventionProjectResultIntegrator {
private static final ILogger logger = ComponentLogger.getInstance(InterventionProjectResultIntegrator.class);
private Dao dao;
private String APPLICATION = "APP";
#Autowired
public void setDao(Dao dao){
this.dao = dao;
}
#Override
public void integrateResponse() {
try {
List<ResponseEntity> responseListByStatus = dao.findAllResponseByStatus(Dao.STATUS_EN_COURS, APPLICATION);
for (ResponseEntity response: responseListByStatus ) {
response.setStatus(Dao.STATUS_OK);
dao.mergeResponseEntity(response);
}
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
throw ex;
}
}
}
As you can see the function is pretty simple:
Getting objects from database
Loop over the objects
Update Each object status
At the end of the loop commit changes
Everything run fine except that the objects are not updated on database and that there is no exception.
The Dao come from a maven dependency that worked fine in another project so I assume that the problem is related to the new one.
I can see following logs in console:
org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder#6dcee890] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#713e49c3] bound to thread org.hibernate.event.internal.AbstractSaveEventListener - Persistent instance of: com.domain.ResponseEntity
org.hibernate.event.internal.DefaultMergeEventListener - Ignoring persistent instance
org.hibernate.action.internal.UnresolvedEntityInsertActions - No entity insert actions have non-nullable, transient entity dependencies.
Did you already face similar issue ?
Regards.
[EDIT 1]
As pointed out in comment, I replaced manual transaction handling with #Transactional annotation. See the code updated.
So now I have a new line in the logs, but the same result, object is not save in database.
org.springframework.transaction.interceptor.TransactionAspectSupport - Completing transaction for [com.response.InterventionProjectResultIntegrator.integrateResponse]
As asked the DAO source. This code is not under my responsability, and worked like a charm in another context.
#Repository
public class Dao {
public static final ILogger logger = ComponentLogger.getInstance(Dao.class);
public static final String STATUS_EN_COURS = "PENDING";
public static final String STATUS_OK = "OK";
public static final String STATUS_ERROR = "ERROR";
#PersistenceContext
protected EntityManager entityManager;
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void mergeMvzResponseEntity(ResponseEntity responseEntity) {
if(entityManager != null) {
this.entityManager.merge(responseEntity);
} else {
logger.error("Entity manager not initialized");
}
}

As suggested, I have reworked the source code to use #Transactional annotation, and to let Spring handle transaction stuff:
#Service
#Transactional
public class InterventionProjectResultIntegrator implements IInterventionProjectResultIntegrator {
private static final ILogger logger = ComponentLogger.getInstance(InterventionProjectResultIntegrator.class);
private Dao dao;
private String APPLICATION = "APP";
#Autowired
public void setDao(Dao dao){
this.dao = dao;
}
#Override
public void integrateResponse() {
try {
List<ResponseEntity> responseListByStatus = dao.findAllResponseByStatus(Dao.STATUS_EN_COURS, APPLICATION);
for (ResponseEntity response: responseListByStatus ) {
response.setStatus(Dao.STATUS_OK);
dao.mergeResponseEntity(response);
}
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
throw ex;
}
}
}
Then added this line to my xml configuration file:
<tx:annotation-driven/>
Now it works like a charm. Thanks to #M. Deinum to have pointed it out.

Related

How to use Hibernate #Filter without #Transactional?

I am using Hibernate #Filter with Spring Data to add specific "where" clause for every query in my project. The problem is that it works as long as I use #Transactional annotation for my 'findAll' method. Is there any way to avoid using #Transactional? Why is it important here?
Here is the filter config:
#FilterDef(name = "testFilter",
parameters = #ParamDef(name = "company_id", type = "long"),
defaultCondition = "company_id=:companyId")
#Filter(name = "testFilter")
#EntityListeners(EmployeeListener.class)
#NoArgsConstructor
public class Employee {//basic fields}
Repository:
#Repository
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, UUID> {
}
Aspect that enables the filter:
#Aspect
#Component
public class TestAspect {
private final SecurityAspect securityAspect;
private final Logger logger;
#PersistenceContext
private EntityManager entityManager;
#Autowired
public TestAspect(SecurityAspect securityAspect, Logger logger) {
this.securityAspect = securityAspect;
this.logger = logger;
}
#Around("execution(* org.springframework.data.repository.CrudRepository+.findAll(..))")
public Object aroundFindAllTenantAware(ProceedingJoinPoint joinPoint) {
Session session = this.entityManager.unwrap(Session.class);
session.enableFilter("testFilter")
.setParameter("companyId", this.securityAspect.getCompanyId());
Object retValue = null;
try {
retValue = joinPoint.proceed();
} catch (Throwable throwable) {
logger.error(throwable.getMessage(), throwable);
} finally {
session.disableFilter("testFilter");
}
return retValue;
}
}
And, finally, my service:
#Service
#Transactional
public class EmployeeApiServiceImpl {
private final EmployeeRepository repository;
#Autowired
public EmployeeApiServiceImpl(EmployeeRepository repository) {
this.employeeRepository = employeeRepository; }
#Override
public Response listProfiles(SecurityContext securityContext) {
List<Employee> employees = repository.findAll();
return Response.ok().entity(employees).build();
}
Without #Transactional annotation, it does not work. When I am debugging the aspect I can see that the filter was enabled but the query did not change. When I put the annotation, everything works fine. But I don't get why it is happening. This annotation is not supposed to be on read methods, plus in every tutorial everything works without it, but not in my case.
After some research I found a solution - using TransactionTemplate class. So I just need to put my code from aspect into method 'execute' and it will do the same as #Transactional annotation. But in this case, I don't have to annotate each method in each service as Transactional. These articles helped me a lot:
https://www.baeldung.com/spring-programmatic-transaction-management
https://www.baeldung.com/transaction-configuration-with-jpa-and-spring
transactionTemplate.execute(status -> {
Session session = null;
Object result = null;
try {
session = entityManager.unwrap(Session.class);
session.enableFilter(Constants.FILTER_NAME)
.setParameter(Constants.PARAMETER_NAME, this.securityAspect.getCompanyId());
result = joinPoint.proceed();
} catch (Throwable throwable) {
logger.error(throwable.getMessage(), throwable);
} finally {
if (session != null) {
session.disableFilter(Constants.FILTER_NAME);
}
}
return result;
});
The problem is the EntityManager proxy that is injected by Spring which only supports certain operations without a transaction. You'll need a custom proxy implementation if you want support for this. See org.springframework.orm.jpa.SharedEntityManagerCreator.SharedEntityManagerInvocationHandler#invoke

JPA EntityManager not working when using Guice's PrivateModule

I have a service with a persistence setup using JPA, Hibernate and Guice (if it's useful, I'm not using Spring). This is the first, working version of my code:
public class BookDao {
#Inject
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) {
getEntityManager().persist(book);
}
}
public class MyAppModule extends AbstractModule {
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
final JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
But now I need to configure multiple persistence units. I'm following the advice in this mailing list, and according to them, I should move my module logic to a private module. I did as suggested and created a second version of the same code, the changes are commented below:
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ FIELD, PARAMETER, METHOD })
public #interface ProductionDataSource {} // defined this new annotation
public class BookDao {
#Inject
#ProductionDataSource // added the annotation here
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) throws Exception {
getEntityManager().persist(book);
}
}
public class MyAppModule extends PrivateModule { // module is now private
#Override
protected void configure() {
initializePersistence();
// expose the annotated entity manager
Provider<EntityManager> entityManagerProvider = binder().getProvider(EntityManager.class);
bind(EntityManager.class).annotatedWith(ProductionDataSource.class).toProvider(entityManagerProvider);
expose(EntityManager.class).annotatedWith(ProductionDataSource.class);
}
private void initializePersistence() {
JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
The newly annotated EntityManager is being correctly injected by Guice and is non-null, but here's the fun part: some of my unit tests started failing, for example:
class BookDaoTest {
private Injector injector;
private BookDao testee;
#BeforeEach
public void setup() {
injector = Guice.createInjector(new MyAppModule());
injector.injectMembers(this);
testee = injector.getInstance(BookDao.class);
}
#Test
public void testPersistBook() throws Exception {
// given
Book newBook = new Book();
assertNull(newBook.getId());
// when
newBook = testee.persist(newBook);
// then
assertNotNull(newBook.getId()); // works in the first version, fails in the second
}
}
In the first version of my code the last line above just works: the entity is persisted and has a new id. However, in the second version of my code (using a PrivateModule and exposing an annotated EntityManager from it) the persist() operation doesn't work anymore, the entity is without an id. What could be the problem? I didn't do any other configuration changes in my environment, and I don't see error messages in the logs. Let me know if you need more details.
It turns out that the problem was the #Transactional annotation. In the first version of my code, Guice automatically adds interceptors for managing the transaction. By doing a debug, I found out that before executing my persist(Book book) method, Guice calls the following method from the com.google.inject.internal.InterceptorStackCallback package:
public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy)
In the second version of my code, when I exposed the persistence unit from a private module the above interceptor was no longer called, leaving my persist operation without transaction handling. This is a known issue and is by design.
As a workaround I had to implement transactions by hand, making my code more verbose. I also had to change the way the entity manager is injected. This solution worked for me:
public class BookDao {
#Inject
#Named(PROD_PERSISTENCE_UNIT_NAME)
private EntityManagerFactory entityManagerFactory;
private EntityManager getEntityManager() {
return entityManagerFactory.createEntityManager();
}
public void persist(Book book) throws Exception {
EntityManager em = getEntityManager();
try {
em.getTransaction().begin();
em.persist(book);
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
throw e;
} finally {
em.close();
}
}
}
public class MyAppModule extends PrivateModule {
public static final String PROD_PERSISTENCE_UNIT_NAME = "prod";
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
// persistence unit set to prod DB
final JpaPersistModule jpaPersistModule = new JpaPersistModule(PROD_PERSISTENCE_UNIT_NAME);
// connection properties set to suitable prod values
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
// expose bindings to entity manager annotated as "prod"
bind(JPAInitializer.class).asEagerSingleton();
bind(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).to(PersistService.class).asEagerSingleton();
expose(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManagerFactory.class));
expose(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManager.class));
expose(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(UnitOfWork.class));
expose(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
}
}
As a lesson, be very watchful around annotations and other such "magic" that modifies your code under the hood, finding bugs becomes quite difficult.

#Inject gives a NullPointer Exception in java EE

I'm trying to create a webservice that gives some results taken through hibernate from the database.
#Path("/book")
public class BookService {
#Inject
private dbController db;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getBookTitle() {
return "H2G2";
}
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_JSON)
public Response getUsers(){
List<UserEntity> users = db.getUsers();
return Response.ok(users,MediaType.APPLICATION_JSON).build();
}
}
the db variable whenever I call http://localhost/book/users is always null.
The dbController is:
public class dbController {
#Inject
private HibernateUtil util;
public List<UserEntity> getUsers(){
List<UserEntity> result = null;
try{
result = (List<UserEntity>) this.util.createQuery("select e from UserEntity e");
}catch (Exception e){
System.out.println(e.getMessage());
}
return result;
}
}
and the HibernateUtil is:
public class HibernateUtil {
private static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("NewPersistenceUnit");
private EntityManager entityManager = null;
private void createEntityManager(){
if (this.entityManager==null){
this.entityManager = entityManagerFactory.createEntityManager(); // here is your persistence unit name
}
}
private void closeConnection(){
this.entityManager.close();
this.entityManager = null;
}
public List createQuery(String query) throws Exception{
this.createEntityManager();
List result;
try{
result = this.entityManager.createQuery(query).getResultList();
}catch (Exception e){
throw new Exception(e.getMessage());
}
return result;
}
}
I'm using intellij and I added a break point at the db.getUsers() and set the variable db by adding new dbController(). However, the Intellij gives me the error "Class not loaded : controller.dbController".
The hibernate works for sure...so the problem is not there. It's the first time I'm trying to use Dependency Injection, but I'm not sure what I'm doing wrong.
Thanks
You cannot inject POJO it has to be a Bean. So making it a bean requires the annotations, for example:
#Stateful
#LocalBean
public class dbController {..} // Should be DbController, start with CAPS
and
#Stateful // or maybe #Stateless ?
#LocalBean
public class HibernateUtil {..}
Then when you have a Bean it is not allowed to use static final so change like this is needed:
private EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("NewPersistenceUnit");
But actually the easiest way to get EntityManager is just to inject it also. Like:
#PersistenceContext// maybe also with unit name (unitName = "whatever_the_unit_name_is")
private EntityManager em;

Spring TransactionTemplate called from a #Transaction method

I've a particular flow calls in my Spring app.
I've a repository, and I call the method sell() as first call.
#Service
#Transactional
public class ServiceRepositoryImpl implements ServiceRepository {
#Inject
private SellRepository sellRepository;
#Override
public long sell(long id)
....
....
....
Sell sell = new Sell();
...
sellRepository.save(sell);
}
and my SellService:
#Service
public class SellRepositoryImpl implements SellRepository {
#PersistenceContext
private EntityManager entityManager;
#Inject
SellHelperRepository sellHelperRepository;
#Inject
private TransactionTemplate transactionTemplate;
#Override
public <S extends Sell> S save(S sell) throws Exception {
transactionTemplate.execute(new TransactionCallback<S>() {
#Override
public S doInTransaction(TransactionStatus status) {
try {
...
entityManager.persist(sell);
} catch (Throwable e) {
log.error("", e);
status.setRollbackOnly();
}
return vendita;
}
});
sellHelperRepository.createTickets(sell.getId());
return vendita;
}
this is my sellHelperRepository:
#Service
#Transactional
public class SellHelperRepositoryImpl implements SellHelperRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
#Async
public void createTickets(long sellID) {
Sell sell = entityManager.find(Sell.class, sellID);
try{
...
Ticket t = new Ticket();
ticketService.save(t);
}catch(Throwable e){
sell.setStatus("CANCELED");
}
}
and in the end my ticketService:
#Service
#Transactional
public class TicketRepositoryImpl implements TicketRepository {
#PersistenceContext
protected EntityManager entityManager;
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Biglietto save(Ticket ticket) throws Exception {
entityManager.persist(ticket);
}
The problem is that in this chain of calls when I'm on SellRepositoryImpl, the object sell is not persisted asap I'm out of the transactiontemplate but it should be! So when I enter in SellHelperRepositoryImpl and I try to search the sell it doesn't find it!
This particular structure is needed because these methods are used also from others repository.
I created many junit tests case to check if all works fine; for the rest of tests all works fine, a part for this particular chain of calls.
I think I'm missing something....
Thanks
the problem with your code is that you are using the #Async on the createTickets method , which forces Spring to execute it in the different thread with the "fresh" transaction, so you need to flush/commit first transaction which was opened in the SellRepositoryImpl class.
So you can go in three ways
Remove the #Async from the createTickets method
Change the Transaction Isolation Level from DEFAULT to READ_UNCOMMITTED in SellHelperRepositoryImpl
#Service
#Transactional(isolation=Isolation.READ_UNCOMMITTED)
public class SellHelperRepositoryImpl implements SellHelperRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
#Async
public void createTickets(long sellID) {
Sell sell = entityManager.find(Sell.class, sellID);
try{
...
Ticket t = new Ticket();
ticketService.save(t);
}catch(Throwable e){
sell.setStatus("CANCELED");
}
}
Flush the manually managed transaction in save(S sell), see following code snippet.
#Override
public <S extends Sell> S save(S sell) throws Exception {
transactionTemplate.execute(new TransactionCallback<S>() {
#Override
public S doInTransaction(TransactionStatus status) {
try {
...
entityManager.persist(sell);
entityManager.getTransaction().commit();
status.flush();
} catch (Throwable e) {
log.error("", e);
status.setRollbackOnly();
}
return vendita;
}
});
sellHelperRepository.createTickets(sell.getId());
return vendita;
}

Guice, JDBC and managing database connections

I'm looking to create a sample project while learning Guice which uses JDBC to read/write to a SQL database. However, after years of using Spring and letting it abstract away connection handling and transactions I'm struggling to work it our conceptually.
I'd like to have a service which starts and stops a transaction and calls numerous repositories which reuse the same connection and participate in the same transaction. My questions are:
Where do I create my Datasource?
How do I give the repositories access to the connection? (ThreadLocal?)
Best way to manage the transaction (Creating an Interceptor for an annotation?)
The code below shows how I would do this in Spring. The JdbcOperations injected into each repository would have access to the connection associated with the active transaction.
I haven't been able to find many tutorials which cover this, beyond ones which show creating interceptors for transactions.
I am happy with continuing to use Spring as it is working very well in my projects, but I'd like to know how to do this in pure Guice and JBBC (No JPA/Hibernate/Warp/Reusing Spring)
#Service
public class MyService implements MyInterface {
#Autowired
private RepositoryA repositoryA;
#Autowired
private RepositoryB repositoryB;
#Autowired
private RepositoryC repositoryC;
#Override
#Transactional
public void doSomeWork() {
this.repositoryA.someInsert();
this.repositoryB.someUpdate();
this.repositoryC.someSelect();
}
}
#Repository
public class MyRepositoryA implements RepositoryA {
#Autowired
private JdbcOperations jdbcOperations;
#Override
public void someInsert() {
//use jdbcOperations to perform an insert
}
}
#Repository
public class MyRepositoryB implements RepositoryB {
#Autowired
private JdbcOperations jdbcOperations;
#Override
public void someUpdate() {
//use jdbcOperations to perform an update
}
}
#Repository
public class MyRepositoryC implements RepositoryC {
#Autowired
private JdbcOperations jdbcOperations;
#Override
public String someSelect() {
//use jdbcOperations to perform a select and use a RowMapper to produce results
return "select result";
}
}
If your database change infrequently, you could use the data source that comes with the database's JDBC driver and isolate the calls to the 3rd party library in a provider (My example uses the one provided by the H2 dataabse, but all JDBC providers should have one). If you change to a different implementation of the DataSource (e.g. c3PO, Apache DBCP, or one provided by app server container) you can simply write a new Provider implementation to get the datasource from the appropriate place. Here I've use singleton scope to allow the DataSource instance to be shared amongst those classes that depend on it (necessary for pooling).
public class DataSourceModule extends AbstractModule {
#Override
protected void configure() {
Names.bindProperties(binder(), loadProperties());
bind(DataSource.class).toProvider(H2DataSourceProvider.class).in(Scopes.SINGLETON);
bind(MyService.class);
}
static class H2DataSourceProvider implements Provider<DataSource> {
private final String url;
private final String username;
private final String password;
public H2DataSourceProvider(#Named("url") final String url,
#Named("username") final String username,
#Named("password") final String password) {
this.url = url;
this.username = username;
this.password = password;
}
#Override
public DataSource get() {
final JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
static class MyService {
private final DataSource dataSource;
#Inject
public MyService(final DataSource dataSource) {
this.dataSource = dataSource;
}
public void singleUnitOfWork() {
Connection cn = null;
try {
cn = dataSource.getConnection();
// Use the connection
} finally {
try {
cn.close();
} catch (Exception e) {}
}
}
}
private Properties loadProperties() {
// Load properties from appropriate place...
// should contain definitions for:
// url=...
// username=...
// password=...
return new Properties();
}
}
To handle transactions a Transaction Aware data source should be used. I wouldn't recommend implementing this manually. Using something like warp-persist or a container supplied transaction management, however it would look something like this:
public class TxModule extends AbstractModule {
#Override
protected void configure() {
Names.bindProperties(binder(), loadProperties());
final TransactionManager tm = getTransactionManager();
bind(DataSource.class).annotatedWith(Real.class).toProvider(H2DataSourceProvider.class).in(Scopes.SINGLETON);
bind(DataSource.class).annotatedWith(TxAware.class).to(TxAwareDataSource.class).in(Scopes.SINGLETON);
bind(TransactionManager.class).toInstance(tm);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), new TxMethodInterceptor(tm));
bind(MyService.class);
}
private TransactionManager getTransactionManager() {
// Get the transaction manager
return null;
}
static class TxMethodInterceptor implements MethodInterceptor {
private final TransactionManager tm;
public TxMethodInterceptor(final TransactionManager tm) {
this.tm = tm;
}
#Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Start tx if necessary
return invocation.proceed();
// Commit tx if started here.
}
}
static class TxAwareDataSource implements DataSource {
static ThreadLocal<Connection> txConnection = new ThreadLocal<Connection>();
private final DataSource ds;
private final TransactionManager tm;
#Inject
public TxAwareDataSource(#Real final DataSource ds, final TransactionManager tm) {
this.ds = ds;
this.tm = tm;
}
public Connection getConnection() throws SQLException {
try {
final Transaction transaction = tm.getTransaction();
if (transaction != null && transaction.getStatus() == Status.STATUS_ACTIVE) {
Connection cn = txConnection.get();
if (cn == null) {
cn = new TxAwareConnection(ds.getConnection());
txConnection.set(cn);
}
return cn;
} else {
return ds.getConnection();
}
} catch (final SystemException e) {
throw new SQLException(e);
}
}
// Omitted delegate methods.
}
static class TxAwareConnection implements Connection {
private final Connection cn;
public TxAwareConnection(final Connection cn) {
this.cn = cn;
}
public void close() throws SQLException {
try {
cn.close();
} finally {
TxAwareDataSource.txConnection.set(null);
}
}
// Omitted delegate methods.
}
static class MyService {
private final DataSource dataSource;
#Inject
public MyService(#TxAware final DataSource dataSource) {
this.dataSource = dataSource;
}
#Transactional
public void singleUnitOfWork() {
Connection cn = null;
try {
cn = dataSource.getConnection();
// Use the connection
} catch (final SQLException e) {
throw new RuntimeException(e);
} finally {
try {
cn.close();
} catch (final Exception e) {}
}
}
}
}
I would use something like c3po to create datasources directly. If you use ComboPooledDataSource you only need instance (pooling is done under the covers), which you can bind directly or through a provider.
Then I'd create an interceptor on top of that, one that e.g. picks up #Transactional, manages a connection and commit/ rollback. You could make Connection injectable as well, but you need to make sure you close the connections somewhere to allow them to be checked into the pool again.
To inject a data source, you probably don't need to be bound to a single data source instance since the database you are connecting to features in the url. Using Guice, it is possible to force programmers to provide a binding to a DataSource implementation (link) . This data source can be injected into a ConnectionProvider to return a data source.
The connection has to be in a thread local scope. You can even implement your thread local scope but all thread local connections must be closed & removed from ThreadLocal object after commit or rollback operations to prevent memory leakage. After hacking around, I have found that you need to have a hook to the Injector object to remove ThreadLocal elements. An injector can easily be injected into your Guice AOP interceptor, some thing like this:
protected void visitThreadLocalScope(Injector injector,
DefaultBindingScopingVisitor visitor) {
if (injector == null) {
return;
}
for (Map.Entry, Binding> entry :
injector.getBindings().entrySet()) {
final Binding binding = entry.getValue();
// Not interested in the return value as yet.
binding.acceptScopingVisitor(visitor);
}
}
/**
* Default implementation that exits the thread local scope. This is
* essential to clean up and prevent any memory leakage.
*
* The scope is only visited iff the scope is an sub class of or is an
* instance of {#link ThreadLocalScope}.
*/
private static final class ExitingThreadLocalScopeVisitor
extends DefaultBindingScopingVisitor {
#Override
public Void visitScope(Scope scope) {
// ThreadLocalScope is the custom scope.
if (ThreadLocalScope.class.isAssignableFrom(scope.getClass())) {
ThreadLocalScope threadLocalScope = (ThreadLocalScope) scope;
threadLocalScope.exit();
}
return null;
}
}
Make sure you call this after the method has been invoked and closing the connection. Try this to see if this works.
Please check the solution I provided: Transactions with Guice and JDBC - Solution discussion
it is just a very basic version and simple approach. but it works just fine to handle transactions with Guice and JDBC.

Categories

Resources