Java Hibernate单向关联的问题

Java Hibernate单向关联的问题,java,hibernate,session,associations,Java,Hibernate,Session,Associations,我试图在Java应用程序中实现Hibernate持久层,但遇到了一些问题。每次尝试访问简单的单向关联时,我都会遇到无代理错误。我没有以正确的方式实现Hibernate——我使用的是会话控制的线程方法,他们确实建议您不要在生产环境中使用它。但是,他们在教程中使用它。我仍在努力让基础知识发挥作用,所以我认为遵循教程就可以了。然而,我无法让这种简单的联系发挥作用。我有一个类似这样的类: public class Foo { private Long id; private String

我试图在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;
    }
}
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)
Foo的Bar类实现为一个简单的单向关联。它位于.hbm.xml中,如下所示:

<many-to-one name="bar" column="bar_id" not-null="true" />
hibernate实例配置为使用连接池1,并使用线程方法进行会话处理,如下所示:

<property name="connection.pool_size">1</property>
<property name="current_session_context_class">thread</property>
长话短说,这就是我开始时遇到的错误。从那以后,我花了几天时间阅读这里的Hibernate文档和帖子,试图找出如何实现这一点

我想我学到的是,Hibernate对象需要连接到会话。即,创建它们的会话。当我创建我的对象时,在
getAllFoo()
中,这就是那些对象所涉及的会话。在该会话中,Bar代理存在并有意义。但是,当我调用
t.commit()
——因为我使用的是会话处理的线程方法——我将结束该会话。其结果是,当我稍后调用
bar.getName()
时,bar现在是一个孤立的代理。该代理的会话已关闭

我找到了两种可能的解决方案:

1) 不要关闭初始会话。在
getAllFoo()
中不调用
t.commit()
使其保持打开状态

这是有效的——但是,我认为我不能使用它。我将一次加载数千个这样的对象。当用户有思考的时间时,他们将需要在一段较长的时间内保持开放,但我需要能够自由访问这些关联。将来,数据库锁可能会出现并发问题

2) 在调用关联之前,将对象重新附加到新会话

这不管用。也许我做错了-我找到的文档不清楚。在访问关联之前,我尝试启动一个新会话并调用
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。这导致了一个新的错误:

org.hibernate.PersistentObjectException: attempted to load into an instance that was already associated with the session: [Foo#1]
我想即使在阅读了几天的教程、帖子和hibernate文档之后,我仍然无法理解这种结构。因此,我要求StackOverflow对此进行解释

首先,如果您能弄清楚,我当前的代码中发生了什么?会话是打开的还是关闭的?为什么在第一个方法中有代理错误,但在第二个方法中声明会话仍然打开,对象仍然附加

第二,处理会话的正确方法是什么?我实际上是如何做到这一点的?我认为我希望使用的方法是每请求一次会话-这似乎是最受欢迎和适用于我的情况的方法。但是,这实际上是如何通过关联和延迟加载实现的呢


是的,我知道我不应该使用thread方法,但我还有一大堆问题与JTA vs thread vs Managed有关,这个问题已经太长了。

我认为Hibernate默认为延迟初始化。因此,当您在会话中加载foo列表时,在尝试使用它们之前,不会加载它们的关联栏。此时,您已经关闭了会话,这将导致错误

一些潜在的解决方案:

  • 在Foo-Bar关联上启用即时抓取
  • 获取时“加入”(实际上是一个急切的获取)
  • 尝试在会话中访问Bar(这将起作用,但您调用Foo.getBar()只是为了加载相关的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():

  • 异常是由于对象未从其他会话分离而导致的。它表明你没有关闭另一个会话

  • 处理问题的首选方法是在完成工作之前保持会话活动


  • 在某种程度上,这不是打破了延迟加载的整个要点吗?我认为延迟加载的目的是在需要数据之前不加载数据。如果我使用即时抓取或任何相关方法,我将把整个数据库加载到内存中。我以为hibernate的一部分就是尽量避免。。。难道没有办法让每个请求的延迟加载和会话一起玩得很好吗?是的。为了保持延迟加载,您可以将Foo实例重新附加到另一个Hibernate会话,然后它将能够加载关联的对象。然而,对于客户机代码,我很难将其变成一个脑死操作。他们必须记住什么是已加载的,什么不是…那么关闭会话的正确方法是什么?将对象重新附加到新创建的会话的正确方法是什么?更新了答案,并提供了参考文档中的一些示例。同样,客户端代码不能简单地执行[Foo-Foo=getFoo()],然后尝试[Bar-Bar=Foo.getBar()]。它需要依赖于像[Bar-getBar(Foo-f)]这样的dao方法,该方法在新会话中获取Bar。
    // 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
    
    //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);