Spring boot transaction cannot work in timer - java

I have a transaction like below,
#Transactional
public void changeJobStatus(Long jobId){
JobEntity jobEntity = jobRepository.findOneForUpdate(jobId);
...
}
And findOneForUpdate is to lookup database with pessimistic lock,
public interface JobRepository extends CrudRepository<JobEntity, Long>{
#Lock(LockModeType.PESSIMISTIC_WRITE)
#Query("select j from JobEntity j where j.id = :id")
JobEntity findOneForUpdate(#Param("id") Long id);
}
This works well, if I call changeJobStatus normally.
But when calling in a TimerTask like below,
TimerTask task = new TimerTask() {
#Override
public void run() {
changeJobStatus(jobId);
}
};
timer.schedule(task, waitTime);
there would be an exception:
javax.persistence.TransactionRequiredException: no transaction is in progress
Why this happens? And if there is a way to call transaction in a TimerTask?

The call to changeJobStatus() is effectively direct to your bean (self-invocation), and therefore not subject to the usual Spring proxying when calling between beans. For this reason no transaction is getting started.
See: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative-annotations search for "self-invocation".
There may be several potential ways to approach this:
You could auto-wire a reference to your own bean, which would be fulfilled with a proxy, and call thru that;
You could use mode="aspectj", which performs bytecode weaving (enhancement).
You could control the transaction manually via PlatformTransactionManager;
My approach would depend on whether this is isolated, or a common case. If common, I'd investigate "aspectj" mode; but I would probably hope that it were an outlier and I could stick to the standard Spring "proxy" mode.

This is caused by Spring's AOP limitation. As Thomas has suggested, controlling transaction manually can solve this problem, rather than using #Transactional. Here is the detail implementation,
I have created an simple transaction service like below,
#Service
public class SimpleTransactionService {
private final TransactionTemplate transactionTemplate;
#Autowired
public SimpleTransactionService(PlatformTransactionManager transactionManager){
transactionTemplate = new TransactionTemplate(transactionManager);
}
public void executeTransaction(ITransactionService task){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
task.transactionExecute();
}
});
}
}
ITransactionService is just an simple interface with one method,
public interface ITransactionService {
void transactionExecute();
}
Here is how I use above in my TimerTask,
public void addTimerTask(Object param, Long waitTime){
TimerTask task = new TimerTask() {
#Override
public void run() {
simpleTransactionService.executeTransaction(() -> someOperation(param));
}
};
timer.schedule(task, waitTime);
}
someOperation is the actual transaction executed. With the simple transaction service and a lambda, transaction can be done without any annotation.

Related

Set pessimistic lock on entity with EntityManager

