JPA和Hibernate代理行为 [英] JPA and Hibernate proxy behavior

查看:95
本文介绍了JPA和Hibernate代理行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图观察下面的JPA2/Hibernate4代理行为,

i tried to observe JPA2 / Hibernate4 proxy behavior below,

//具有延迟加载的循环实体:

// Circular entity with lazy loading:

@Entity
public class Employee {

 @Id@Generated
 int id;
 String name;
 @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
 Employee boss;

 public String toString() {
  return id + "|" + name + "|" + boss;
 }

 //getters and setters ...

}

//坚持存在的实体

// Outer entity:
Employee employee = new Employee();
employee.setName("engineer");
// Inner entity:
Employee boss = new Employee();
boss.setName("manager");
employee.setBoss(boss);

entityTransaction.begin();
entityManager.persist(employee);
entityTransaction.commit();
System.out.println(employee);

//输出:

Hibernate: insert into Employee (id, boss_id, name) values (default, ?, ?)
Hibernate: insert into Employee (id, boss_id, name) values (default, ?, ?)

2|engineer|1|manager|null

//加载外部实体:

String queryString = "select e from Employee e where e.id=" + employee.getId();
Query query = entityManager.createQuery(queryString);
Object loadedEmployee = query.getSingleResult();
System.out.println(loadedEmployee.getClass().getSimpleName());

//输出:

Hibernate: select employee0_.id as id2_, employee0_.boss_id as boss3_2_, employee0_.name as name2_ from Employee employee0_ where employee0_.id=2 limit ?

Employee

令我惊讶的是,上面加载的外部实体仍然是普通实体,但是我希望它是Hibernate proxylazy loading产生的结果.我可能在这里错过了一些东西,那么如何正确处理呢?一个简单而具体的示例将不胜感激!

To my surprise the loaded outer entity above is still the plain one, but i'd expected it to be Hibernate proxy resulting from lazy loading. I could have missed something here, so how to get it right? A simple yet concrete example is much appreciated!

@EDIT

根据来自@kostja的回答,我修改了代码并在下面的SE模式下对其进行了调试,因此既无法生成LazyInitializationException,也无法对其进行代理.还有其他提示吗?

According to the answer from @kostja i adapted the code and debugged it in SE mode below, neither could LazyInitializationException be produced, nor was boss property proxied. Any further hints?

@EDIT 2

最后,我将确认@kostja的答案无疑是很棒的.

Finally, i'd confirm that the answer from @kostja is undoubtly great.

我在EE模式下进行了测试,因此在下面观察到了proxied boss property

I tested in EE mode, so the proxied boss property was observed below,

//LazyInitializationException抛出:

public Employee retrieve(int id) {
 Employee employee = entityManager.find(Employee.class, id);
 // access to the proxied boss property outside of persistence/transaction ctx
 Employee boss = employee.getBoss();
 System.out.println(boss instanceof HibernateProxy);
 System.out.println(boss.getClass().getSimpleName());
 return boss;
}

//放置Spring Tx后绿灯:

@Transactional
public Employee retrieve(int id) ...

//输出:

true
Employee_$$_javassist_0

也可以参考推荐答案

这是预期的JPA行为.您的查询中的实体没有必要被代理-这是查询的常规结果.但是,此实体的boss属性应该是代理.它不会告诉您是否要询问-在对托管实体的延迟加载的属性执行任何操作时,都会触发提取.

This is the expected JPA behaviour. There is no reason for the entity from your query to be proxied - it is a regular result of a query. The boss property of this entity however should be a proxy. It will not tell if asked though - when you execute any operations on the lazily loaded property of a managed entity, it will trigger the fetch.

因此,您应该在事务外部访问boss属性.如果尚未获取,则会显示LazyInitializationException.

So you should access the boss property outside the transaction. If it has not been fetched, you will get a LazyInitializationException.

如何处理取决于EntityManagerPersistenceContext的类型.

How you would go about it depends on the kind of the EntityManager and PersistenceContext.

  • 仅从JPA 2.0开始有效-调用em.detach(loadedEmployee),然后访问boss属性.
  • works only since JPA 2.0 - call em.detach(loadedEmployee) and then access the boss property.

对于JPA 1:

  • 如果您在Java EE环境中,请用@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)标记该方法以暂停事务.

  • If you are in a Java EE environment, mark the method with @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) to suspend the transaction.

在具有用户事务的SE环境中,在访问boss属性之前调用transaction.commit().

In an SE environment with user transactions, call transaction.commit() before accessing the boss property.

如果使用的扩展PersistenceContext会使交易失效,请致电em.clear().

If using an EXTENDED PersistenceContext that will outlive the transaction, call em.clear().

EIDT:我想您未得到例外的原因是FetchType.LAZY只是JPA提供程序的提示,因此不能保证延迟加载该属性.与此相反,FetchType.EAGER保证了热切的获取.我想,您的JPA提供商选择急于加载.

EIDT: I suppose that the reason you are not getting the exception is that FetchType.LAZY is just a hint for the JPA provider, so there is no guarantee for the property to be loaded lazily. Contrary to that, FetchType.EAGER guarantees eager fetching. I suppose, your JPA provider chooses to load eagerly.

我重现了该示例,尽管有所不同,并且可复制地在log语句中获取了LazyInitializationException.该测试是一项Arquillian测试,该测试在基于Hibernate 4.0.1的JBoss 7.1.1和JPA 2.0上运行:

I have reproduced the example, though a bit differently and I am reproducibly getting the LazyInitializationException on the log statement. The test is an Arquillian test running on JBoss 7.1.1 with JPA 2.0 over Hibernate 4.0.1:

@RunWith(Arquillian.class)
public class CircularEmployeeTest {
    @Deployment
    public static Archive<?> createTestArchive() {
        return ShrinkWrap
                .create(WebArchive.class, "test.war")
                .addClasses(Employee.class, Resources.class)
                .addAsResource("META-INF/persistence.xml",
                        "META-INF/persistence.xml")
                .addAsResource("testSeeds/2CircularEmployees.sql", "import.sql")
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @PersistenceContext
    EntityManager em;

    @Inject
    UserTransaction tx;

    @Inject
    Logger log;

    @Test
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void testConfirmLazyLoading() throws Exception {
        String query = "SELECT e FROM Employee e WHERE e.id = 1";

        tx.begin();
        Employee employee = em.createQuery(query,
                Employee.class).getSingleResult();
        tx.commit();
        log.info("retrieving the boss: {}", employee.getBoss());
    }
}

这篇关于JPA和Hibernate代理行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