Hibernate Lazy Initialization Exception for loading lazy list - java

I have these two entities
#Entity
#Table(name = "CallSession")
public class CallSession implements Serializable {
private long id;
private List<CallParticipant> members;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#OneToMany(mappedBy = "callSession", fetch = FetchType.LAZY)
public List<CallParticipant> getMembers() {
return members;
}
public void setMembers(List<CallParticipant> members) {
this.members = members;
}
}
#Entity
#Table(name = "CallParticipant")
public class CallParticipant implements Serializable {
private long id;
private CallSession callSession;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#ManyToOne
public CallSession getCallSession() {
return callSession;
}
public void setCallSession(CallSession callSession) {
this.callSession = callSession;
}
}
but when I invoke callSession.getMembers() method,
I get this Exception:
Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.
I cannot make a head or tail of why I get this error? Why do I get this error and how can I fix this?

I’m going to start by assuming you want your collection to be lazy loaded.
Hibernate’s session can be closed in a lot of contexts, and once the session is closed, it won’t be able to fetch any lazy loaded collections.
Generally Hibernate is very good at keeping sessions open for the lifecycle of HTTP threads in a web app context (Spring’s “open session in view”). Reasons the session could be closed include that the object was handed off from one thread to another, or the object was cached and then accessed by another thread.
But it can be more difficult if your code is running in a job or a non-web application context.
Fixes
1. Create a repository method to explicitly fetch the collection
Using #Query and join fetch, add a repository method that explicitly eager-loads the collection.
2. Call .toString() on the collection after fetching the object.
This is a nasty hack that I’ve seen many people use in the real world before. Basically, before caching the object or handing it off to an executor or somewhere where it would be accessed by another thread, call .toString() on the collection to load it. Usually leave a comment explaining why.
3. Add #Transactional to a method that is both fetching the data and accessing the collection
This has many implications other than keeping the session alive (e.g. database operations succeed and fail together), but can be a quick fix to keep the session alive in for example a job method.
Hope this helps.

Related

Thread safe spring data delete

I have two entities with one-to-one association. And some services that works with them in parallel. I need to delete one, but sometimes i have DataIntegrityViolationException, that as i understand means that i can't delete some entity while other has foreign key on it.
Here is my entities:
public class Foo{
#Id
#GeneratedValue
private Long id;
}
and:
public class Bar{
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Foo foo;
}
Method that doesn't work (all repos extends from JpaRepository):
#Transactional
public void deleteFoo(Long fooId) {
barRepository.deleteAllByFooId(fooId);
fooRepository.deleteById(fooId);
}
And some method that works in parallel and breaks everything:
#Transactional
public void method(Long fooId) {
...
Foo foo = fooRepository.findById(fooId);
barRepository.save(new Bar(foo));
...
}
So i have ConstraintViolationException, as i understand because im trying to delete Foo but i have that new Bar(foo) in method that wasn't deleted by barRepository.deleteAllByFooId(fooId) in deleteFoo .
I need some approach like "delete method should wait until all current transactions finishes and then run only one transaction".
Can't use KeyLockManager because real structure of project already enough complicated and if i use it it should be same lock in many classes.
The problem is that you try to delete entity that being used in transaction. To close transaction you could use repository's save() method.
Also you should look at cascades and orphan removal. Use like this:
In Bar entity:
#ManyToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
private Foo foo;
And delete like so:
barRepository.save(bar);
or so:

CascadeType doesn't work in in-memory database

I have a webapp with database where two Entities have many-to-many relationship but I implemented join table manually. When one of the entities gets deleted it deletes all entries in the join table and updates the other entity so all works perfectly fine, but now I'm supposed to write a test for this feature. For tests I am using in-memory database and that's really the only difference, the same methods with the same annotations (and cascade types) are called but I keep getting:
org.hibernate.exception.ConstraintViolationException: integrity constraint violation: foreign key no action; FKC17477FD8940DF2B table ENTITY1_ENTITY2
I didn't paste any code as I don't believe there is anything wrong with it since it's working. I don't ask to fix this for me, I just need to know what is likely to cause this kind of behavior because I've just ran out of ideas and I don't know what else to search for... Thanks
EDIT: here's some code:
#Entity
#Table(name = "interviewer")
public class Interviewer implements Identifiable {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "interviewer_id")
private Collection<InterviewerTechnology> technologies;
}
#Entity
#Table(name = "technology")
public class Technology implements Identifiable {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "technology_id")
private Collection<InterviewerTechnology> technologies;
}
#Entity
#Table(name = "interviewer_technology")
public class InterviewerTechnology implements Identifiable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
private Interviewer interviewer;
#ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
private Technology technology;
}
#Component
public class TechnologyDao extends AbstractEntityDao<Technology> {
public void remove(Integer id) {
Technology technology = find(id);
em.remove(technology);
}
}
This code does exactly what I want it to do, it just seems like database used for tests does not see CascadeType parameters that do all the job here
I have found the problem and it was #Transactional annotation. All my test DAOs were extending generic test DAO which was annotated with #Transactional and I blindly annotated every single DAO with it again. The problem here is that some operations need to be performed as single transactions (may need flush() after being executed) so that data is available for other operations straight away. Consider following example:
#Transactional
public abstract class AbstractEntityDao<E> {
#PersistenceContext(unitName = "some-persistence")
protected EntityManager em;
public E create(E e) {
em.persist(e);
return e;
}
(...)
}
which means that every method in this class is a transaction. Now if we annotate another class that extends this class with #Transactional every method will be another transaction, which means if we delete several things in one method it should take several transactions (they all need flush() method to be called in order to execute cascade) but instead they will run as one transaction (unless we specify Propagation). Let this be a lesson for everyone (especially me) to think carefully about which operations need separate transactions and which can be executed as one.

