如何使用JPA生命周期事件来获取实体数据 [英] how to use JPA life-cycle events to get entity data

查看:154
本文介绍了如何使用JPA生命周期事件来获取实体数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个RESTful API,它使用了一个用@EntityListners注释的实体类。在EntityListner.java中,我有一个用@PostPersist注释的方法。因此,当该事件触发时,我想要提取有关刚刚保存到数据库的实体的所有信息。但是,当我尝试这样做时,Glassfish正在生成异常,并且EntityListner类中的方法未按预期执行。这是代码

I have a RESTful API that makes use of an entity class annotated with @EntityListners. And in the EntityListner.java, I have a method annotated with @PostPersist. So, when that event fires, I want to extract all the information regarding the entity that just got persisted to the database. But when I try to do that, Glassfish is generating an exception and the method in EntityListner class is not executing as expected. Here is the code

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 ,一切正常。

If I send the commented out successMessage message instead of custData, everything works fine.

http://www.objectdb.com/java/jpa/persistence/event 关于实体生命周期方法的说明如下,我想知道这是否是这里的情况。

http://www.objectdb.com/java/jpa/persistence/event says the following regarding the entity lifecycle methods, and I am wondering if that is the situation here.


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

To avoid conflicts with the original database operation that fires the entity lifecycle event (which is still in progress) callback methods should not call EntityMan­ager or Query methods and should not access any other entity objects

任何想法?

推荐答案

正如该段所述,该标准不支持呼叫实体管理来自实体侦听器的方法。我强烈建议从持久化实体构建 custData ,正如Heiko Rupp在他的回答中所说。如果这不可行,请考虑:

As that paragraph says, the standard does not support calling entity manager methods from inside entity listeners. I strongly recommend building custData from the persisted entity, as Heiko Rupp says in his answer. If that is not feasible, consider:


  • 异步通知。 我不推荐这个,因为它可能取决于正常工作的时间:

  • notifying asynchronously. I do not really recommend this as it probably depends on timing to work properly:

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,它将是:

    • registering some post-commit trigger (my recommendation if Heilo Rupp answer is not feasible). This is not timing dependant because it is guaranteed to execute after you have flushed to database. Furthermore, it has the added benefit that you don't notify if you end up rolling back your transaction. The way to do this depends on what you are using for transaction management, but basically you create an instance of some particular instance and then register it in some registry. For example, with JTA it would be:
    • 
      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交易,代码将非常相似,只会更改一些类名。

      If you are using Spring transactions, the code will be very similar, with just some class name changes.

      指针:

      • ScheduledExecutorService Javadoc, for triggering asynchronous actions.

      与JTA的事务同步:事务Javadoc 同步Javadoc

      transaction synchronization with JTA: Transaction Javadoc and Synchronization Javadoc

      EJB事务划分

      Sp环当量: TransactionSynchronizationManager的Javadoc TransactionSynchronization的Javadoc

      关于Spring交易的一些Spring文档

      这篇关于如何使用JPA生命周期事件来获取实体数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