java – Hibernate单向关联的麻烦

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

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()函数访问关联时,我都遇到异常.这是代理错误,它看起来像这样:

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()中,这些对象与这些对象相关.在那个会话中,Bar代理存在并且有意义.但是,当我调用t.commit()时 – 因为我正在使用会话处理的Thread方法 – 我结束了那个Session.结果是,当我稍后调用bar.getName()时,bar现在是一个孤立的代理.这是一个会话已关闭的代理人.

我发现了两种可能的解决方

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

这工作 – 但是,我不认为我可以使用它.我将同时加载数千个这些对象.当用户有思考时,他们需要长时间保持开放 – 但我需要能够*地访问这些关联.未来可能存在数据库锁的并发问题.

2)在调用关联之前将对象重新附加到新的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()而不是访问bar.这导致了一个新错误:

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

我想即使经过几天的阅读教程,帖子和hibernate文档,我仍然无法围绕这个结构.所以我请求*对此有所了解.

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

其次,处理Sessions的正确方法是什么 – 我该如何实现这项工作?我认为我想要使用的方式是每个请求的会话 – 这似乎是最受欢迎和适用于我的情况.但那么实际上如何实现关联和延迟加载?

是的,我知道我不应该使用线程方法 – 但是我有另外一批与JTA vs Thread vs Managed有关的问题,这个问题已经太长了.

解决方法:

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

一些潜在的解决方

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

来自评论的更新:

会话/交易管理的成语:

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):

如果已修改对象:在新会话中更新对象

// 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);
上一篇:mysql – 仅从行和关联中获取Sequelize的值


下一篇:php – 我们如何进行laravel多级关联