代码之家  ›  专栏  ›  技术社区  ›  Daniel Bingham

休眠单向关联问题

  •  4
  • Daniel Bingham  · 技术社区  · 16 年前

    我试图在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() 对其中一个创建的对象执行函数。这是代理错误,如下所示:

    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的文档和文章,试图弄清楚如何让它工作。

    我学到的是,休眠对象需要连接到一个会话。也就是说,它们是由创建的会话。当我创建我的对象时,在 getAllFoo() 这是那些对象所涉及的会话。在该会话中,BAR代理存在并有意义。但是,当我打电话的时候 t.commit() -因为我正在使用会话处理的线程方法-我正在结束该会话。结果是当我去打电话的时候 bar.getName() 稍后,Bar现在是一个孤立的代理。它是已关闭会话的代理。

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

    1)不要关闭初始会话。不打电话就把它打开 T.() 在里面 GETALOFFULL() .

    这很管用——不过,我想我用不上。我将同时装载数千个这样的物体。他们需要在用户有思考时间的情况下长时间保持开放-但我需要能够自由地访问这些关联。将来,数据库锁可能会出现并发问题。

    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;
    }
    

    然后我修改了 GETBARNAMEY() 打电话 getBar() 而不是访问栏。这导致了一个新错误:

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

    我想,即使在阅读了数天的教程、文章和Hibernate文档之后,我仍然不能完全理解这个结构。所以我要求StackOverflow对这个做一些说明。

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

    第二,处理会话的正确方法是什么——我如何真正做到这一点? 我想使用的方式是按请求进行会话——这似乎是最流行的,并且适用于我的情况。但是,这实际上是如何通过关联和延迟加载实现的呢?

    是的,我知道我不应该使用线程方法——但是我还有一整批与jta vs thread vs managed相关的问题,这个问题已经太长了。

    2 回复  |  直到 12 年前
        1
  •  1
  •   marklai    16 年前

    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
    

    //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);
    
        2
  •  0
  •   user271287    16 年前
    推荐文章