JPA: question about merging an entity before removing it - java

I know I have to merge the entity before removing it, but I never thought I have to do it inside EJB. First I have these:
e = (Event) scholarBean.merge(e);
scholarBean.remove(e);
in my managed bean. It give me this error
java.lang.IllegalArgumentException: Entity must be managed to call remove: com.scholar.entity.Event#998, try merging the detached and try the remove again.
So then I bring those two lines inside my session bean, and it works. Any idea why?
Managed Bean
myEJB.deleteEvent(e);
and
myEJB.java
public void deleteEvent(Event e){
e = (Event) merge(e);
em.remove(e);
}

I know I have to merge the entity before removing it
Not exactly. The object passed to remove has to be an entity and must not be detached. That's different.
but I never thought I have to do it inside EJB. First I have these (...)
Let's see what you're doing:
1: e = (Event) scholarBean.merge(e);
2: scholarBean.remove(e);
So in 1:, you call an EJB (very likely with a transaction-scoped Persistence Context) that merges the entity. But then the method ends, the transaction commits, the Persistence Context gets closed, making the returned entity detached again.
And in 2:, you pass the (still) detached entity to an EJB and tries to remove it, which is not allowed. And KaBOOM!
So then I bring those two lines inside my session bean, and it works. Any idea why?
It works because you're now working within the scope of the persistence context associated to the JTA transaction and you're thus really passing a managed entity to remove.

...and you can even combine those:
Like so:
public void deleteManCheck(ManCheck manCheck) {
em.remove(em.merge(manCheck));
}

I had the same transaction-issues when this was used in a servlet. When using the EJB-service-bean from a MDB it worked fine, since the transaction was started before the EJB was called, but when the EJB-call came from a servlet, there was no running transaction.
I solved this in my webapp by creating a filter that starts and commis a UserTransaction. Then every call to the EJB-methods joins my UserTransaction instead of starting it's own transaction.

Related

How do I use multiple #Transactional annotated methods?