hibernate jpa entitymanager commit not writing objects to the database

I'm using hibernate JPA (without Spring) and it's working well, but I have come across a problem which has stumped me for the last 3 days.
I have written some generic DAO classes and am using them to persist my objects. They all work fine, except for one class of object which is not being persisted. No exceptions are thrown. I've tried debugging inside the hibernate code and found that the reason the entity is not being persisted is that in the org.hibernate.event.def.DefaultFlushListener onFlush() method, source.getPersistenceContext().getEntityEntries().size() == 0 so no flushing is performed. But I can't work out why that would be the case.
The classes in question look like this:
#Entity
#Table(name="er_batch_runs")
public class BatchRun implements Serializable, Comparable<BatchRun>, BatchBean {
private Long runId;
private String hostname;
.... more field here
#Override
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="runseq")
#SequenceGenerator(name="runseq", sequenceName="er_batch_runs_seq", allocationSize=1 /*, initialValue = 10*/)
#Column(name="batch_run_id")
public Long getId() {
return runId;
}
public void setId(long runId) {
this.runId = runId;
}
#Column(name="hostname")
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
pretty straightforward hibernate JPA stuff.
Here's another class:
#Entity
#Table(name="er_batch_txns")
public class BatchTxn implements Serializable, Comparable<BatchTxn>, BatchBean {
private long id;
.......... more fields
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="batchtxnseq")
#SequenceGenerator(name="batchtxnseq", sequenceName="ER_BATCH_TXNS_SEQ", allocationSize=1/*00, initialValue = 10*/)
#Override
#Id
#Column(name="BATCH_TXN_ID")
public Long getId() {
return id;
}
the BatchBean interface is what allows me to use generic DAOs like this:
public Long create(BatchBean newInstance) {
getOpenEntityManager().persist(newInstance);
logger.debug("hopefully created {} with id {}",newInstance.getTypeName(),newInstance.getId());
return newInstance.getId();
}
Transactions are being handled manually. I've set the flush type to COMMIT (ie flush on commit) and when I've completed the persist, I do a commit. After the persist, then BatchTxn object has been assigned a primary key from the sequence. When I debug hibernate I can see that getPersistenceContext().getEntityEntries() returns an empty Map.
so the question is why the BatchTxn is not being persisted by the commit, when the BatchRuns, and 5 other classes which implement BatchBean, are?
I'm using hibernate 3.6.0 Final
The only thing I saw that is suspected in your code is this in the BatchTxn class:
private long id;
This will be set automatically to zero. Maybe you should use Long (with a capital letter)?

GWT JPA - The response could not be deserialized

I use GWT and JPA for persistence. I have created a domain JPA enchanted classes, DAO's and RPC for communication between them. Everything works fine, through RPC the client sends the object to server but could not get response. Server cannot deserialize in a compatible way with the client side. So i cannot use the server callBack back to the client. The exception message is this:
The response could not be
deserialized,
com.google.gwt.user.client.rpc.SerializationException
Here's a sample code of one of my classes:
#Entity
#Table(name="course")
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
private int courseId;
private String name;
private List<Group> groups;
private List<Module> modules;
public Course() {
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(unique=true, nullable=false)
public int getCourseId() {
return this.courseId;
}
public void setCourseId(int courseId) {
this.courseId = courseId;
}
#Column(nullable=false, length=100)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
//bi-directional many-to-one association to Group
#OneToMany(mappedBy="course", fetch=FetchType.LAZY)
public List<Group> getGroups() {
return this.groups;
}
public void setGroups(List<Group> groups) {
this.groups = groups;
}
//bi-directional many-to-one association to Module
#OneToMany(mappedBy="course", fetch=FetchType.LAZY)
public List<Module> getModules() {
return this.modules;
}
public void setModules(List<Module> modules) {
this.modules = modules;
}
}
If i remove the relationships it work's fine. This is done because collections like lists, set's e.t.c are converted into hibernate objects that cannot be handled by GWT client side.
Well the problem is that my class has #OneToMany association to another class. If i remove the association it work's fine. But it's impossible to that, since I use a normalized relational database
If you're using GWT-RPC, make sure that all of the classes you're trying to serialize have a public default (no-argument) constructor and implement Serializable. If you have embedded classes, they must also have a no-arg constructor.
Once , I have prepared gwt-jpa sample for this question. It is just serialization of JPA entity.. It might give you a clue about what is wrong in your case..
I used Gilead and it fixed the issue.
Please check the corresponding post: GWT with JPA
got it working...after a redeploy of war again...strange..cant point to one specific thing (as i did clear browser cache/eclipse classes output/restart eclipse)
Apparently workaround seems to be try redeploying webapp whenever this issue occurs..

