2011-03-18 36 views
10

Tôi có một ứng dụng springframework trong đó tôi muốn thêm một người nghe giao dịch vào một giao dịch hiện đang được tiến hành. Động lực là kích hoạt một hành động cam kết đăng bài thông báo cho các hệ thống hạ lưu. Tôi đang sử dụng @Transactional để gói giao dịch xung quanh một số phương thức dịch vụ - đó là nơi tôi muốn tạo/đăng ký trình xử lý giao dịch bài đăng. Tôi muốn làm một cái gì đó "như" sau đây.tự động đăng ký người nghe giao dịch với mùa xuân?

public class MyService { 
@Transaction 
public void doIt() { 
    modifyObjects(); 

    // something like this 
    getTransactionManager().registerPostCommitAction(new 
    TransactionSynchronizationAdapter() { 
    public void afterCommit() { 
     notifyDownstream(); 
    } 
    }); 
} 
} 

Mùa xuân có giao diện TransactionSynchronization và lớp bộ điều hợp có vẻ chính xác những gì tôi muốn; tuy nhiên, không rõ ngay cách đăng ký một cách động với giao dịch hiện tại hoặc người quản lý giao dịch. Tôi không muốn phân lớp JtaTransactionManager nếu tôi có thể tránh nó.

Q: Có ai đã làm điều này trước đây.

Q: cách đơn giản nhất để đăng ký bộ điều hợp của tôi là gì?

Trả lời

3

bạn có thể sử dụng một khía cạnh aspect to match phương pháp giao dịch trong dịch vụ của bạn để thực hiện điều này:

@Aspect 
public class AfterReturningExample { 

    @AfterReturning("execution(* com.mypackage.MyService.*(..))") 
    public void afterReturning() { 
    // ... 
    } 

} 
+0

Một đề xuất tốt, tuy nhiên, điều này sẽ không thực thi trong bất kỳ giao dịch nào? Tôi chỉ muốn hoàn thành thành công. Ngoài ra, tôi có thể sử dụng những chú thích AOP với mùa xuân mặc định (tôi không sử dụng mã hóa dệt) chỉ JDK proxy. – Justin

+0

Lời khuyên trả về không chạy khi phương thức ném ngoại lệ, vì vậy điều này phụ thuộc vào @Transactional cam kết khi phương thức trả về kết quả và quay lại khi ném ngoại lệ, thường là điều xảy ra. Các khía cạnh được gọi thông qua các proxy động của lò xo nên nó không thực hiện bất kỳ mã nào dệt (trừ khi bạn không cung cấp giao diện cho mùa xuân đến proxy, nơi mùa xuân sẽ quay trở lại để dệt mã trong thời gian chạy với cglib). – krock

+1

+1 cho thú vị, nhưng tôi vẫn không thuyết phục mà không thử nghiệm. Có ít nhất 2 @AfterReturning tham gia: @Transactional và sau đó là Aspect tùy chỉnh của tôi. Nếu @Transactional chạy, sau đó tôi chạy tất cả là tốt; tuy nhiên theo thứ tự khác, tôi sẽ thực hiện trước - độc lập với trạng thái cam kết giao dịch. – Justin

26

Trên thực tế nó là không khó như tôi nghĩ; mùa xuân có một lớp trợ giúp tĩnh đặt các công cụ 'đúng' vào ngữ cảnh luồng.

TransactionSynchronizationManager.registerSynchronization(
    new TransactionSynchronizationAdapter() { 
     @Override 
     public void afterCommit() { 
      s_logger.info("TRANSACTION COMPLETE!!!"); 
     } 
    } 
); 
+0

+1 để có câu trả lời hay và dễ hiểu. Tôi không biết rằng TransactionSynchronizationManager đã tồn tại. – krock

+0

Trên thực tế giải pháp này không tốt như tôi đã có mặc dù. Hợp đồng cho TransactionSynchronizationManager rộng hơn và cố gắng gửi một tin nhắn JMS từ bên trong afterCommit() không thành công (không có ngoại lệ) vì mã trên không tuân theo hợp đồng. – Justin

+0

