java - Hibernate 单向关联问题

标签 java hibernate session associations

我正在尝试在 Java 应用程序中实现 Hibernate 持久层,但遇到了一些问题。每次尝试访问简单的单向关联时,我都会遇到无代理错误。我没有以非常正确的方式实现 Hibernate - 我正在使用 Session 控制的线程方法,他们确实建议您不要将其用于生产。但是,他们在教程中使用它。我仍在努力让基础工作,所以我认为遵循教程会很好。但是,我无法让简单的关联发挥作用。我有一个看起来像这样的类(class):

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 并使用线程方法进行 session 处理,如下所示:
<property name="connection.pool_size">1</property>
<property name="current_session_context_class">thread</property>

每次尝试使用 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 。即,它们是由创建的 session 。当我创建对象时,在 getAllFoo()那是那些对象相关的 session 。在那个 session 中,Bar 代理存在并且有意义。但是,当我调用 t.commit() 时- 因为我正在使用 session 处理的 Thread 方法 - 我正在结束该 session 。这样的结果是,当我去调用bar.getName()后来,bar 现在是一个孤立的代理。它是 session 已关闭的代理。

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

1)不要关闭初始 session 。不要打电话,让它保持打开状态 t.commit()getAllFoo() .

这有效 - 但是,我认为我无法使用它。我将同时加载数千个这些对象。当用户有思考时间时,它们将需要长时间保持打开状态 - 但我需要能够自由访问关联。将来,数据库锁可能会出现并发问题。

2)在调用关联之前将对象重新附加到新 session 。

这没有用。也许我做错了 - 我发现的文档不清楚。我试图开始一个新的 session 并调用 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()而不是访问栏。这导致了一个新的错误:
org.hibernate.PersistentObjectException: attempted to load into an instance that was already associated with the session: [Foo#1]

我想即使在阅读了几天的教程、帖子和 hibernate 文档之后,我仍然无法理解这个结构。所以我要求 StackOverflow 阐明这一点。

首先,如果你能弄清楚,我目前拥有的代码中发生了什么? session 是打开还是关闭?为什么在第一种方法中出现代理错误,但在第二种方法中声称 session 仍然打开并且对象仍然附加?

其次,处理 Sessions 的正确方法是什么 - 我如何实际完成这项工作? 我想我想使用的方式是每个请求的 session - 这似乎是最流行和最适用于我的情况的方式。但是,这实际上是如何通过关联和延迟加载来实现的呢?

是的,我知道我不应该使用线程方法 - 但我还有一整批与 JTA、线程和托管相关的问题,这个问题已经太长了。

最佳答案

我相信 Hibernate 默认为延迟初始化。因此,当您在 session 中加载 Foos 列表时,在尝试使用它们之前,不会加载任何关联的 Bars。到那时,您已经关闭了 session ,这会导致错误。

一些潜在的解决方案:

  • 在 Foo-Bar 关联上启用即时获取
  • “加入”获取(实际上是一个急切的获取)
  • 尝试访问 session 中的 Bar(这会起作用,但您调用 Foo.getBar() 只是为了加载关联的 Bar 对象)

  • 评论更新:

    session /事务管理的习语:
    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();
      }
    

    重新附加“分离的”对象(基于 reference docs):

    如果对象已被修改:在新 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
    

    如果对象未修改,可以使用 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);
    

    关于java - Hibernate 单向关联问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2290414/

    相关文章:

    java - 避免 Java 中泛型的警告

    java - Hibernate Criteria 无法检索嵌套对象的集合

    php - 如何在领域驱动设计中处理用户 session

    java - 如何使用 log4j2.xml 配置 hibernate 日志记录?

    asp.net - 在 SessionPageStatePersister 中保持 ViewState

    javascript - 如何在javascript中访问存储在 session 中的hashmap?

    java - 解析json数组android

    java - JPA/Jackson - 反序列化时排除字段并在序列化时包含它们

    java - 如何根据条件获取 JSON 值

    java - PSQL异常 : ERROR: could not identify an equality operator for type xml