如何使用JPA生命周期事件得到实体数据(how to use JPA life-cycle even

2019-08-02 00:59发布

我有一个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的或查询方法,而不应访问任何其他实体对象的原始数据库操作冲突

有任何想法吗?

Answer 1:

由于该段说,标准的不支持调用从内实体监听实体管理器的方法。 我强烈建议建立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文档



Answer 2:

我猜你可能会看到一个NPE,因为你可能会违反你引用了一段话:

String custData = custFacade.find(customerId).toString();

find似乎隐含查询对象(如你描述),这可能不是完全同步到数据库中,因此尚无法使用。



Answer 3:

在他的回答,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;
    }
}


文章来源: how to use JPA life-cycle events to get entity data