如何使用JPA生命周期事件来获取实体数据

Abr*_*ham 8 java jpa eclipselink entitylisteners glassfish-3

我有一个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{

    }
  }    
} 
Run Code Online (Sandbox Code Playgroud)

如果我发送注释掉的successMessage消息而不是custData,一切正常.

http://www.objectdb.com/java/jpa/persistence/event关于实体生命周期方法说了以下内容,我想知道这是否是这种情况.

为了避免与触发实体生命周期事件(仍在进行中)的原始数据库操作发生冲突,回调方法不应该调用EntityManager或Query方法,也不应该访问任何其他实体对象

有任何想法吗?

gpe*_*che 9

正如该段所述,该标准不支持从实体侦听器内部调用实体管理器方法.正如Heiko Rupp在他的回答中所说,我强烈建议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 Rupp回答不可行,我的建议).这不依赖于时序,因为它保证在刷新到数据库后执行.此外,它还有一个额外的好处,即如果最终回滚您的交易,您不会通知.执行此操作的方式取决于您用于事务管理的内容,但基本上您创建某个特定实例的实例,然后在某个注册表中注册它.例如,使用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事务,那么代码将非常相似,只需更改一些类名.

一些指示: