JPA 作為 EJB 3.0 核心,以簡潔配置、強大預設值橫跨輕重量級架構。但脫離 J2EE 容器管理時,惰性加載 常在視圖層引發 LazyInitializationException:Service 層事務結束導致 EntityManager 關閉,Entity 變為游離狀態,視圖渲染時無法觸發關聯集合載入。
// Action 處理器
public Page doList(WebForm form, Module module) {
NewsDirQueryObject ndqo = new NewsDirQueryObject();
form.toPo(ndqo);
IPageList pageList = service.queryDirsByConditions(ndqo); // 事務在此結束
form.addResult("dirPath", getDirPath(form));
return module.findPage("list");
}
// Velocity 模板
#foreach($info in ${dir.children}) // 此處觸發惰性加載,拋出異常
目錄名稱:${info.title}
#end
Spring 宣告式事務配置於 Service 層(propagation="SUPPORTS" read-only="true"),導致查詢方法返回即提交/關閉事務,視圖層無可用 Session。
核心思想:讓 Controller 開啟事務,Service 方法加入現有事務。以 EasyJWeb 為例,攔截核心處理器 RequestProcessor.process() 方法:
<aop:config>
<aop:pointcut id="easyjwebProcessor"
expression="execution(* com.easyjf.web.RequestProcessor.process(..))"/>
<aop:advisor advice-ref="txEasyjwebProcessorAdvice"
pointcut-ref="easyjwebProcessor"/>
</aop:config>
<tx:advice id="txEasyjwebProcessorAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<bean class="com.easyjf.web.core.DefaultRequestProcessor" name="EasyJWeb-Processor"/>
propagation="REQUIRED" 確保其融入外層事務。JOIN FETCH 或 @EntityGraph 預載關聯。事務邊界前移是治本之策,配置簡單、風險可控,是輕量級架構下解決 JPA 惰性加載的最佳實踐之一。