Problems with Hibernate Single-User Associations

I am trying to implement a Hibernate persistence level in a Java application and I am having problems with it. I encounter a lack of proxy error every time I try to access a simple one-way association. I did not implement Hibernate completely correctly - I use the session control flow method that they suggest that you do not use it for production. However, they use it in their textbooks. I'm still trying to get the basics to work, so I decided that after the tutorials everything would be okay. However, I cannot get a simple association to work. I have a class that looks something like this:

public class Foo { private Long id; private String name; private Bar bar; // Static Handler Methods - I'll flesh these out further down in the question. public static List getAllFoo(); // Convenience methods public String getBarName() { return bar.getName(); } // Accessor methods public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Bar getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } } 

The Foo Bar class is implemented as a simple one-way communication. This is in the .hbm.xml file, for example:

 <many-to-one name="bar" column="bar_id" not-null="true" /> 

I create Foo in a static method inside Foo like this:

 public static List getAllFoo() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction t = session.beginTransaction(); List foos = session.createCriteria(Foo.class).list(); t.commit(); return foos; } 

The hibernate instance is configured to use connection pool 1 and uses the streaming processing method to handle the session as follows:

 <property name="connection.pool_size">1</property> <property name="current_session_context_class">thread</property> 

I encounter an exception every time I try to access the association using the getBarName() function on one of the created objects. This is a proxy error, it looks like this:

 org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) at Bar_$$_javassist_7.getName(Bar_$$_javassist_7.java) at Foo.getBarName(Bar.java:126) 

So, to make a long story short, this is where I started and the mistake I first encountered. Since then, I spent the last few days reading Hibernate documents and posts here to try and figure out how to do this.

What I found out, I think, is that Hibernate objects should be connected to the session. Namely, the Session they are created. When I create my objects, in getAllFoo() is the session to which these objects relate. In this session, the Bar proxy exists and makes sense. However, when I call t.commit() - because I use the Thread method to handle the session - I end this session. The result of this is that when I switch to calling bar.getName() later, bar will now become a proxy orphan. This is a proxy server whose session is closed.

I found two possible solutions:

1) do not close the initial session. Leave it open without typing t.commit() in getAllFoo() .

It worked - but I don't think I can use it. I am going to download thousands of these objects at once. They should remain open for a long period of time while the user thinks about time - but I need to be able to freely access associations. And in the future, there may be problems with concurrency database locks.

2) reconnect the object to the new session before calling the association.

This did not work. Perhaps I did it wrong - the documentation I found is not clear. I tried to start a new session and call session.load(this, this.id) before I turned to the association. Like this:

 public Bar getBar() { Bar tmp = null; Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction t = session.beginTransaction(); session.load(this, this.id); tmp = bar; t.commit(); return tmp; } 

Then I modified getBarName() to call getBar() instead of accessing the bar. As a result, a new error appeared:

 org.hibernate.PersistentObjectException: attempted to load into an instance that was already associated with the session: [Foo#1] 

I think that even after several days of reading textbooks, messages, and hibernation documentation, I still cannot wrap my head around this structure. Therefore, I ask StackOverflow to shed light on this.

First, if you can understand what is going on in the code that I currently have? Is the session open or closed? Why does he have a proxy error in the first method, but claim that the session is still open and the object is still bound to the second?

Secondly, what is the correct way to handle sessions - how can I make this work? . I think I want to use a session for each request - this is apparently the most popular and applicable to my situation. But then, how is this actually implemented with associations and lazy loading?

Yes, I know that I should not use the thread method, but I have a whole other batch of questions related to JTA vs Thread vs Managed, and this question is already too long.

+4
source share
2 answers

I believe Hibernate defaults to lazy initialization. Therefore, while you have downloaded the Foos list in a session, not one of the bars associated with them is loaded until you try to use them. You have already closed the session by then, which leads to an error.

Some possible solutions:

  • allows you to get binding to the association Foo-Bar
  • β€œjoin” to the sample (effectively looking)
  • try to access the panel in the session (this will work, but you call Foo.getBar () to get the associated Bar object).

Update from comments:

Session / Transaction Management Idiom:

 Session sess = factory.openSession(); Transaction tx; try { tx = sess.beginTransaction(); //do some work ... tx.commit(); } catch (Exception e) { if (tx!=null) tx.rollback(); throw e; } finally { sess.close(); } 

To reconnect "individual" objects (based on reference documents ):

if the object has been modified: update the object in a new session

 // in the first session Cat cat = (Cat) firstSession.load(Cat.class, catId); Cat potentialMate = new Cat(); firstSession.save(potentialMate); // in a higher layer of the application cat.setMate(potentialMate); // later, in a new session secondSession.update(cat); // update cat secondSession.update(mate); // update mate 

If the object is unmodified, you can use lock ():

 //just reassociate: sess.lock(fritz, LockMode.NONE); //do a version check, then reassociate: sess.lock(izi, LockMode.READ); //do a version check, using SELECT ... FOR UPDATE, then reassociate: sess.lock(pk, LockMode.UPGRADE); 
+1
source
  • The exception is caused by the fact that the object is not separated from another session. He suggested that you did not close another session.

  • The preferred way to solve your problem is to keep the session until you are done. Read it in detail .

0
source

Source: https://habr.com/ru/post/1301721/


All Articles