I get the an exception when trying to get data, lazily(Exception at the very end)
//application gets data by the following DAO.
public T findById(PK id) {
T result = getHibernateTemplate().get(this.type, id);
getHibernateTemplate().flush();
return result;
}
//Junit test calls a serviceX.getById
#Transactional
public SomeObject getById(int x){
return (SomeObject) aboveDao.findById(x);
}
//Withing the JUnit
SomeObject someObj = serviceX.getById(3);
someObj.getAnotherObject().y.equals("3"); //**Exception** at this line.
//SomeObject class has the following property.
#OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
private AnotherObject anotherObject;
I get the following exception when tryin to access anotherObject in the junit
Methods already tried + extra configuration
We use spring annotation TransactionManager.
<tx:annotation-driven /> specified in the config file.
Also, I tried to add #Transaction(propagation = Propagation.REQUIRED) on top of the JUnit, this did not solve the issue. If I run the application, it works without any issues.
How to solve this type of issue for JUnit?
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role xxxxx , no session or session was closed
Here's what happens
SomeObject someObj = serviceX.getById(3); // #Transactional boundary, no more session
someObj.getAnotherObject().y.equals("3"); // session doesn't exist, can't fetch LAZY loaded object
Because your AnotherObject is LAZY fetched, it doesn't actually get loaded in the getById() method. The Session it was associated with is lost when the #Transactional ends, ie. when execution returns from getById(). Because there is no longer a Session, you get the exception.
You can change your FetchType to EAGER. If you're going to that field of your object, you need to initialize it in your Transaction boundaries.
If you only some times need the anotherObject, a possible solution is to create a #Transactional method that calls the getById and eagerly loads the object.
#Transactional
public SomeObject eagerGetById(int x){
SomeObject obj = getById(x);
obj.getAnotherObject(); // will force loading the object
return obj;
}
Calls this method whenever you need to eagerly load the object.
This is could be useful to you LazyInitilializationException
Related
Recently I faced an issue regarding updating entities from a #Scheduled method where it would fail with the exception org.hibernate.TransientPropertyValueException: object references an unsaved transient instance even though it would work seamless when invoked from a #RestController method. This is the relevant example:
The offending method (other parts of the class omitted for brevity):
#Service
public class AnonymizationService
{
private final ItemRepository itemRepository;
public Result anonymizeItemsOlderThan(int days) {
List<Item> data = itemRepository.findAllByCreatedDateBeforeAndAnonymizationDateIsNull(Instant.now().minus(days, ChronoUnit.DAYS));
List<String> itemsAnonymized = new ArrayList<>(data.size());
data.forEach(item -> itemsAnonymized.add(itemRepository.save(item.anonymize()).getRequestId()));
return Result.builder().anonymizedItems(itemsAnonymized).build();
}
}
The #RestController caller (again most stuff omitted):
#RestController
public class DataAnonymizationAPI
{
private final AnonymizationService anonymizationService;
#PutMapping(path = "${datadeletion.path:/anonymize}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Result> anonymizeAll(#Valid DataDeletionRules dataDeletionRules) {
return ResponseEntity.ok(anonymizationService.anonymizeItemsOlderThan(dataDeletionRules.getMinimunAge()));
}
}
Again, this works just fine when used like above. The problem happens when AnonymizationService#anonymizeItemsOlderThan() is instead invoked from the following #Scheduled method:
#Component
public class DataDeletionTasks
{
private final AnonymizationService anonymizationService;
private final DataAnonymizationProperties properties;
#Scheduled(cron = "${datadeletion.anonymization.schedule}")
public void anonymizeItemsPeriodically() {
anonymizationService.anonymizeItemsOlderThan(properties.getAnonymization().getMinAge());
}
}
In this case it fails with the exception mentioned above (org.hibernate.TransientPropertyValueException).
Upon changing the log level to DEBUG and carefully analyzing it, nothing unexpected happens:
When the method is invoked from the #RestController an existing EntityManager is used and a transaction created:
o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(1702787226<open>)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
When the method is invoked from the #Scheduled method a new EntityManager is created:
o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(644498403<open>)] for JPA transaction
Naturally, my instinct was to add #Transactional to the Anonymization#anonymizeItemsOlderThan() method which immediately solved it, but why?
Why does it work in one case and not in the other? Why does the saveAndFlush() must be performed using the same EntityManager used to retrieve the entity in the first place?
This situation made me think my knowledge is flawed on a very basic level, but somehow couldn't find a clear explanation to it. In any case feel free to point me towards relevant literature that might help me.
I try to persist an object in postgres database.
After the persist call, I return my object, I modify my object, research the object (with function find of my entity manager) and data is still the same (before my object's modification).
I believe after persist, the object is link to database, and a modification with a setter is automatically persist in database
I use JPA2.1 with hibernate, wildfly 13.
I already check theses answer :
What is the difference between persist() and merge() in JPA and Hibernate?
JPA EntityManager: Why use persist() over merge()?
I have a repository class with an entity manager :
#Transactional(TxType.SUPPORTS)
class testRepository{
#PersistenceContext(unitName = "xxxPU")
private EntityManager em;
[...]
#Transactional(TxType.REQUIRED)
public Test create(#NotNull Test test) {
em.persist(test);
return test;
}
public Test find(#NotNull Long id) {
return this.em.find(Test.class, id);
}
[...]
}
And my test class with the folowing test :
Test test = new Test(null, null, "12", "RUE DE LA PAIX", "75000", "PARIS", null);
test= testRepository.create(test);
I believe at this point object test is link to database, but if i call this code :
test.setAValue("93000");
And I search this object :
Test testFind = testRepository.find(test.getId());
And compare the two object, I have a test error, because test.aValue = 75000 and testFind.aValue=93000.
After setter, if I made a call to a merge function, testFind.aValue has the correct value.
I don't understand why my object is not link to my database after the persist call.
I have the following code:
public void method1(String id){
Object object = repository.findOne(id);
object.setState("running");
repository.save(object);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
So, I change the state to "running" before calling a method that takes hours to execute. My object is a JPA Entity(with lazily loded collections) and method2() tries to load all the linked entities.
Now, in method2, I am getting
could not initialize proxy - no Session
error because it is outside of transaction (expected behavior). To prevent this, there are two solutions:
Annotate method1 with #Transactional. This would solve it, but then, the state won't be reflected to other transactions until the method execution finishes.
Change the fetch mode in Entity config and make it Eager. This would also solve it but I don't want eager fetching every time.
Is there any other way by which I can make it work?
How about this:
Option 1
1) Create a service method for status changing like following:
#Transactional( propagation = Propagation.REQUIRES_NEW)
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Change the original method as follows:
#Autowired
Service service;
#Transactional
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
Object object = repository.findOne(id);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
Thanks to this set-up, everything can be run under one #Transactional method, but when the state is to be changed to 'running' then :
The current transaction would be suspended
New one would be created
State would be changed and transaction commited
Parent transaction would continue and you can process with your big operation not having a problem that other users will wont see the status change for 2 hours..
Option 2
1) Create a service method for status changing like following:
#Transactional
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Create transactional method just for long processing
#Transactional
public void performLongProcessing(String id){
Object object = repository.findOne(id);
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
repository.save(objects;
}
3) Mark the main method to run without transaction:
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
try{
service.performLongProcessing(id);
}catch(Exception e){
service.changeStatusInNewTransaction(id, "failed");
}
}
Having a transaction around a method that executes for several hours, seems like a design mistake, so method1() should not have #Transactional! When you start a transaction, you need a connection and this connection will be allocated from you connection pool for the entire duration, which greatly limits scalability (and pisses of your DBA).
could not initialize proxy - no Session
You get this error because (without #Transactional on method1) your entity is detached after repository.save() has been called, and you can't load the lazy collections. A quick solution for this is to inject an EntityManager into object2 and call EntityManager.refresh() inside method2() this does not require a transaction, as you are only reading data.
There is no reason to use any sort of Transaction propagation to solve this issue.
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.t4bt.gov.persistence.entities.Experts.institutaionList, no session or session was closed
You provide very little details in your question (code?), so it will have to be a generalized answer regarding lazy loading. In the future, if you want answers, please provide concrete information about the actual problem, as well as descriptions as to what you have tried to solve it.
A LazyInitialization occurs when you try to access a lazily loaded property after the session is closed (which is usually after the transaction has ended). The way lazy initalization works is that it doesn't fetch the lazily initialized properties when you fetch the object, but when you actually try to access it, Hibernate does another query to the database to fetch it.
The following would produce such an error:
public class Something {
[...]
#OneToMany(fetch = FetchType.LAZY)
private List<SomethingElse> somethingElse;
public List<SomethingElse> getSomethingElse() {
return somethingElse;
}
}
public class SomethingDao {
#Inject
private EntityManager em;
#Transactional
public Something getById(final Integer id) {
return em.find(Something.class, id);
}
}
public class SomethingService {
#Inject
private SomethingDao dao;
public List<SomethingElse> getSomethingElseForSomething(final Integer somethingId) {
final Something something = dao.getById(somethingId);
return something.getSomethingElse() //Throws LazyInitializationException
}
}
Here the transaction (and thus the session) only exists in the dao-class. Once leaving the dao-method, the session is gone. So, when you try to access a lazy-loaded property in the service, it will fail when Hibernates tries to contact the session in order to retrieve it.
To avoid this, there are several possibilities.
Change the annotation of the Something-class to #OneToMany(fetch = FetchType.EAGER)
The property is no longer lazy-loaded, so no more problems.
Add #Transactional to Service-method. Then the call to getSomethingElse() would be in the same transaction as the fetching of the Something-object, and the session will still be alive when doing so.
Add a call to getSomethingElse() in the Dao-method. Then it will initialize the property (fetch it from the database) before leaving the Dao-class (and the transaction), and it will be available outside the transaction, with no need to communicate with the session in order to retrieve it.
MyObject myObject = repositoryHibernateImpl.getMyObjectFromDatabase();
//transaction is finished, and no, there is not an option to reopen it
ThirdPartyUtility.doStuffWithMyObjectType( myObject );
at this point you've already defined what is lazy and eager loaded, and the third party utility will try to call all of the methods on your "myObject" instance, this is fine because you don't want to return anything for the lazily loaded properties, unfortunately it doesn't return null, it throws a LazyInitializationException.
This happens because you're actually calling the method on Hibernate's proxy of your object, and it knows that it hasn't fetched that data, and throws an exception.
Is it even possible to get the underlying object with null values so that a getter just returns null, and doesn't throw an exception? Basically detaching the object so that Hibernate is no longer aware of it at all. The accessor to the object that is lazily loaded must return null, it cannot return the actual values, we want to be able to convert the entity into a POJO without having to create an object that looks just like the entity and has to remap all the values.
Let's say you have a field, in the getter you could:
MyField getMyField() {
if (Hibernate.isInitialized(myField)) {
return myField;
}
return null;
}
From the javadoc of org.hibernate.Hibernate:
public static boolean
isInitialized(Object proxy): check if
the proxy or persistent collection is
initialized.
If you don't want to couple your domain to Hibernate, another possibility is to have your DAO instantiate your own instance of the entity from inside getMyObjectFromDatabase() and populate that with the appropriate fields from Hibernate's proxy. I've done this and it works well.
Obviously this is more code, but you're guaranteed a "pure" instance of your entity (complete with null uninitialized values) if that's what you want.
check my solution.
Minimal example:
I do not load the property from the object but from the controller.
Code:
{..}
MyProp prop = employeeController.getMyProp(employee);
{..}
This initiaqlizes the property via repository object and returns it.
EmployeeController.java:
public Set<MyProp> getMyProp(Employee employee) {
if (this.employeeRepository.InitMyProp(employee)){
return employee.getMyProp();
}
return null;
}
Repository get/open the session, reload employee object ! and initialize lazy loaded field
EmployeeRepository.java:
public boolean InitMyProp(Employee employee) {
if (Hibernate.isInitialized(employee.getMyProp())){
return true;
}
try {
Session session = getSession();
session.refresh(employee);
Hibernate.initialize(employee.getMyProp());
} catch (Exception ex) {
return false;
}
return true;
}
private Session getSession(){
if (session == null || !session.isConnected() || !session.isOpen()){
session = HibernateUtil.getSessionFactory().getCurrentSession();
}
if (!session.getTransaction().isActive()) {
session.beginTransaction();
}
return session;
}
I have in my solution a TableView with several thousand records and 2 further TableViews with details on the selected record in the first TableView.
hope it helps.