Consider the following situation:
We receive a request from a web service which updates our entity. Sometimes we might get two requests at (almost) the same time. We had situations in which our entity looked completely wrong, because of concurrent updates. The idea is to lock the entity pessimistic so that whenever the first request comes it instantly locks the entity and the second request can't touch it (Optimistic locking is no alternative for us). I wrote an integration test to check this behaviour.
I got an integration test which looks like the following:
protected static TestRemoteFacade testFacade;
#BeforeClass
public static void setup() {
testFacade = BeanLocator.lookupRemote(TestRemoteFacade.class, TestRemoteFacade.REMOTE_JNDI_NAME, TestRemoteFacade.NAMESPACE);
}
#Test
public void testPessimisticLock() throws Exception {
testFacade.readPessimisticTwice();
}
which calls the bean
#Stateless
#Clustered
#SecurityDomain("myDomain")
#RolesAllowed({ Roles.ACCESS })
public class TestFacadeBean extends FacadeBean implements TestRemoteFacade {
#EJB
private FiolaProduktLocalFacade produkt;
#Override
public void readPessimisticTwice() {
produkt.readPessimisticTwice();
}
}
with produkt being a bean itself
#Stateless
#Clustered
#SecurityDomain("myDomain")
#RolesAllowed({ Roles.ACCESS })
public class ProduktFacadeBean implements ProduktLocalFacade {
#Override
public void readPessimisticTwice() {
EntityManager entityManager = MyService.getCrudService().getEntityManager();
System.out.println("Before first try.");
entityManager.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE);
System.out.println("Before second try.");
entityManager.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE);
System.out.println("After second try.");
}
}
with
public class MyService {
public static CrudServiceLocalFacade getCrudService() {
return CrudServiceLookup.getCrudService();
}
}
public final class CrudServiceLookup {
private static CrudServiceLocalFacade crudService;
private CrudServiceLookup(){
}
public static CrudServiceLocalFacade getCrudService() {
if (crudService == null)
crudService = BeanLocator.lookup(CrudServiceLocalFacade.class, CrudServiceLocalFacade.LOCAL_JNDI_NAME);
return crudService;
}
public static void setCrudService(CrudServiceLocalFacade crudService) {
CrudServiceLookup.crudService = crudService;
}
}
#Stateless
#Local(CrudServiceLocalFacade.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
#Interceptors(OracleDataBaseInterceptor.class)
public class CrudServiceFacadeBean implements CrudServiceLocalFacade {
private EntityManager em;
#Override
#PersistenceContext(unitName = "persistence_unit")
public void setEntityManager(EntityManager entityManager) {
em = entityManager;
}
#Override
public EntityManager getEntityManager() {
return em;
}
}
The problem that arises now is: If I start the integration test once with a breakpoint at System.out.println("Before second try."); and then start the integration test a second time, the latter one can still read MyEntity. Remarkable is that they were different instances (I made this observation on the instanceId in debug mode). This suggests that the entityManager didn't share his hibernate context.
I made the following observations:
Whenever I call a setter on entity and save it to the db, the lock is aquired. But this is not what I need. I need the lock without having modified the entity.
I tried the method entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE) as well, but the behaviour was the same.
I found Transaction settings in DBVisualizer. At the moment it is set to TRANSACTION_NONE. I tried all the others (TRANSACTION_READ_UNCOMMITTED, TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ, TRANSACTION_SERIALIZABLE) as well, without any success.
Let the first thread read the entity, then the second thread read the same entity. Let the first tread modify the entity and then the second modify it. Then let both save the entity and whoever saves the entity last wins and no exceptions will be thrown.
How can I read an object pessimistic, that means: Whenever I load an entity from the db I want it to be locked immediately (even if there was no modification).
Both ways you describe ie.
em.find(MyEntity.class, 1, LockModeType.PESSIMISTIC_WRITE)
em.lock(entity, LockModeType.PESSIMISTIC_WRITE)
hold a lock on the related row in database but only for the the entityManager lifespan, ie. for the time of the enclosing transaction, the lock will be so automatically released once you've reached the end of the transaction
#Transactional()
public void doSomething() {
em.lock(entity, LockModeType.PESSIMISTIC_WRITE); // entity is locked
// any other thread trying to update the entity until this method finishes will raise an error
}
...
object.doSomething();
object.doSomethingElse(); // lock is already released here
Have you tried to set the isolation level in your application server?
To get a lock on a row no matter what you are trying to do afterwards (read/write), you need to set the isolation level to TRANSACTION_SERIALIZABLE.
Lock fails only if another thread is already holding the lock. You can take two FOR UPDATE locks on single row in DB, so it's not JPA-specific thing.

How do I connect with play persistence (JPA) in Akka actors?