I have a Spring-boot project where I have a service bean with 2 #Transactional annotated methods.
These methods do read-only JPA (hibernated) actions to fetch data from an HSQL file database, using both JPA repositories and lazy loaded getters in entities.
I also have a cli bean that handles commands (Using PicoCLI). From one of these commands I try to call both #Transactional annotated methods, but I get the following error during execution of the second method:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
at <mypackage>.SomeImpl.getThings(SomeImpl.java:<linenr>)
...
If I mark the method that calls both #Transactional annotated methods with #Transactional itself, the code seems to work (due to there now only being 1 top level transaction I presume?).
I just want to find out why I cannot start multiple transactions in a single session or why the second transaction doesn't start a new session if there are none.
So my questions are:
Does this have to do with how hibernate starts a session, how transactions close sessions or anything related to the HSQL database?
Is adding an encompassing transaction the right way to fix the issue
or is this just fighting the symptom?
What would be the best way to be able to use multiple #Transactional annotated methods from one method?
EDIT: I want to make clear that I don't expose the entities outside of the transactional methods, so on the surface it looks to me like the 2 transactional methods should be working independently from one another.
EDIT2: for more clarification: the transactional methods need to be available in an api and the user of the api should be able to call multiple of these transactional methods, without needing to use transactional annotations and without getting the LazyInitializationException
Api:
public interface SomeApi {
List<String> getSomeList();
List<Something> getThings(String somethingGroupName);
}
Implementation:
public class SomeImpl implements SomeApi {
#Transactional
public List<String> getSomeList() {
return ...; //Do jpa stuff to get the list
}
#Transactional
public List<Something> getThings(String somethingGroupName) {
return ...; //Do other jpa stuff to get the result from the group name
}
}
Usage by 3rd party (who might not know what transactionality is):
public someMethod(String somethingGroupName) {
...
SomeApi someApi = ...; // Get an implementation of the api in some way
List<String> someList = someApi.someList();
if (someList.contains(somethingGroupName) {
System.out.println(someApi.getThings(somethingGroupName));
}
...
}
It seems that you are accessing some not initialized data from your entities after the transactions have ended. In that cases, the persistence provider may throw the lazyinitialization exception.
If you need to retrieve some information not eagerly loaded with the entities, you may use one of two strategies:
annotate the calling method also with #Transactional annotation, as you did: it does not start a new transaction for each call, but makes the opened transaction active until your calling method ends, avoiding the exception; or
make the called methods load eagerly the required fields USING the JOIN FETCH JPQL idiom.
Transaction boundaries requires some analysis of your scenario. Please, read this answer and search for better books or tutorials to master it. Probably only you will be able to define aptly your requirements.
I found that hibernate out of the box doesn't reopen a session and therefore doesn't enable lazy loading after the first transaction has ended, whether or not subsequent jpa statements are in a transaction or not. There is however a property in hibernate to enable this feature:
spring:
jpa:
properties:
hibernate.enable_lazy_load_no_trans: true
This will make sure that if there is no session, then a temp session will be created. I believe that it will also prevent a session from ending after a transaction, but I don't know this for sure.
Partial credit goes to the following answers from other StackOverflow questions:
http://stackoverflow.com/a/32046337/2877358
https://stackoverflow.com/a/11913404/2877358
WARNING: In hibernate 4.1.8 there is a bug that could lead to loss of data! Make sure that you are using 4.2.12, 4.3.5 or newer versions of hibernate. See: https://hibernate.atlassian.net/browse/HHH-7971.

Transaction not committed at end of method

I have two methods annotated with transactional (they are on the same level - have the same parent transaction, as sketched below):
#javax.transaction.Transactional
persist() {
persistEntities1()
persistEntities2()
}
#javax.transaction.Transactional(value = Transactional.TxType.REQUIRES_NEW)
persistEntities1() {}
#javax.transaction.Transactional(value = Transactional.TxType.REQUIRES_NEW)
persistEntities2() {}
In persistEntities1 everything is OK, there is a merge call on EntityManager instance.
In persistEntities2 there is an uncaught exception.
Problem: entities that should get persisted in persistEntities1 do not get persisted.
Why is this happening? I thought that REQUIRES_NEW ensures that transaction gets committed at the end of method.
I am using Wildfly 8.2.
The solution was to move both methods persistEntities1 and persistEntities2to separate bean. Now the behavior is as expected.
It seems that this specific jpa implementation ignores child transaction annotations if child methods live in same bean as parent method.
The reason that would happen is because a RuntimeException was thrown within the execution of persistEntities1().
See why does transaction roll back on RuntimeException but not SQLException.
It doesn't seem to matter whether the exception has been caught and handled or not, the transaction context still gets set to "rollback only".

JPA transaction spanned over several postbacks

I need to create a simple JEE application which could have two modes. First, it automatically stores each modification to the database, and second, all the changes are stored on demand. Is it possible to start one JPA transaction and span it over many postbacks on a given stateful bean and commit changes (or rollback) when a user clicks some button?
I tried to set the following parameters on my bean:
#Stateful
#TransactionManagement(TransactionManagementType.BEAN)
and also for EntityManager:
#PersistenceContext(type = PersistenceContextType.EXTENDED)
but I get the following exception when I try to commit:
java.lang.IllegalStateException: Transaction is not active in the current thread.
If it is not possible like that, what is the easiest way to create the above functionality?
It is because you forgot to open a transaction. You made everything correct, but additional you have to inject in your #Stateful bean a UserTransaction (given your PersistenceContext is JTA and not RESOURCE_LOCAL) and then to start a transaction before comitting the changes.

EJB3 Container Managed Persistence - When does an entity become unmanaged

With container-managed JPA persistence, container-managed JTA transactions, and an entity manager injected into stateless local session beans, what does invoking a transaction-not-supported method do to the managed state of an entity? I have read this: https://community.jboss.org/thread/183007 and similar threads, but there seems to be some ambiguity around whether a PC gets propagated to a NOT_SUPPORTED method if they both use injected EMs from the same factory?
Consider this snippet from a a bean that is going to retrieve an item and then invoke a method from another bean:
#PersistenceContext
private EntityManager em;
#Inject
private LeanBean leanBean;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void startHere() {
MyItem item = em.find(MyItem.class, key);
leanBean.txMethod(item);
leanBean.nonTxMethod(item);
}
Now here is LeanBean.java. Note that its two methods have different transaction propagation
#Stateless
#LocalBean
public Class LeanBean {
#PersistenceContext
private EntityManager em;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void txMethod(MyItem item) {
doSomething(item); // item is managed; persistence context propagated with
} // transaction context; em is the same as my caller's em
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void nonTxMethod(MyItem item) {
doSomething(item); // caller's transaction context has been suspsended.
} // did em propagate? is item managed?
}
What I can't figure out from the spec(s):
Inside nonTxMethod, is item in a detached state? What is supposed to happen if I reference em?
What if the method in the calling snippet had been annotated TransactionAttributeType.NOT_SUPPORTED? would would be the state of item in either of the methods inside LeanBean?
What I'm trying to accomplish here is to keep an entity attached inside a method that I do not want to have as part of a transaction.
Inside nonTxMethod, is item in a detached state? What is supposed to
happen if I reference em?
TransactionAttributeType.NOT_SUPPORTED is just a way to tell that your method does not need transaction and you will not be able call persist, merge or remove in EntityManager. But you can still call other methods like em.find(Someclass.class,objectId) . So i think the entity is not in a detached state .
What if the method in the calling snippet had been annotated
TransactionAttributeType.NOT_SUPPORTED? would would be the state of
item in either of the methods inside LeanBean?
This will work exactly the same as previous. Just make sure that , whenever you will put TransactionAttributeType.NOT_SUPPORTED in your calling method eg startHere(), all the subsequent method calls transaction state would be suspended , and if you need trasaction in any of the subsequent calls you have to explicitely specify TransactionAttributeType.REQUIRED above that method .
And by default when you call any session bean's method from outside of the EJB , let from the JSP , the trasaction will automatically started unless you specify explicitly that you don't need transaction.

EntityManager throws TransactionRequiredException on merge() in JBoss JSF bean

I've set up a JSF application on JBoss 5.0.1GA to present a list of Users in a table and allow deleting of individual users via a button next to each user.
When deleteUser is called, the call is passed to a UserDAOBean which gets an EntityManager injected from JBoss.
I'm using the code
public void delete(E entity)
{
em.remove(em.merge(entity));
}
to delete the user (code was c&p from a JPA tutorial). Just calling em.remove(entity) has no effect and still causes the same exception.
When this line is reached, I'm getting a TransactionRequiredException:
(skipping apparently irrelevant stacktrace-stuff)
...
20:38:06,406 ERROR [[Faces Servlet]]
Servlet.service() for servlet Faces
Servlet threw exception
javax.persistence.TransactionRequiredException:
EntityManager must be access within a
transaction at
org.jboss.jpa.deployment.ManagedEntityManagerFactory.verifyInTx(ManagedEntityManagerFactory.java:155)
at
org.jboss.jpa.tx.TransactionScopedEntityManager.merge(TransactionScopedEntityManager.java:192)
at
at.fhj.itm.utils.DAOImplTemplate.delete(DAOImplTemplate.java:54)
at
at.fhj.itm.UserBean.delete(UserBean.java:53)
at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native
Method)
...
I already tried to wrap a manually managed transaction (em.getTransaction().begin() + .commit() ) around it, but this failed because it is not allowed within JBoss container. I had no success with UserTransaction either. Searches on the web for this issue also turned up no similar case and solution.
Has anyone experienced something similar before and found a solution to this?
Found the missing link.
It was indeed a missing transaction but the solution was not to use the EntityManager to handle it but to add an injected UserTransaction.
#Resource
UserTransaction ut;
...
public void delete(E entity)
{
ut.begin();
em.remove(em.merge(entity));
ut.commit();
}
Thanks to all suggestions which somehow over 100 corners lead to this solution.
Know this is an old question, but just in case somebody stumbles on this like me.
Try
em.joinTransaction();
em.remove(bean);
em.flush();
That's what we use in all our #Stateful beans.
If you are using Seam, you can also use #Transactional(TransactionPropagationType.REQUIRED) annotation.
Are you sure that you annotated you bean with #Stateless or register it with xml?
Try add transaction's annotation to you code, this can help you:
#TransactionAttribute(REQUIRED)
public void delete(E entity)
{
em.remove(em.merge(entity));
}
But it seems strange, because this is default value if you don't set it explicitly.
just a note: we ran into this same issue today, turned out someone had marked the EJB as TransactionAttributeType.NOT_SUPPORTED AND the method as TransactionAttributeType.REQUIRED, causing the em.merge to fail for lack of transaction.

Categories

Resources