sof*_*sof 4 hibernate jpa jpa-2.0 hibernate-4.x
我试着在下面观察JPA2/Hibernate4代理行为,
//延迟加载的循环实体:
@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 ...
}
Run Code Online (Sandbox Code Playgroud)
//坚持实体:
// 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);
Run Code Online (Sandbox Code Playgroud)
//输出:
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
Run Code Online (Sandbox Code Playgroud)
//加载外部实体:
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());
Run Code Online (Sandbox Code Playgroud)
//输出:
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
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,上面加载的外部实体仍然是普通的实体,但我预计它将Hibernate proxy由此产生lazy loading.我可能在这里错过了一些东西,那么如何才能做到正确?一个简单但具体的例子非常感谢!
@编辑
根据@kostja我的回答我调整了代码并在下面以SE模式调试它,既不能LazyInitializationException生成也不能boss property代理.还有什么提示吗?


@EDIT 2
最后,我确认答案@kostja无疑是伟大的.
我在EE模式下测试,所以在proxied boss property下面观察,
// 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;
}
Run Code Online (Sandbox Code Playgroud)
//投入Spring Tx使用后的绿灯:
@Transactional
public Employee retrieve(int id) ...
Run Code Online (Sandbox Code Playgroud)
//输出:
true
Employee_$$_javassist_0
Run Code Online (Sandbox Code Playgroud)
这是预期的JPA行为.您的查询中的实体没有理由被代理 - 这是查询的常规结果.boss但是,该实体的财产应该是代理人.它不会告诉我是否 - 当你对托管实体的延迟加载属性执行任何操作时,它将触发获取.
所以你应该访问交易之外的boss属性.如果没有提取,你会得到一个LazyInitializationException.
那你怎么去了解它取决于同类产品中EntityManager和PersistenceContext.
em.detach(loadedEmployee)然后访问该boss属性.对于JPA 1:
如果您在Java EE环境中,请使用方法标记@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)以暂停事务.
在具有用户事务的SE环境中,transaction.commit()在访问boss属性之前调用.
如果使用的EXTENDED PersistenceContext会比交易更长,请致电em.clear().
EIDT:我认为你没有得到异常的原因是这FetchType.LAZY只是JPA提供者的一个提示,因此无法保证懒惰地加载该属性.与此相反,FetchType.EAGER保证渴望获取.我想,你的JPA提供商选择热切地加载.
我已经复制了这个例子,虽然有点不同,我可以重复地获取LazyInitializationException日志声明.该测试是在JBoss 7.1.1上运行的Arquillian测试,JPA 2.0基于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());
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4301 次 |
| 最近记录: |