I have a code where transaction control is done manually,
But the code calls methods of other EJB's of the same application where the transaction is not controlled manually, for example, there are methods using the Hibernate Session that is managed by the Container.
How to prevent my manually managed transaction from downloading the commit when there is a query method using eg a Session.createCriteria.
When this occurs, my transaction unloads the commit before my process is actually completed.
Example
private void exe() throws Exception {
#EJB Businessbusiness;
this.beginTransaction();
business.processar(); // Exemplo
this.commit();
}
#Stateless
public class Business() {
#EJB
private DAO dao;
private void processar() throws Exception {
// executando processo 1
this.save();
// executando processo 2
this.update();
// Saving and updating has not yet been committed. So far it is correct.
Teste = dao.buscarTeste(1L);
// Here, after performing the search, my transaction downloads the commit to the bank without completing the whole process.
}
}
#Stateless
public class DAO() {
public Teste buscarTeste(Long codigo) {
Criteria cri = getSession().createCriteria(Teste.class);
cri.add(Restrictions.eq("codigo", codigo));
return (Teste) cri.uniqueResult();
}
}
I am not sure I actually got your point.
But assuming you are not getting any error, when you invoke the Business.processar() method it inherits the transaction. Which remains 'pending' until the exe client commit.
So, I would investigate what your getSession() does in the middle, I am quite sure it starts a brand new transaction, which is going to retrieve uncommitted data.
By the way, is there a reason for using hibernate instead of JPA with hibernate as a concrete implementation ?
Related
I have a SpringBoot 2 project and i'm using spring data jpa with hibernate with MySQL5.7
I have problems with the following use case: i have a service method that calls another service's method. If second service's method generates a runtime exception, also the first method is marked as rollback and i cannot commit things anymore. I'd like to only rollback second method and still commit something in the first one.
I tried to use propagation.NESTED but nested transaction are not allowed with hibernate (even if jpaTransactionManager supports them and MySQL supports savepoints).
How can i solve this problem? Can i configure nested in some way?
Please remember i need second method to see changes committed by first so i can't mark the second method as propagation.REQUIRES_NEW
Here is come sample code to clarify my problem:
FirstServiceImpl.java
#Service
public class FirstServiceImpl implements FirstService
#Autowired
SecondService secondService;
#Autowired
FirstServiceRepository firstServiceRepository;
#Transactional
public void firstServiceMethod() {
//do something
...
FirstEntity firstEntity = firstServiceRepository.findByXXX();
firstEntity.setStatus(0);
firstServiceRepository.saveAndFlush(firstEntity);
...
boolean runtimeExceptionHappened = secondService.secondServiceMethod();
if (runtimeExceptionHappened) {
firstEntity.setStatus(1);
firstServiceRepository.save();
} else {
firstEntity.setStatus(2);
firstServiceRepository.save();
}
}
SecondServiceImpl.java
#Service
public class SecondServiceImpl implements SecondService
#Transactional
public boolean secondServiceMethod() {
boolean runtimeExceptionHappened = false;
try {
//do something that saves to db but that may throw a runtime exception
...
} catch (Exception ex) {
runtimeExceptionHappened = true;
}
return runtimeExceptionHappened;
}
So the problem is that when secondServiceMethod() raises a runtime exception it rollback its operations (and that's OK) and then set its return variable runtimeExceptionHappened to false, but then firstServiceMethod is marked as rollback only and then
firstEntity.setStatus(1);
firstServiceRepository.save();
isn't committed.
Since i can't use NESTED propagation how can i achieve my goal?
I would suggest you break them up into two separate transactions.
In the first transaction do all of the work presently in firstServiceMethod that you know you want to commit. (e.g. through saveAndFlush). Now as you exit this method the changes are committed, so they will be available to subsequent calls.
Then have whatever called firstServiceMethod call a new Transactional method setFirstEntityStatus() that calls secondServiceMethod and sets the status of the entity as appropriate.
Basically, instead of attempting to NEST the transactions, split them into two fully separate transactions and use the ordering to ensure the result of the 1st is available to the 2nd.
In this queue processing I wanted to put the sending itself into separate transactions to avoid rolling back the sent status. However I run into problem due to the closed session or detached entity at the lazy fetching.
This is my code:
// Before this is called queueToSend is fetched in a separate transaction
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void sendMails(List<QueuedMail> queueToSend)
{
for (QueuedMail queuedMail: queueToSend) {
sendMail(queuedMail);
}
}
public void sendMail(QueuedMail queuedMail)
{
System.out.println("Checking "+queuedMail);
crud.getEntityManager().merge(queuedMail);
Mail mail = (Mail)crud.find(queuedMail.getMail().getId(),Mail.class);
System.out.println("mail id: "+queuedMail.getMail().getId());
crud.getEntityManager().merge(mail);
Boolean unsubscribed = false;
Set<Campaign> campaigns = mail.getCampaigns();
for (Campaign campaign: campaigns) {
if (campaign.getUnsubscribedUsers().contains(queuedMail.getUser())) {
unsubscribed = true;
}
}
// ...
}
The error is:
failed to lazily initialize a collection of role: dps.simplemailing.entities.Mail.campaigns, could not initialize proxy - no Session
And comes first at the line:
for (Campaign campaign: campaigns) {
I thought it can be because the queuedMail is already detached, however I am trying to reattach both queuedMail and mail using merge, but it doesn't help.
Also maybe it's already in cache, and that is why it doesn't start a new session.
Basically I want it to do the same as before (before I added TransactionAttribute), but as a separate transaction within the loop. I don't think I should do any vendor-specific solution as it seems to be a trivial task.
Update:
I have done some research, and found that I have to use the result of the merge operation in order to reattach the detached entity. This changed the vendor-specific error (as using lazy loading properties of a detached entity undefined) to a vendor-independent error, and I think it shows the real problem.
The modified code is:
public void sendMail(QueuedMail queuedMail)
{
System.out.println("Checking "+queuedMail);
queuedMail = crud.getEntityManager().merge(queuedMail);
Mail mail = (Mail)crud.getEntityManager().merge(queuedMail.getMail());
Boolean unsubscribed = false;
Set<Campaign> campaigns = mail.getCampaigns();
for (Campaign campaign: campaigns) {
if (campaign.getUnsubscribedUsers().contains(queuedMail.getUser())) {
unsubscribed = true;
}
}
I have also changed the entity to define the cascade type:
#ManyToMany(mappedBy = "mails",cascade = CascadeType.MERGE)
private Set<Campaign> campaigns;
Now the error is this:
Transaction is required to perform this operation (either use a transaction or extended persistence context)
What I don't really understand about this error is that the transaction should have been started as the class default is TransactionAttributeType.REQUIRED. The intention for setting the other methods to TransactionAttributeType.NOT_SUPPORTED was to start the transaction in this method. I am beginner in transactions and entity manager, so I will just continue research to understand it better, but maybe I get answer here sooner, or if I find the solution I will post it.
I found the glitch, it's pretty tricky, enough to say, that no further research shown the real problem, but I got the answer during taking time out and walking.
As I told, the main idea for this method was that the sendMails has no transaction, while the sendMail (should) have a transaction, and this would have to provide one transaction for every item to be processed.
The error in the update shown to me that there is no transaction in the sendMail, and I were really confused about it.
The real reason is found in the fact that the bean is executed using a proxy. Normally this is done from outside of the bean itself. If the bean calls it's own method directly, the container doesn't have the chance to proxy the request, nor it can start a transaction.
The corrected code has an injection point in the bean injecting it's own instance, but using the container:
#Stateless
public class MailQueue {
#Inject MailQueue mailQueue;
// ...
}
And using this injection point one method can call the other method via the proxy:
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void sendMails(List<QueuedMail> queueToSend)
{
for (QueuedMail queuedMail: queueToSend) {
mailQueue.sendMail(queuedMail);
}
}
public void sendMail(QueuedMail queuedMail)
{
// ...
}
With this solution the container has the chance to start the new transaction before the call to sendMail.
I'm implementing simultaneous write into database and Oracle Coherence 3.7.1 and want to make whole operation transactional.
I would like to have a critique on my approach.
Currently, I've created façade class like this:
public class Facade {
#EJB
private JdbcDao jdbcDao;
#EJB
private CoherenceDao coherenceDao;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void updateMethod(List<DomainObject> list) {
jdbcDao.update(list);
coherenceDao.update(list);
}
}
I guess JDBC DAO would not need to do anything specific about transactions, it something happens Hibernate would throw some kind of RuntimeException.
public class JdbcDao {
private void update(List<DomainObject> list) {
// I presume there is nothing specific I have to do about transactions.
// if I don't catch any exceptions it would work just fine
}
}
Here is interesting part. How do I make Coherence support transactions?
I guess I should open coherence transaction inside update() method and on any exceptions inside it I should throw RuntimeException myself?
I currently thinking of something like this:
public class CoherenceDao {
private void update(List<DomainObject> list) {
// how should I make it transactional?
// I guess it should somehow throw RuntimeException?
TransactionMap mapTx = CacheFactory.getLocalTransaction(cache);
mapTx.setTransactionIsolation(TransactionMap.TRANSACTION_REPEATABLE_GET);
mapTx.setConcurrency(TransactionMap.CONCUR_PESSIMISTIC);
// gather the cache(s) into a Collection
Collection txnCollection = Collections.singleton(mapTx);
try {
mapTx.begin();
// put into mapTx here
CacheFactory.commitTransactionCollection(txnCollection, 1);
} catch (Throwable t) {
CacheFactory.rollbackTransactionCollection(txnCollection);
throw new RuntimeException();
}
}
}
Would this approach work as expected?
I know that you asked this question a year ago and my answer now might not be as much as value for you after a year but I still give it a try.
What you are trying to do works as long as there is no RuneTimeException after the method call of coherenceDao.update(list); You might be assuming that you don't have any line of codes after that line but that's not the whole story.
As an example: You might have some deferrable constraints in your Database. Those constraints will be applied when the container is trying to commit the transaction which is on method exit of updateMethod(List<DomainObject> list) and after your method call to coherenceDao.update(list). Another cases would be like a connection timeout to database after that coherenceDao.update(list) is executed but still before the transaction commit.
In both cases your update method of CoherenceDAO class is executed safe and sound and your coherence transaction is not rollbacked anymore which will put your cache in an inconsistent state because you will get a RuneTimeException because of those DB or Hibernate Exceptions and that will cause your container managed transaction to be rollbacked!
I have a couple of questions about Transactions in Spring if you may.
Let's suppose i have this DAO class :
public class MyDAO {
/**
* verifies if a certain record in DB contains 'True' in a certain Column named publishFlag
*/
#Transactional
public bloolean isBeingPublished(Long recordID){
...
}
/**
* sets the record's publishFlag column to true indicating that it's being published
*/
#Transactional
public boolean setBeingPublished(Long recordID){
...
}
}
And the following class using it :
public class MyClass {
#Autowired
MyDAO dao;
public void publishRecords(List<Long> ids){
for(Long id : ids){
if(!dao.isBeingPublished(id)){
dao.setBeingPublished(id);
//do something to publish the record
}
}
}
}
My questions are :
First of all, will the !dao.isBeingPublished(id) and dao.setBeingPublished(id) be executed in the same transaction or in separate ones?
Second question's about concurrency, Multiple MyClass instances can be created and concurrent calls to the publishRecord method can occur, so two concurrent calls to !dao.isBeingPublished(id) might both give the same result and thus making the record published twice!
I would consider making the publishRecords synchronized but the application may be deployed on multiple servers which renders the synchronized declaration useless, hence my question about transactions since the database is the only shared resource between the apps deployed on those servers.
What would be the solution to my problem exactly? I read about spring's transaction propagation and found out that REQUIRES_NEW would create a new transaction even if one is currently being executed, but still, I just can't see how that's going to be a solution to my problem.
Thank you in advance for your help.
Few things need consider, DAO is focus on operation on single entity, and service is focus on operation of one or more entities, so the transaction should put on service layer, so you can reuse DAO's operation without any transaction, but let service to decide when start and end transaction
It is not in single transaction, but two separate transaction.
That is the problem concurrency issue with your current design, see the following suggestion.
Interface
public interface MyClass {
public void publishRecords(List<Long> ids);
}
Implementation
#Service
#Transactional(readOnly = false)
class DefaultMyClass implements MyClass {
#Autowired
MyDAO dao;
// single transaction
#Override
public void publishRecords(List<Long> ids) {
for(Long id : ids){
if(!dao.isBeingPublished(id)){
dao.setBeingPublished(id);
//do something to publish the record
}
}
}
}
DAO
class MyDAO {
public bloolean isBeingPublished(Long recordID){
// bigbang
}
public boolean setBeingPublished(Long recordID){
// bigbang
}
}
Using the above design, both problems are being resolved.
First of all, will the !dao.isBeingPublished(id) and
dao.setBeingPublished(id) be executed in the same transaction or in
seperate ones?
Unless there's a method annotated with #Transactional further up the stack, they will be occurring in separate transactions, so yes you will have a potential for a race condition.
If I were you, I would toss the isBeingPublished and setBeingPublished in favor of a single #Transactional publishIfPossible method that returns a boolean value of whether it was able to acquire the database row lock and do the publish operation.
First, I have a stateless bean which do a simple retreive, looking like this.
#Stateless
#LocalBean
public A {
#PersistenceContext
private EntityManager em;
public MyEntity retrieveMethod(){
em.createQuery(...).getSingleResult();
}
}
I have a statefull bean used to manage long conversation with a remote client, it look like this :
#Statefull
#LocalBean
#TransactionAttribute(NOT_SUPPORTED)
public class B implements BRemote {
#PersistenceContext(type = EXTENDED)
private EntityManager em;
#EJB
A a;
public void start(){
OtherEntity oe = new OtherEntity();
oe.setRelationMyEntitie(this.a.retrieveMethod());
em.persist(oe);
}
#TransactionAttribute(REQUIRED)
public void end(){
em.flush();
}
}
The problem come on executing em.persist(oe). oe has a reference to an instance of MyEntity which was loaded by another EntityManager. So em don't know it complaining about persisting detached Entity.
I would like to know what is there is a way to avoid this problem. If there is no direct solution, what is the best pattern to adopt ?
EDIT: I don't want to use a transaction on start() because in real application, the statefull bean is used to realize a complex entity model which need to be persist at once. I try to setup the pattern called session-per-conversation in described here http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/transactions.html#transactions-basics-apptx. So if I understand you right, the solution is to "use a transaction in start() method of bean B", but if I do this, at the end of the method, the content is flushed to database and it's not what I want.
Other solution I can see is to get MyEntity in the B's EntityManager, so do a merge, or a em.find() or to delegate retrieveMethod to some DAO style class, using the em in parameter, and in bean A, do a simple delegation to the DAO, in bean B, call directly the DAO.
Any idea on what is the best approach ?
This is a rather interesting question. The setup seems absolutely reasonable, but the difficulty is in two "automatic" behaviors of a transaction:
Share resources to other beans in the invocation chain
Automatically persist operations that were queued when the persistence context was not associated with a transaction
It's made even more difficult because of the fact that the resource you want to share is exactly the entity manager. Otherwise you could have used the third variant in EJB called the "application managed" entity manager. This one can be programmatically associated with a transaction (using em.join()), independently of the bussiness method being in transaction or not.
You would either need to share without a transaction, or prevent the em to auto-flush upon transaction association. Both are to the best of my knowledge missing features in EJB. Perhaps the application managed extended em doesn't do the auto-flush, but I wouldn't hold my breath.
But what about a more manual approach? Don't call em.persist() yet, but use a seperate list to store references to any entities that need to be persisted during the conversation.
Then in the close method itterate that list and do call em.persist().
P.s.
One other option: what if you use em.merge() instead of em.persist()? Merge is a multifunctional method that does both updates and inserts and doesn't care about entities being attached or not. It would be nicer if entities never became detached between A and B, but this might be a practical solution.
The problem seems to be that a normal (non-extended) persistence context is scoped to a JTA transaction.
Since you have declared bean B as transactions NOT_SUPPORTED, while A has REQUIRED, invoking method start will give you a different persistence context as the one in `retrieveMethod'. (actually, there will be not persistence context at all in the first case).
Normally in EJB resources, including entity managers are automatically shared within a single transaction, so even if looks like different injections in different beans, you'll still get the same resource.
Even without bean A, your code would not have worked since persisting requires a transaction to be present. The explicit flush wouldn't be of much use either, since this isn't needed in this context (happens automatically when the transaction commits).
If you want to keep the managed entities attached during the conversation, bean B can use the extended persistence context #PersistenceContext(type = EXTENDED). If it also uses transactions, then bean A will share the same context, even though it itself does not have an extended context (what matters is that it will be called from within B's transactional context).
Here is the solution I used :
import java.lang.reflect.Field;
import javax.annotation.Resource;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
public class SessionPerConversationInterceptor {
private final static ThreadLocal<EntityManager> s_thEntityManager = new ThreadLocal<>();
#AroundInvoke
public Object manageEntityManager(InvocationContext ctx) throws java.lang.Exception {
EntityManager em = s_thEntityManager.get();
if (em == null) {
MasterPersistenceContext traversableEntityManager = ctx.getTarget().getClass().getAnnotation(MasterPersistenceContext.class);
if (traversableEntityManager != null) {
for (Field field : ctx.getTarget().getClass().getDeclaredFields()) {
if (field.getAnnotation(PersistenceContext.class) != null) {
field.setAccessible(true);
em = (EntityManager) field.get(ctx.getTarget());
s_thEntityManager.set(em);
try {
Object oRet = ctx.proceed();
return oRet;
} finally {
s_thEntityManager.set(null);
}
}
}
}
} else if (ctx.getTarget().getClass().getAnnotation(MasterPersistenceContext.class) == null) {
for (Field field : ctx.getTarget().getClass().getDeclaredFields()) {
if (field.getAnnotation(PersistenceContext.class) != null) {
field.setAccessible(true);
EntityManager oldEntityManager = (EntityManager) field.get(ctx.getTarget());
field.set(ctx.getTarget(), em);
try {
Object oRet = ctx.proceed();
return oRet;
} finally {
field.set(ctx.getTarget(), oldEntityManager);
}
}
}
}
return ctx.proceed();
}
}
I hope it can help (and I hope this a not so ugly/dumb solution).