I'm using play framework and JPA. Few messages are passed to the Akka Actors to async processing. Inside async process, I need to connect my database through JPA.
public class OrderCreation extends UntypedActor {
private EntityManagerFactory emFact = null;
private ActorSelection provisioner;
#Transactional(readOnly = false)
#Override
public void onReceive(Object order) throws Exception {
//HERE I need to do JPA related transactions
}
#Override
public void postStop() throws Exception {
}
#Override
public void preStart() throws Exception {
provisioner =getContext().actorSelection("/user/OrderProvisioner");
emFact = Persistence.createEntityManagerFactory("test-data-play");
}
}
I got this error
[akka://application/user/OrderCreation] No EntityManager bound to this thread. Try wrapping this call in JPA.withTransaction, or ensure that the HTTP context is setup on this thread.
java.lang.RuntimeException: No EntityManager bound to this thread. Try wrapping this call in JPA.withTransaction, or ensure that the HTTP context is setup on this thread.
at play.db.jpa.JPA.em(JPA.java:58)
Anybody has an idea to connect JPA through Akka?
#Transactional is an action composition, it will only work in Controllers.
You need to inject JPAApi in your actor and use jpaApi.withTransaction method to create/attach EntityManager to the thread and wrap you code within a transaction.

Open new transaction when already inside ejb

Consider the following situation:
#Stateless
#Clustered
public class FacadeBean implements Facade {
#EJB
private Facade facade;
#Override
public void foo(List<Integer> ids) {
// read specific id's from the db
for (Integer id : ids) {
facade.bar(id);
}
}
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void bar(Integer id) {
// save something to the db
}
}
Method foo gets called from outside the ejb. I want each id to be executed in its own transaction, so that data get's persisted directly to the database. It is not possible to have the foreach outside of this class. I am wondering what is the best way to do this? At the moment I am injecting the interface again, to step over the ejb boundaries (so that the TransactionAttribute get's evaluated).
Your approach as to circular reference is perfectly fine. Circular reference in EJBs is allowed. This is even mandatory in order to start out a new transaction, or an #Asynchronous thread (otherwise the current thread would still block).
#Stateless
public class SomeService {
#EJB
private SomeService self; // Self-reference is perfectly fine.
// Below example starts a new transaction for each item.
public void foo(Iterable<Long> ids) {
for (Long id : ids) {
self.fooInNewTransaction(id);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void fooInNewTransaction(Long id) {
// ...
}
// Below example fires an async thread in new transaction.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void bar(Iterable<Long> ids) {
for (Long id : ids) {
self.fooAsynchronously(id);
}
}
#Asynchronous
public void fooAsynchronously(Long id) {
// ...
}
}
Only in older containers, this did not work, most notably JBoss AS 5 with the ancient EJB 3.0 API. That's why people invented workarounds like SessionContext#getBusinessObject() or even manually grabbing via JNDI.
Those are unnecessary these days. Those are workarounds not solutions.
I'd personally only do it the other way round as to transactions. The foo() method is clearly never intented to be transactional.
#Stateless
public class SomeService {
#EJB
private SomeService self;
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void foo(Iterable<Long> ids) {
for (Long id : ids) {
self.foo(id);
}
}
public void foo(Long id) {
// ...
}
}
Depending on the concrete functional requirement, you could even make the foo(Long id) #Asynchronous, hereby speeding up the task.
Do you really have to have both methods in one class? You can move bar() to an own bean and make it transactional. Then you don't have to use this kind of self-injection.
You can also try to use SessionContext#getBusinessObject() method.
#Resource
SessionContext sessionContext;
#Override
public void foo(List<Integer> ids) {
Facade facade = sessionContext.getBusinessObject(Facade.class);
// read specific id's from the db
for (Integer id : ids) {
facade.bar(id);
}
}

Java Spring #Transactional method not rolling back as expected

Below is a quick outline of what I'm trying to do. I want to push a record to two different tables in the database from one method call. If anything fails, I want everything to roll back. So if insertIntoB fails, I want anything that would be committed in insertIntoA to be rolled back.
public class Service {
MyDAO dao;
public void insertRecords(List<Record> records){
for (Record record : records){
insertIntoAAndB(record);
}
}
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
Where MyDAO dao is an interface that is mapped to the database using mybatis and is set using Spring injections.
Right now if insertIntoB fails, everything from insertIntoA still gets pushed to the database. How can I correct this behavior?
EDIT:
I modified the class to give a more accurate description of what I'm trying to achieve. If I run insertIntoAAndB directly, the roll back works if there are any issues, but if I call insertIntoAAndB from insertRecords, the roll back doesn't work if any issues arise.
I found the solution!
Apparently Spring can't intercept internal method calls to transactional methods. So I took out the method calling the transactional method, and put it into a separate class, and the rollback works just fine. Below is a rough example of the fix.
public class Foo {
public void insertRecords(List<Record> records){
Service myService = new Service();
for (Record record : records){
myService.insertIntoAAndB(record);
}
}
}
public class Service {
MyDAO dao;
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
I think the behavior you encounter is dependent on what ORM / persistence provider and database you're using. I tested your case using hibernate & mysql and all my transactions rolled back alright.
If you do use hibernate enable SQL and transaction logging to see what it's doing:
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG
// for hibernate 4.2.2
// log4j.logger.org.hibernate.engine.transaction=DEBUG
If you're on plain jdbc (using spring JdbcTemplate), you can also debug SQL & transaction on Spring level
log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
Double check your autocommit settings and database specific peciular (eg: most DDL will be comitted right away, you won't be able to roll it back although spring/hibernate did so)
Just because jdk parses aop annotation not only with the method, also parse annotation with the target class.
For example, you have method A with #transactional, and method B which calls method A but without #transactional, When you invoke the method B with reflection, Spring AOP will check the B method with the target class has any annotations.
So if your calling method in this class is not with the #transactional, it will not parse any other method in this method.
At last, show you the source code:
org.springframework.aop.framework.jdkDynamicAopProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping orfancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}

Hibernate: long CMT transaction

I have two beans: FirstBean and SecondBean.
FirstBean have method method1 with REQUIRED transaction attribute, SecondBean have method method2 with NOT_SUPPORTED transaction attribute.
method1 invokes method2.
#Stateless
class FirstBean implement IFirstBean
{
ISecondBean secondBean;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
void method1()
{
//...
secondBean.method2()
//...
}
}
#Stateless
class SecondBean implement ISecondBean
{
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
void method2()
{
//....
}
}
and it works well.
But when method2 worked for a long time (more then 4-5 min) I get exception on JBoss 4 server
Caused by: java.lang.IllegalStateException: [com.arjuna.ats.internal.jta.transaction.arjunacore.inactive] [com.arjuna.ats.internal.jta.transaction.arjunacore.inactive] The transaction is not active!
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1379)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:135)
and next on WebLogic 10.3
Caused By: org.hibernate.SessionException: Session is closed!
at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:49)
at org.hibernate.impl.SessionImpl.clear(SessionImpl.java:253)
method1 is finished and exception is thrown after
To be honest I would defer any transactional work until all the non-transactional tasks were completed. Easily done by creating a non-transactional 'wrapper' method:
class FirstBean implements IFirstBean {
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void overlordMethod() {
secondBean.method2();
method1();
}
}
This prevents the long running method for which no transaction is required from causing issues in the method that needs one. Now, five minutes for a method to complete is alot, but assuming that there is no improvements to be made in the runtime, and that you for some reason cannot reorder your method calls, then you can always increase the transaction timeout for your JTA. Read here for a variety of ways to do this on JBoss 4.
In JBoss 4.x using JTA transactions, the default timeout is 5 minutes. You are most likely exceeding the timeout for the transaction in progress when "method 1" was invoked because the elapsed time is being incremented while "method 1"'s transaction has been suspended while executing "method 2". You can confirm this by increasing the timeout to a very large value - JBoss has a TransactionTimeout attribute that you can annotate "method 1" with.
One non-obvious thing to remember is that when a transaction is suspended, it doesn't mean that the transactional timer is stopped.
I use next constraction at the end.
#Stateless
class FirstBean implement IFirstBean
{
ISecondBean secondBean;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
void firstPartOfmethod1()
{
//...
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
void secondPartOfmethod1()
{
//...
}
}
#Stateless
class SecondBean implement ISecondBean
{
#TransactionAttribute(TransactionAttributeType.NEVER)
void method2()
{
//....
}
}
#Stateless
class MainBean implement ISecondBean
{
#TransactionAttribute(TransactionAttributeType.NEVER)
void mainMethod()
{
firstBean.firstPartOfMethod1();
secondBean.method2();
firstBean.secondPartOfMethod1();
//....
}
}

Categories