Hibernate annotated many-to-one not adding child to parent Collection

I have the following annotated Hibernate entity classes:
#Entity
public class Cat {
#Column(name = "ID") #GeneratedValue(strategy = GenerationType.AUTO) #Id
private Long id;
#OneToMany(mappedBy = "cat", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Kitten> kittens = new HashSet<Kitten>();
public void setId(Long id) { this.id = id; }
public Long getId() { return id; }
public void setKittens(Set<Kitten> kittens) { this.kittens = kittens; }
public Set<Kitten> getKittens() { return kittens; }
}
#Entity
public class Kitten {
#Column(name = "ID") #GeneratedValue(strategy = GenerationType.AUTO) #Id
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Cat cat;
public void setId(Long id) { this.id = id; }
public Long getId() { return id; }
public void setCat(Cat cat) { this.cat = cat; }
public Cat getCat() { return cat; }
}
My intention here is a bidirectional one-to-many/many-to-one relationship between Cat and Kitten, with Kitten being the "owning side".
What I want to happen is when I create a new Cat, followed by a new Kitten referencing the Cat, the Set of kittens on my Cat should contain the new Kitten. However, this does not happen in the following test:
#Test
public void testAssociations()
{
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
Cat cat = new Cat();
session.save(cat);
Kitten kitten = new Kitten();
kitten.setCat(cat);
session.save(kitten);
tx.commit();
assertNotNull(kitten.getCat());
assertEquals(cat.getId(), kitten.getCat().getId());
assertTrue(cat.getKittens().size() == 1); // <-- ASSERTION FAILS
assertEquals(kitten, new ArrayList<Kitten>(cat.getKittens()).get(0));
}
Even after re-querying the Cat, the Set is still empty:
// added before tx.commit() and assertions
cat = (Cat)session.get(Cat.class, cat.getId());
Am I expecting too much from Hibernate here? Or is the burden on me to manage the Collection myself? The (Annotations) documentation doesn't make any indication that I need to create convenience addTo*/removeFrom* methods on my parent object.
Can someone please enlighten me on what my expectations should be from Hibernate with this relationship? Or if nothing else, point me to the correct Hibernate documentation that tells me what I should be expecting to happen here.
What do I need to do to make the parent Collection automatically contain the child Entity?
It won't automatically add it. You have to add it yourself.
I wouldn't directly call Kitten.setCat() either. The typical pattern for this is to put a method in Cat like:
public void addKitten(Kitten kitten) {
if (kittens == null) {
kittens = new HashSet<Kitten>();
}
kittens.add(kitten);
kitten.setCat(this);
}
and then simply call:
cat.addKitten(kitten);
When working with bi-directional associations, you have to handle both sides of the "link" and it is very common to use defensive link management methods for that, as suggested by #cletus. From Hibernate Core documentation:
1.2.6. Working bi-directional links
First, keep in mind that Hibernate
does not affect normal Java semantics.
How did we create a link between a
Person and an Event in the
unidirectional example? You add an
instance of Event to the collection of
event references, of an instance of
Person. If you want to make this link
bi-directional, you have to do the
same on the other side by adding a
Person reference to the collection in
an Event. This process of "setting
the link on both sides" is absolutely
necessary with bi-directional links.
Many developers program defensively
and create link management methods to
correctly set both sides (for example,
in Person):
protected Set getEvents() {
return events;
}
protected void setEvents(Set events) {
this.events = events;
}
public void addToEvent(Event event) {
this.getEvents().add(event);
event.getParticipants().add(this);
}
public void removeFromEvent(Event event) {
this.getEvents().remove(event);
event.getParticipants().remove(this);
}
The get and set methods for the
collection are now protected. This
allows classes in the same package and
subclasses to still access the
methods, but prevents everybody else
from altering the collections
directly. Repeat the steps for the
collection on the other side.
More References
1.2.6. Working bi-directional links (this one is the more obvious)
6.3.2. Bidirectional associations
Chapter 21, Example: Parent/Child
Java Persistence with Hibernate

Categories