Reputation: 155
I've been struggling for some time with JPA (with Hibernate) to bypass the cache and hit the database.
We are constructing a framework that receives the entity that must be saved, but in some cases, I need to check the current data on the database to determine which kind of update to execute (this is a business requirement).
So, I have an incoming attached entity updated by the user, but I need to verify its current database information to determine what business rule to execute (sometimes before and sometimes after the JPA merge() operation).
The only way I thought that it could work was to creating a brand new javax.persistence.EntityManager with the entity manager factory and with this new entity manager, execute the operation, but I got a concurrency error.
Look (this is inside a save() method in a DAO):
EntityManager newEM = currentEntityManager.getEntityManagerFactory().createEntityManager();
newEM.find(e.getClass(), new Short("1"));
But when I try to execute the find() operation, I think that JPA will try to get the instance currently being managed by currentEntityManager instead of trying to hit the database.
javax.persistence.PersistenceException: org.hibernate.exception.LockAcquisitionException: could not load an entity: [com.company.framework.persistence.model.TestEntity]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1694)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1141)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1068)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:334)
at com.sun.proxy.$Proxy32.find(Unknown Source)
Can you guys help me? This is a 2 weeks issue. I am really with no ideas. Thank you very much.
Upvotes: 1
Views: 1280
Reputation: 153840
The easiest solution is to inject the DataSource into your DAO, right beside your entity manager and create a JDBCTemplate:
@Autowired
private final DataSource dataSource;
private final JdbcTemplate jdbcTemplate;
@Override
public void afterPropertiesSet() {
jdbcTemplate = new JDBCTemplate(dataSource);
}
Then use the jdbcTemplate to run your queries, because those will use a different JDBC connection than the EntityManager.
If the connection is bound to the current thread you need to execute your statement in a Callable submitted to an ExecutorService. Make sure you call the Future.get method on your callable result, so that the originating thread waits for the callable to finish.
In Java EE you can also mark your Bean methods with the @NotSupported transaction propagation. This will suspend the current transaction and execute your queries in a separate transaction.
Upvotes: 2