Hầu hết các giải pháp nhắn tin mùa xuân (JMS, AMQP) cho phép bạn cắm trình quản lý giao dịch của bạn vào chúng để giao dịch giữa hai hệ thống (db, môi giới thư) được đồng bộ hóa. Tôi không muốn làm điều này vì RabbitMQ TX rất chậm. Do đó bạn có thể muốn xem xét giải pháp của tôi. –

2

Dưới đây là giải pháp hoàn chỉnh hơn mà tôi đã thực hiện cho một vấn đề tương tự với việc gửi thư của tôi sau khi giao dịch được thực hiện (tôi có thể đã sử dụng RabbitMQ TX nhưng chúng khá chậm).

public class MessageBusUtils { 
    public static Optional<MessageBusResourceHolder> getTransactionalResourceHolder(TxMessageBus messageBus) { 

     if (! TransactionSynchronizationManager.isActualTransactionActive()) { 
      return Optional.absent(); 
     } 

     MessageBusResourceHolder o = (MessageBusResourceHolder) TransactionSynchronizationManager.getResource(messageBus); 
     if (o != null) return Optional.of(o); 

     o = new MessageBusResourceHolder(); 
     TransactionSynchronizationManager.bindResource(messageBus, o); 
     o.setSynchronizedWithTransaction(true); 
     if (TransactionSynchronizationManager.isSynchronizationActive()) { 
      TransactionSynchronizationManager.registerSynchronization(new MessageBusResourceSynchronization(o, messageBus)); 
     } 
     return Optional.of(o); 

    } 

    private static class MessageBusResourceSynchronization extends ResourceHolderSynchronization<MessageBusResourceHolder, TxMessageBus> { 
     private final TxMessageBus messageBus; 
     private final MessageBusResourceHolder holder; 

     public MessageBusResourceSynchronization(MessageBusResourceHolder resourceHolder, TxMessageBus resourceKey) { 
      super(resourceHolder, resourceKey); 
      this.messageBus = resourceKey; 
      this.holder = resourceHolder; 
     } 


     @Override 
     protected void cleanupResource(MessageBusResourceHolder resourceHolder, TxMessageBus resourceKey, 
       boolean committed) { 
      resourceHolder.getPendingMessages().clear(); 
     } 

     @Override 
     public void afterCompletion(int status) { 
      if (status == TransactionSynchronization.STATUS_COMMITTED) { 
       for (Object o : holder.getPendingMessages()) { 
        messageBus.post(o, false); 
       } 
      } 
      else { 
       holder.getPendingMessages().clear(); 
      } 
      super.afterCompletion(status); 
     } 


    } 
} 

public class MessageBusResourceHolder extends ResourceHolderSupport { 

    private List<Object> pendingMessages = Lists.newArrayList(); 

    public void addMessage(Object message) { 
     pendingMessages.add(message); 
    } 


    protected List<Object> getPendingMessages() { 
     return pendingMessages; 
    } 

} 

Bây giờ trong lớp học của bạn, nơi bạn có thực sự gửi tin nhắn bạn sẽ làm

@Override 
public void postAfterCommit(Object o) { 
    Optional<MessageBusResourceHolder> holder = MessageBusTxUtils.getTransactionalResourceHolder(this); 
    if (holder.isPresent()) { 
     holder.get().addMessage(o); 
    } 
    else { 
     post(o, false); 
    } 
} 

Xin lỗi cho các mẫu mã hơi dài nhưng hy vọng rằng sẽ hiển thị một người nào đó làm thế nào để làm điều gì đó sau một cam kết.

+0

Tôi đang sử dụng mã của bạn để cập nhật chỉ số 3 solr sau khi giao dịch được thực hiện. Tôi cũng đang sử dụng sự kiện Spring để phát hiện sửa đổi đối tượng nghiệp vụ, lưu trữ sự kiện trong ResourceHolder và cập nhật chỉ mục cần cập nhật không đồng bộ. –

0

Có ý nghĩa khi ghi đè trình quản lý giao dịch lên các phương thức cam kết và khôi phục, gọi số super.commit() ngay lúc đầu.

Các vấn đề liên quan