• Post Reply Bookmark Topic Watch Topic
  • New Topic

transaction manager issue  RSS feed

 
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi ,
I have a method public int method2(Entity e) which is annotated with @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW).
While calling this method from a method1 which is already in transaction , its working fine (java project which invokes these methods). But if i call it from junit then entity is not longer attached with entity manager as a new entity manager is created .So i am getting de-attached entity error. Although its a new transaction, i want to use current entity manager only as its working from the java project.
While debugging i reached AbstractPlatformTransactionManager.getTransaction method. (I am using JPATransactionManager).
Below are the logs. Any help will be highly appreciated.

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();

// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();

if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
Going from below block if its not working fine

if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}

// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}

// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
Going from below block when its working fine

else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
 
Rancher
Posts: 3456
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Can you show your test?
Particularly the code around the object that is ending up detached.
Can you show how it is created before the call to method2?

I am assuming it is the parameter that is becoming detached.
 
San Gupta
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, the parameter is getting detached.
 
Dave Tolls
Rancher
Posts: 3456
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, that answers the last sentence I posted.

How about the first bits?
What does the test look like?
 
San Gupta
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have a job(java based application  and not spring batch) which process a list of members.For each member it perform some operations through service1 and service2. Now Service1 has method m1() having @Transactional(readOnly = false, value = "txManager", propagation = Propagation.REQUIRES_NEW). This method gets the entity and calls some of the setter method to update the entity and passes to Service2.m2(entity e) which is also having @Transactional(readOnly = false, value = "txManager", propagation = Propagation.REQUIRES_NEW). This is working fine as i am not getting detached issue in method m2.

Now the same services are part of web application which is exposing rest services. While i call those rest service through junit (POST), detached issue is coming in method m2. Flow is same in both the cases as m1 is calling m2. I found that it could be because of PersistenceContextType.EXTENDED , which is solving the issue in second case but i am not sure why its working in the first case when it is not PersistenceContextType.EXTENDED. I also tried opensessioninview filter but its also not working.Below is the junit test.

SomeMemberObject memberObject=new SomeMemberObject ("name","age");
String response = LocalRestClientInvoker.invoke("http://localhost:8080/someRestService","/saveInfo",memberObject,MethodType.POST);

LoaclRestClientInvoker is using RestTemplate to call POST method.
 
Dave Tolls
Rancher
Posts: 3456
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It doesn't help tracking this with 'm1' and 'm2' etc.

Is this running on the same server as when you run it "by hand"?
Or is this using some other mechanism for the test?

Do you get the same result when you POST to the rest service your self (say using something like SOAPUI)?
 
San Gupta
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
As i mention in the first case its running as part of java application. In second case its deployed as web application in tomcat server.
 
Dave Tolls
Rancher
Posts: 3456
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
And when you hit the Tomcat server outside of the JUnit test?
Because I suspect JUnit is a red herring.
 
Bartender
Posts: 19373
87
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm afraid I'm too busy to decode all that in detail, but just in case it helps, I'd like to make some higher-level comments.

First, the overhead for logging is extremely low, so there's not much benefit to caching the current log state, and it deprives you of the freedom to turn the log level on and off on the fly. And when I say extremely low, the figure I've seen said nanoseconds. So not only don't I cache log state, I usually don't even check log level unless I'm formatting a log message string or doing other extensive work whose overhead would be better omitted if the logger is going to suppress it anyway.

That does absolutely nothing to solve the problem, but I figured it might be useful to know, as it can simplify coding.

More to the point, I'm presuming that you're attempting to use XA transactions. As I said, I don't have time to actually interpret the sample code, but just in case it hasn't been considered I'd like to point out that XA is primarily intended to allow aggregation of transactions on multiple, possibly heterogeneous targets. I wouldn't recommend using XA as a means of managing transactions spanning web requests. Which, in any case, would be counter to the spirit of ReST, in addition to the great likelihood that the server wouldn't be able to deal with it as expected. It would be akin to the more elemental ploy sometimes used in basic JDBC where people attempt to save the Connection between requests in an HttpSession object (which you should absolutely never do). A multi-request transaction is better off manually managed by building up temporary objects then staging them to their final destinations in a single commit transaction when (if) the triggering web request demands it.

Maybe none of this applies here, but I figured it couldn't hurt to know.
 
San Gupta
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, i am hitting the tomcat server from outside (java project).
 
Dave Tolls
Rancher
Posts: 3456
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Some more detail would help here.

When you say you are hitting the server from outside, how are you dong it (for this particular service that is causing the issue) and does it fail in the same way?
 
San Gupta
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have Rest Controller which is getting invoked through junit through a client application(Spring's RestTemplate). The rest controller then calling the service methods.
 
Dave Tolls
Rancher
Posts: 3456
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I did suggest taking JUnit out of the equation.

All I can say is I would need to see some code.
The REST controller, the first method and the second method.

At the moment, though you may think you are giving enough information, I am flying blind here.
 
San Gupta
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Dave for staying with me on this. This issue is resolved now but i am not able to understand the logic behind it. In my application i have two transaction managers. If @Transactional is not not having transaction manager mentioned , spring is taking the first one declared in the application context.

<tx:annotation-driven transaction-manager="transactionManager" />
<tx:annotation-driven transaction-manager="txManager" />

In the above case , spring is taking "transactionManager" if @Transactional is not having any mention.

Case : 1 (Job i.e java based project)

Transactional(readOnly = false, value = "txManager")
public Entity m1(Entity e) 

@Transactional(readOnly = false,propagation = Propagation.REQUIRES_NEW)
public Entity m2(Entity e) 

Here m1 calls m2 which is in different service (with interface).
In this case when i put debugger in TransactionSynchronizationManager, i have found that m2 is in "transactionManager". This worked fine without any issue. Entity passed from m1, was persisted correctly by m2.

Case : 2 (Webapplication)

In this case my application context was having transaction manager in different order. So m2 was also in "txManager" and it was failing with "detached entity passed to persist" issue. When i changed the order, it worked fine.

Before entering m2, TransactionSynchronizationManager.getResource(Object key) does not return any value in case 1. But in case 2, it returns. That was the difference. Any explanation will be helpful.


 
Dave Tolls
Rancher
Posts: 3456
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A bit of a guess here, but in the first case (with the different transaction managers) the object is read from one transaction and then persisted in another.  Because those transactions come from different managers, the second transaction does not realise that this was an object retrieved from Hibernate, as it isn't in that managers cache.  So it treats it as a new one and persists happily.

In the second one, that uses the same manager, the second transaction knows that the object it's been given to work with is from Hibernate originally, but not via that transaction, so throws the error.

At least I think that's what's happening.
This is something that might require reading up on how Hibernate tracks its objects.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!