我正在尝试在Java应用程序中实现Hibernate持久层,但遇到了一些麻烦。每次尝试访问简单的单向关联时,我都会遇到无代理错误。我没有以正确的方式实现Hibernate- 我正在使用会话控制的线程方法,他们确实建议您不要将其用于生产。但是,他们在教程中使用它。我仍在尝试使基础知识正常运行,因此我认为按照教程进行操作是可以的。但是,我无法使用简单的关联来工作。我有一堂课,看起来像这样:
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; } }
Foo的Bar类实现为简单的单向关联。像这样在.hbm.xml中:
<many-to-one name="bar" column="bar_id" not-null="true" />
我在Foo内部的静态方法中创建Foo,如下所示:
public static List getAllFoo() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Transaction t = session.beginTransaction(); List foos = session.createCriteria(Foo.class).list(); t.commit(); return foos; }
hibernate实例配置为使用1的连接池,并使用线程方法进行会话处理,如下所示:
<property name="connection.pool_size">1</property> <property name="current_session_context_class">thread</property>
每次尝试使用getBarName()创建的对象之一上的函数访问关联时,都会遇到异常。这是一个代理错误,看起来像这样:
getBarName()
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)
简而言之,这就是我刚开始时遇到的错误。从那以后,我花了几天的时间阅读Hibernate文档和此处的文章,以尝试弄清楚如何进行这项工作。
我了解到-我认为- Hibernate对象需要连接到Session。即,它们创建的会话。当我创建对象时getAllFoo(),就是那些对象所涉及的会话。在该会话中,律师代理存在并且有意义。但是,当我打电话时t.commit()-因为我使用的是Session处理的Thread方法- 我要结束该Session。其结果是,当我以后去打电话时bar.getName(),bar现在是一个孤立的代理。这是一个代理,其会话已关闭。
getAllFoo()
t.commit()
bar.getName()
我发现了两种可能的解决方案:
1)不要关闭初始会话。通过不打电话t.commit()来使其保持打开状态getAllFoo()。
这行得通-但是,我认为我无法使用它。我将一次加载数千个这些对象。他们将需要长时间保持开放,而用户需要思考的时间- 但是我需要能够自由访问关联。将来,数据库锁可能会出现并发问题。
2)在调用关联之前,将对象重新附加到新的Session。
这没用。也许我做错了-我发现的文档不清楚。我尝试开始一个新的会话并session.load(this, this.id)在访问该关联之前进行呼叫。像这样:
session.load(this, this.id)
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; }
然后getBarName(),我修改为调用getBar()而不是访问bar。这导致了一个新的错误:
getBar()
org.hibernate.PersistentObjectException: attempted to load into an instance that was already associated with the session: [Foo#1]
我想即使在阅读了几天的教程,帖子和hibernate文档之后,我仍然无法绕过这种结构。因此,我要求StackOverflow对此进行一些说明。
首先,如果您能弄清楚,我目前拥有的代码中发生了什么? 会话是打开还是关闭?为什么在第一种方法中出现代理错误,但是在第二种方法中却声称会话仍被打开并且对象仍处于连接状态?
其次,处理会话的正确方法是什么-我该如何实际工作? 我认为我要使用的方式是每个请求会话- 这似乎是最受欢迎的,并且适用于我的情况。但是,该如何在关联和延迟加载中实现呢?
是的,我知道我不应该使用线程方法-但是我还有其他一系列与JTA,线程与托管相关的问题,这个问题已经太长了。
我相信Hibernate默认为延迟初始化。因此,当您在会话中加载Foos列表时,在尝试使用它们之前,不会加载任何与它们关联的Bar。到那时,您已经关闭了会话,从而导致错误。
一些潜在的解决方案:
评论更新:
用于会话/事务管理的惯用语:
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(); }
重新附加“分离的”对象(基于参考文档):
如果对象已被修改:在新会话中更新对象
// 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
如果对象未修改,则可以使用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);