2012-11-14 36 views
3

我試圖觀察JPA2/Hibernate4代理下面的行爲,JPA和Hibernate的代理行爲

//圓形,延遲加載實體:

@Entity 
public class Employee { 

@[email protected] 
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 proxy所得從lazy loading。我可能在這裏錯過了一些東西,那麼怎樣才能把它做好?非常感謝一個簡單但具體的例子!

@EDIT

@kostja答案我適應的代碼,並且在下面SE模式調試它,既不能LazyInitializationException來生產,也不是boss property代理。還有什麼提示?

code window

debug window

@EDIT 2

最後,我想確認從@kostja答案是undoubtly很大。

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

//綠色光放Spring Tx到位後:

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

//輸出:

true 
Employee_$$_javassist_0 

另外,可以參考Hibernate文檔中的20.1.4. Initializing collections and proxies

回答

6

這是預期的JPA行爲。您的查詢中的實體沒有理由被代理 - 它是查詢的常規結果。但是,該實體的boss屬性應該是代理。它不會告訴我們是否被問到 - 當您對託管實體的延遲加載的屬性執行任何操作時,它將觸發提取。

所以你應該訪問交易之外的boss屬性。如果它沒有被提取,你會得到一個LazyInitializationException

你會怎樣去取決於EntityManagerPersistenceContext的種類。

  • 僅適用於自JPA 2.0起 - 請致電em.detach(loadedEmployee),然後再訪問boss屬性。

對於JPA 1:

  • 如果你是在Java EE環境中,標記與@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)方法暫停交易。

  • 在包含用戶事務的SE環境中,請在訪問boss屬性之前調用transaction.commit()

  • 如果使用延長交易時間的延長PersistenceContext,請致電em.clear()

EIDT:我想,你沒有得到異常的原因是,FetchType.LAZY僅僅是JPA提供一個提示,所以也不能保證對被延遲加載的特性。相反,FetchType.EAGER保證提前取回。我想,你的JPA提供者會選擇加載。

我已經複製了這個例子,雖然有點不同,我可重複地在日誌語句中得到LazyInitializationException。該測試是在JBoss 7.1.1上使用JPA 2.0在Hibernate 4.0.1上運行的Arquillian測試:

@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()); 
    } 
} 
+0

謝謝。你能否根據你的提示檢查我的修改版本? – sof

+0

@sof - 已經做了一些測試 - 也許你可以重現它 – kostja

+0

非常感謝。我在EE模式下成功進行了另一項測試。 – sof