2012-10-28 30 views
8

Tôi có API RESTful sử dụng lớp thực thể được chú thích với @EntityListners. Và trong EntityListner.java, tôi có một phương thức được chú thích bằng @PostPersist. Vì vậy, khi sự kiện đó kích hoạt, tôi muốn trích xuất tất cả thông tin liên quan đến thực thể vừa được lưu vào cơ sở dữ liệu. Nhưng khi tôi cố gắng làm điều đó, Glassfish đang tạo ra một ngoại lệ và phương thức trong lớp EntityListner không thực hiện như mong đợi. Đây là mãcách sử dụng các sự kiện vòng đời JPA để lấy dữ liệu thực thể

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{ 

    } 
    }  
} 

Nếu tôi gửi tin nhắn nhận xét ra successMessage thay vì custData, mọi thứ đều hoạt động tốt.

http://www.objectdb.com/java/jpa/persistence/event nói những điều sau đây liên quan đến phương pháp vòng đời thực thể và tôi tự hỏi nếu đó là tình huống ở đây.

Để tránh xung đột với những hoạt động cơ sở dữ liệu ban đầu mà cháy sự kiện tổ chức vòng đời (mà vẫn đang tiến hành) các phương thức callback không nên gọi EntityManager hoặc Query phương pháp và không nên truy cập vào bất kỳ tổ chức nào khác các đối tượng

Ý tưởng nào?

+0

Ngoại lệ bạn đang nhận được là gì? –

+0

Bao gồm ngoại lệ và theo dõi ngăn xếp. – James

Trả lời

8

Như đoạn văn đó nói, tiêu chuẩn không hỗ trợ gọi phương thức quản lý thực thể từ bên trong trình nghe thực thể. Tôi mạnh mẽ khuyên bạn nên xây dựng custData từ thực thể kiên trì, như Heiko Rupp nói trong câu trả lời của mình. Nếu điều đó không khả thi, hãy xem xét:

  • thông báo không đồng bộ. tôi không thực sự khuyên này vì nó có thể phụ thuộc vào thời gian làm việc đúng cách:
 
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 { 
     } 
    }  
} 
  • đăng ký một số bài-cam cò (khuyến nghị của tôi nếu Heilo Rupp Câu trả lời là không khả thi). Đây không phải là thời gian phụ thuộc bởi vì nó được đảm bảo để thực thi sau khi bạn đã đỏ mặt vào cơ sở dữ liệu. Hơn nữa, nó có thêm lợi ích mà bạn không thông báo nếu bạn kết thúc quay trở lại giao dịch của bạn. Cách để làm điều này phụ thuộc vào những gì bạn đang sử dụng để quản lý giao dịch, nhưng về cơ bản bạn tạo một cá thể của một số cá thể cụ thể và sau đó đăng ký nó trong một số đăng ký. Ví dụ, với JTA nó sẽ là:
 
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 { 
     } 
    }  
} 

Nếu bạn đang sử dụng các giao dịch mùa xuân, các mã sẽ rất tương tự, chỉ với một số thay đổi tên lớp.

Một số gợi ý:

3

Tôi đoán bạn có thể được nhìn thấy một NPE, như bạn có thể vi phạm các đoạn bạn đã trích dẫn:

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

Các find dường như ngầm truy vấn cho các đối tượng (như bạn mô tả), trong đó có thể không hoàn toàn đồng bộ hóa với cơ sở dữ liệu và do đó chưa truy cập được.

+0

Cảm ơn @Heiko; yea, tôi nhận được NPE. Tôi đã thêm Thread.sleep (2000) vào mã của mình để cung cấp cho cơ sở dữ liệu đủ thời gian để đồng bộ hóa nhưng không có kết quả. Vì vậy, có một công việc xung quanh? – Abraham

+2

Bạn không thể sử dụng đối tượng khách hàng đã được chuyển vào? –

+0

Tôi muốn đọc lại biểu diễn Json của đối số mà Jersey đã sử dụng để tạo ra nó. Nếu tôi sử dụng các đối số mà tôi gửi như là, tôi chỉ nhận được ID của nó. – Abraham

1

Trong câu trả lời của mình, gpeche lưu ý rằng nó là khá đơn giản để dịch lựa chọn của mình # 2 vào mùa xuân. Để tiết kiệm khác những rắc rối của việc đó:

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); 
    } 
} 

Phương pháp dịch vụ (ở đây, updateEndpoints()) có thể sử dụng JPA EntityManager (trong trường hợp của tôi, ban hành các truy vấn và cập nhật các đối tượng) mà không cần bất kỳ vấn đề. Đảm bảo chú thích phương thức updateEndpoints() với @Transaction(propagation = Propagation.REQUIRES_NEW) để đảm bảo rằng có giao dịch mới để thực hiện các hoạt động liên tục.

Không liên quan trực tiếp đến câu hỏi, nhưng ApplicationContextProvider chỉ là lớp tùy chỉnh để trả về ngữ cảnh ứng dụng vì trình nghe thực thể JPA 2.0 không phải là thành phần được quản lý và tôi quá lười để sử dụng @Configurable tại đây. Dưới đây là để hoàn thành:

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; 
    } 
} 
Các vấn đề liên quan