LazyInitializationException in a Spring Transaction - java

In a Spring Boot project I am working at I added the Hibernate ORM plugin to enable lazy loading for #Lob fields in my entity, this didn't work out of the box for DB2. The lazy loading works, when I retrieve the object the lob field isn't loaded yet. But now a new problem occurs, when I do want to get the blob field I get a LazyInitializationException. Now I did some debugging and Google searches. I tried to add #Transactional to the method and to the classes, I also tried multiple propagation options, but nothing works.
I also tried to load the object directly using the EntityManager, but that also gives the same exception:
Object object = entityManager.find(Object.class, id);
object.getLobField();
The full exception that is thrown is:
org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [package.Object.lobField] - no session and settings disallow loading outside the Session

You need to activate in your application.yml
spring:
jpa:
properties:
hibernate:
enable_lazy_load_no_trans: true
This will allow lazy loading to work outside the session that created the object that has properties that are lazy loaded.
Reference: https://www.baeldung.com/hibernate-lazy-loading-workaround, Solve Hibernate Lazy-Init issue with hibernate.enable_lazy_load_no_trans and https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/

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.

Java EntityManager transaction gets broken, how to locate the SQL that causes it?

I have an EntityManager and a JobDAO class which has many methods which use the EntityManager to select/update/delete/insert.
For some methods, I now am getting a javax.persistence.TransactionRequiredException: Executing an update/delete query ..
about not having a transaction.
I have a #Transactional annotation on the method calling the other methods.
I have now fixed it somewhat by using my own database connection for some of these commands, but I'd like to find the SQL that causes the problem.
One idea I have is to add a transaction checker method call to the end of each of the 20 methods that are suspicious. But I'd like to know whether you have a better idea to check on the EntityManager, for example by logging all SQL so I can find the last SQL where it stopped working.
See this post on SO for configuring the ORM provider (e.g. Hibernate, etc.) to log sql:
How to view the SQL queries issued by JPA?
In addition, you can programatically ask the EntityManager if it is currently in a transaction, see isJoinedToTransaction().
Also..., make sure that the class with the methods that have the #Transactional annotation is a Spring Bean, otherwise the annotation does nothing.
In addition you have to tell Spring to enable transaction management. This post will help you understand how to do that: https://www.baeldung.com/transaction-configuration-with-jpa-and-spring

JPA2 data validation verbose logging

