[英]how to use JPA life-cycle events to get entity data
我有一個RESTful API,它使用了一個用@EntityListners注釋的實體類。 在EntityListner.java中,我有一個用@PostPersist注釋的方法。 因此,當該事件觸發時,我想提取有關剛剛保存到數據庫的實體的所有信息。 但是,當我嘗試這樣做時,Glassfish正在生成異常,並且EntityListner類中的方法未按預期執行。 這是代碼
public class EntityListner {
private final static String QUEUE_NAME = "customer";
@PostUpdate
@PostPersist
public void notifyOther(Customer entity){
CustomerFacadeREST custFacade = new CustomerFacadeREST();
Integer customerId = entity.getCustomerId();
String custData = custFacade.find(customerId).toString();
String successMessage = "Entity added to server";
try{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// channel.basicPublish("", QUEUE_NAME, null, successMessage .getBytes());
channel.basicPublish("", QUEUE_NAME, null, custData.getBytes());
channel.close();
connection.close();
}
catch(IOException ex){
}
finally{
}
}
}
如果我發送注釋掉的successMessage消息而不是custData ,一切正常。
http://www.objectdb.com/java/jpa/persistence/event關於實體生命周期方法說了以下內容,我想知道這是否是這種情況。
為避免與觸發實體生命周期事件(仍在進行中)的原始數據庫操作發生沖突,回調方法不應調用EntityManager或Query方法,也不應訪問任何其他實體對象
有任何想法嗎?
正如該段所述,該標准不支持從實體偵聽器內部調用實體管理器方法。 我強烈建議從持久化實體構建custData
,正如Heiko Rupp在他的回答中所說的那樣。 如果這不可行,請考慮:
public class EntityListener { private final static String QUEUE_NAME = "customer"; private ScheduledExecutorService getExecutorService() { // get asynchronous executor service from somewhere // you will most likely need a ScheduledExecutorService // instance, in order to schedule notification with // some delay. Alternatively, you could try Thread.sleep(...) // before notifying, but that is ugly. } private void doNotifyOtherInNewTransaction(Customer entity) { // For all this to work correctly, // you should execute your notification // inside a new transaction. You might // find it easier to do this declaratively // by invoking some method demarcated // with REQUIRES_NEW try { // (begin transaction) doNotifyOther(entity); // (commit transaction) } catch (Exception ex) { // (rollback transaction) } } @PostUpdate @PostPersist public void notifyOther(final Customer entity) { ScheduledExecutorService executor = getExecutorService(); // This is the "raw" version // Most probably you will need to call // executor.schedule and specify a delay, // in order to give the old transaction some time // to flush and commit executor.execute(new Runnable() { @Override public void run() { doNotifyOtherInNewTransaction(entity); } }); } // This is exactly as your original code public void doNotifyOther(Customer entity) { CustomerFacadeREST custFacade = new CustomerFacadeREST(); Integer customerId = entity.getCustomerId(); String custData = custFacade.find(customerId).toString(); String successMessage = "Entity added to server"; try { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicPublish("", QUEUE_NAME, null, custData.getBytes()); channel.close(); connection.close(); } catch(IOException ex){ } finally { } } }
public class EntityListener { private final static String QUEUE_NAME = "customer"; private Transaction getTransaction() { // get current JTA transaction reference from somewhere } private void doNotifyOtherInNewTransaction(Customer entity) { // For all this to work correctly, // you should execute your notification // inside a new transaction. You might // find it easier to do this declaratively // by invoking some method demarcated // with REQUIRES_NEW try { // (begin transaction) doNotifyOther(entity); // (commit transaction) } catch (Exception ex) { // (rollback transaction) } } @PostUpdate @PostPersist public void notifyOther(final Customer entity) { Transaction transaction = getTransaction(); transaction.registerSynchronization(new Synchronization() { @Override public void beforeCompletion() { } @Override public void afterCompletion(int status) { if (status == Status.STATUS_COMMITTED) { doNotifyOtherInNewTransaction(entity); } } }); } // This is exactly as your original code public void doNotifyOther(Customer entity) { CustomerFacadeREST custFacade = new CustomerFacadeREST(); Integer customerId = entity.getCustomerId(); String custData = custFacade.find(customerId).toString(); String successMessage = "Entity added to server"; try { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicPublish("", QUEUE_NAME, null, custData.getBytes()); channel.close(); connection.close(); } catch(IOException ex){ } finally { } } }
如果您使用的是Spring事務,那么代碼將非常相似,只需更改一些類名。
一些指示:
我想你可能會看到一個NPE,因為你可能違反了你引用的段落:
String custData = custFacade.find(customerId).toString();
find
似乎隱式查詢對象(如您所述),該對象可能未完全同步到數據庫,因此無法訪問。
在他的回答中,gpeche指出,將他的選項#2翻譯成Spring是相當簡單的。 為了節省其他人這樣做的麻煩:
package myapp.entity.listener;
import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import myapp.util.ApplicationContextProvider;
import myapp.entity.NetScalerServer;
import myapp.service.LoadBalancerService;
public class NetScalerServerListener {
@PostPersist
@PostUpdate
public void postSave(final NetScalerServer server) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() { postSaveInNewTransaction(server); }
});
}
private void postSaveInNewTransaction(NetScalerServer server) {
ApplicationContext appContext =
ApplicationContextProvider.getApplicationContext();
LoadBalancer lbService = appContext.getBean(LoadBalancerService.class);
lbService.updateEndpoints(server);
}
}
服務方法(此處, updateEndpoints()
)可以使用JPA EntityManager
(在我的情況下,發出查詢和更新實體)而沒有任何問題。 請務必使用@Transaction(propagation = Propagation.REQUIRES_NEW)
注釋updateEndpoints()
方法,以確保有一個新事務來執行持久性操作。
與問題沒有直接關系,但ApplicationContextProvider
只是一個返回應用程序上下文的自定義類,因為JPA 2.0實體監聽器不是托管組件,而且我懶得在這里使用@Configurable
。 這是為了完整性:
package myapp.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
applicationContext = appContext;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.