我有一个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的或查询方法,而不应访问任何其他实体对象的原始数据库操作冲突
有任何想法吗?
由于该段说,标准的不支持调用从内实体监听实体管理器的方法。 我强烈建议建立custData
从持久的实体,如海科鲁普在他回答说。 如果这是不可行的,考虑:
- 异步通知。 我真的不推荐这个 ,因为它很可能取决于时间才能正常工作:
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 {
}
}
}
- 一些注册后提交触发 (我的建议如果Heilo鲁普答案是不可行的)。 因为这是保证你已经刷新到数据库后执行这是不定时依赖。 此外,它有你,如果你最终回滚您的交易不通知的好处。 要做到这一点的方法取决于你所使用的事务管理什么,但基本上你创造一些特定实例的实例,然后在一些登记处登记。 例如,对于JTA这将是:
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的事务中,代码将是非常相似,只是一些类名称的变化。
一些指针:
ScheduledExecutorService的Javadoc中 ,用于触发异步操作。
:用JTA事务的同步交易的Javadoc和同步的Javadoc
EJB事务划分
春节当量: TransactionSynchronizationManager的Javadoc和TransactionSynchronization的Javadoc 。
而在Spring的事务中一些Spring文档
我猜你可能会看到一个NPE,因为你可能会违反你引用了一段话:
String custData = custFacade.find(customerId).toString();
该find
似乎隐含查询对象(如你描述),这可能不是完全同步到数据库中,因此尚无法使用。
在他的回答,gpeche指出,这是相当简单的,以他的选项#2转换成春。 挽救他人这样做的麻烦:
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
(在我的情况,发出查询和更新实体),没有任何问题。 一定要注释updateEndpoints()
方法与@Transaction(propagation = Propagation.REQUIRES_NEW)
以确保有一个新的事务来执行持久性操作。
不直接相关的问题,但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;
}
}