I'm using EclipseLink 2.5 with JPA 2.1 in standalone java application.
Some field are marked with #Basic(optional=false), but even with null value I didn't get any error before commit. The constraint is set on database so I got a JDBC exception.
Adding Hibernate Validator to project and setting validation mode to callback didn't help.
Only with #NotNull annotation on field I got exception: javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details. which is not very specific and does not inform where the problem is.
I would like to know if there is any way to make this message look more robust: like field name not set or something like this and force eclipselink to check optional=false.
EDIT:
I know the difference between JPA and Bean Validation. I am trying to perform validation with JPA only, using optional=false. As far as I know (#Basic(optional = false) vs #Column(nullable = false) in JPA) #Basic(optional=false) should be checked at runtime and #Column(nullable=false) should be used to make column nonnullable in the database.
I'm looking for a method to display violations with out catching ConstraintViolations everywhere.
A couple of things here. First of you have to distinguish between JPA and Bean Validation. Two different specifications and things. #Basic is a JPA annotation whereas #NotNull is a Bean Validation annotation. Using #Basic(optional=false) in conjunction with schema creation you will indeed get a database constraint which in turn lead to a JDBC exception during persist.
By introducing Bean Validation you activate the JPA integration of Bean Validation. In this case prior to writing to the database the data will be validated via Bean Validation. In this case as part of pre-persist. As per specification a ConstraintViolationException is thrown in this case. You can call ConstraintViolationException.getConstraintViolations to get a set of the failing constraints. It is up to you to catch this exception and do the unwrapping yourself.
I'm looking for a method to display violations with out catching ConstraintViolations everywhere.
You could add a catch(ConstraintViolationException cve) {...} block at the outermost level of your application code (e.g. in form of some request handler/interceptor in case this is a web application) and use that to handle constraint violations in a generic way. The ConstraintViolation object provides lots of information such as the name of the concerned property etc. I'm not sure though why EclipseLink doesn't consider #Basic(optional=false), though.

Why can I insert/update data with hibernate even when there's no transaction involved?

As we all know that in Hibernate if no transaction commit, the changes won't affect in database. But I found something weird. And the code as follows:
ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring.xml");
SessionFactory sessionFactory = (SessionFactory) ctx.getBean("sessionFactory");
Session session = sessionFactory.openSession();
Model model = new Model();
...
session.save(model);
session.flush();
session.close();
And the model was saved to database even there's no transaction, anyone can explain this?
Any comments would be appreciated! Thanks!
PS: I am using mysql.
The session.flush command saved the transaction. If it's wrong, you should use transaction.
usually hibernate needs the line session.beginTransaction(); to work. You didn't write that and your application worked, I guess your application runs in an Application server, which provides transaction management. e.g. jboss, weblogic...
However it doesn't mean that there is no transaction. Did you set auto-commit true?
btw, session.flush() and txn.commit() are different.
Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.
After session.flush(), you still can call txn.rollback() to rollback all changes.
edit
oh I saw you used spring. did you configured txnmanager in spring?
Hibernate doesn't need transactions, the most common problems in database-based applications are just easier to solve with transactions which is why usually everyone uses transactions with Hibernate. But that's mere coincidence/convention/laziness.
All Hibernate needs is a java.sql.Connection and if your container provides one even though there is no current transaction manager configured, Hibernate is fine with that.
In fact, Hibernate has no idea that there might be a transaction manager. So session.flush() will use the ApplicationContext to get a connection, generate the SQL and use JDBC to send the generated SQL code to the database.
From Hibernate's point of view, that's all that happens.
There can be several reasons why the data is committed to the database:
You forgot to turn of auto commit on the connection.
Your web container / spring config automatically wires a transaction manager that synchronizes with HTTP requests.
Your code is called form another method which is annotated with #Transactional; in this case, you inherit the existing transaction.

OpenJPA says: You have supplied colums for FooBar but this mapping cannot have columns in this context

I have some Java entity classes that are working well in my production code. I am writing an automated test for this application where I am reusing the very same classes.
When the test application is creating its entity manager this way:
Properties props = new Properties();
props.setProperty("provider", "org.apache.openjpa.persistence.PersistenceProviderImpl");
props.setProperty(...);
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("foobar-upgrade-restful-test", props);
EntityManager entityManager = entityManagerFactory.createEntityManager();
The applications throws an error message telling that:
Caused by: <openjpa-2.2.0-r422266:1244990 fatal user error> org.apache.openjpa.persistence.ArgumentException: You have supplied columns for "com.foo.bar.server.commontypes.job.CompositeJob", but this mapping cannot have columns in this context.
at org.apache.openjpa.jdbc.meta.MappingInfo.assertNoSchemaComponents(MappingInfo.java:382)
at org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy.map(FlatClassStrategy.java:51)
at org.apache.openjpa.jdbc.meta.ClassMapping.setStrategy(ClassMapping.java:392)
at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:56)
at org.apache.openjpa.jdbc.meta.MappingRepository.prepareMapping(MappingRepository.java:411)
at org.apache.openjpa.meta.MetaDataRepository.preMapping(MetaDataRepository.java:762)
at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:651)
... 72 more
I do not really understand the error message as the same entity bean is working in the application under Glassfish and the persistence.xml is also almost identical. (In the test I use RESOURCE_LOCAL tx instead of JTA but I doubt that it has relation with this error.
Do you have any idea what's wrong?
The error indicates that there is a problem with the mapping information that you provided for com.foo.bar.server.commontypes.job.CompositeJob.
I'm guessing the reason it works for JTA, but not resource local most likely has to do with some oddity in the way that metadata is loaded / processed in the different environments. It would be helpful if you post the relevant mapping data from your Entity.

Categories

Resources