We help IT Professionals succeed at work.

How should we annotate Java entities to persist lists and then make lazy loading work?

Jack McKenzie
on
In Java, I have a requirement to allow user-defined properties.

Example:
Database table: property

Table Row:
ID: 1
Label: Annual Salary

Database table: contact
ID: 1
Email: test@example.com
...

In addition to existing columns for contacts, users will define their own columns in Property Manager. Such column is "Annual Revenue" (number) for example.

So we need a table:
Database table: contact_properties
ID: 1
Contact_id: 1
Property_id 1

In this table we link user-defined properties to contacts

We however need more. We have 4 fixed (immutable) field types: Text, Number, Checkbox, Dropdown.
Every user-defined property should have a type, something like in MS Excel, but easier.

And finally, every user-defined property should belong to some group. Groups are user-defined too.

Database table: property_group

Row:
ID: 1
Label: Contact Information

ID: 2
Label: Company information

...

How should we annotate entities in Java to persist the above? Attached is all source code (mini project). See the entity package in module-properties. I have also prepared something quickly in the App.java (main) to test persistence.

The current exceptoin is:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: uk.co.mybigdata.crm.module.properties.entity.Property.optionList, could not initialize proxy - no Session
      at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
      at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:201) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
      at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:561) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
      at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
      at org.hibernate.collection.internal.PersistentBag.toArray(PersistentBag.java:283) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
      at uk.co.mybigdata.crm.application.App.run(App.java:85) [classes/:na]
      at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:797) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
      ... 5 common frames omitted
elements.zip
Comment
Watch Question

Distinguished Expert 2019
Commented:
It seems that you're trying to access the entities after the session is closed.

Can you try to process everything within a transaction?

org.hibernate.Transaction tx = session.beginTransaction(); 
...
tx.commit();

Open in new window

Jack McKenzieChief Executive Officer http://mybigdata.co.uk

Author

Commented:
Transactions are managed by the container, as you see in the code, so I have annotated App.java run() with @Transactional and it has helped!

The full answer comes from StackOverflow (the exact same stack trace - Hibernate attempts to lazy load from a bag, uses a temporary session if needed, then throws the exception because the session has been closed = same last 3 stack trace steps).

https://stackoverflow.com/questions/19304671/cannot-fetch-lazy-jpa

The solution is to eager load the collection, or annotate the method @Transactional, or make the session scope broader, possibly with a filter.