LazyInitializationException,对于int

时间:2015-07-28 11:02:44

标签: java hibernate h2 lazy-initialization proxy-classes

带整数字段的简单类:

@Entity
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Table(name = "myObject")
public class MyObject 
{
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(columnDefinition = "int default 0")
  @Index(name = "refCount")
  private int refCount;

  public int getRefCount(){ return refCount; }
}

使用简单的Utility方法从数据库中提取对象:

Session session = SessionFactoryUtil.getCurrentSession();
Transaction tx = session.beginTransaction();

criteria.setFetchSize(1);
T object = (T) criteria.uniqueResult();

// I tried to add this line, but it made no difference
Hibernate.initialize(object);

tx.commit();
return object;

问题如下: 在获取此对象后不久,我调用了getRefCount方法。那时我遇到以下异常:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:164)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:285)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
    at mypackage.MyObject_$$_javassist_1.getRefCount(MyObject_$$_javassist_1.java)

我的hibernate配置文件(即hibernate.cfg.xml)包含以下属性:

 <property name="hibernate.current_session_context_class">thread</property>

我不明白: 如果这会发生在集合中,那么我只需要添加fetch = FetchType.LAZY注释。但是这个简单的int字段不是连接。为什么int首先会被Proxy包裹在内?

我尝试添加Hibernate.initialize(object);行,但它没有任何区别。

我还尝试了hibernate.current_session_context_class="managed"设置。之后我必须手动启动和停止所有会话。我在每次获取时打开它并在finally块中关闭它。但这也没有区别。

这是我的第一个Hibernate项目之一。我开始怀疑在调用hibernate对象上的getter之前是否应该打开一个事务。

我没有使用Spring,只是使用Hibernate。

编辑:实际上有第二个对象

实际上有一个父对象(我最初认为它并不重要)。此Parent对象包含指向MyObject

的链接
@Entity
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Table(name = "parentObject")
public class ParentObject 
{
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  // link
  @ManyToOne(optional = false, fetch = FetchType.LAZY)
  @ElementCollection(targetClass = MyObject.class)
  @JoinColumn(name = "myObjectId")
  private MyObject myObject;

  public MyObject getMyObject(){ return myObject; }
}

会发生什么:

  1. 获取Parent对象
  2. 调用
  3. parent.getMyObject()来获取MyObject实例
  4. MyObject实例实际上是一个没有任何字段的代理。
  5. 只要在此MyObject个实例上调用方法,我就会获得LazyInitializationException
  6. 当我获取对象时,我确保存在会话并创建事务。但在取出后我立即关闭了交易。

    当我调用getMyObject()或调用getter时,我没有创建事务。我猜这就是问题所在。我会测试是否有所作为。

    编辑2:

    事实证明我确实需要在事务中调用getter。但这本身还不够。

    第二个问题是Parent对象是在已提交的事务中获取的。因此,代理对象不再绑定到事件。我想这就是他们所谓的“超然对象”。 (哈哈,我只是在学习这里。)

    我必须通过调用Session#update(proxy)方法“重新附加”此对象。现在终于可以无异常地调用getter。

    // uses a transaction internally
    Parent parent = MyHibernateUtil.fetch(Parent.class, ...);
    MyObject object = parent.getMyObject();
    
    ...
    
    // create a new transaction
    Session session = SessionFactoryUtil.getCurrentSession();
    Transaction tx = session.beginTransaction();
    
    // reattach the object
    SessionFactory.getCurrentSession().update(myObject);
    
    int count = myObject.getRefCount();
    tx.commit();
    

    但是我从这个问题中学到的是我可能以错误的方式使用交易。我想我应该做更长的事务,包含提取和对getter的调用。对吗?

2 个答案:

答案 0 :(得分:1)

在关闭事务之前尝试调用getId函数。不知道会发生什么,只是一个建议。

答案 1 :(得分:1)

我认为整个对象(在你的情况下是MyObject)是代理的。你可以调用getId而不是getRefCount()吗?