2016-04-06 17 views
8

Ứng dụng của tôi tải danh sách các thực thể cần được xử lý. Điều này xảy ra trong một lớp học có sử dụng một lịch trìnhTôi có nên chuyển pháp nhân được quản lý sang phương thức yêu cầu giao dịch mới không?

@Component 
class TaskScheduler { 

    @Autowired 
    private TaskRepository taskRepository; 

    @Autowired 
    private HandlingService handlingService; 

    @Scheduled(fixedRate = 15000) 
    @Transactional 
    public void triggerTransactionStatusChangeHandling() { 
     taskRepository.findByStatus(Status.OPEN).stream() 
           .forEach(handlingService::handle); 
    } 
} 

Trong HandlingService quá trình tôi từng nhiệm vụ trong issolation sử dụng REQUIRES_NEW cho mức độ tuyên truyền.

@Component 
class HandlingService { 

    @Transactional(propagation = Propagation.REQUIRES_NEW) 
    public void handle(Task task) { 
     try { 
      processTask(task); // here the actual processing would take place 
      task.setStatus(Status.PROCCESED); 
     } catch (RuntimeException e) { 
      task.setStatus(Status.ERROR); 
     } 
    } 
} 

Mã chỉ hoạt động vì tôi đã bắt đầu giao dịch gốc trên TaskScheduler lớp. Nếu tôi xóa chú thích @Transactional thì các thực thể sẽ không được quản lý nữa và bản cập nhật cho thực thể nhiệm vụ không được truyền sang db.I không thấy tự nhiên khi thực hiện giao dịch phương thức được lên lịch.

Từ những gì tôi thấy tôi có hai lựa chọn:

1. đang Giữ như ngày nay.

  • Có thể đó chỉ là tôi và đây là một sản phẩm chính xác.
  • Biến thể này có ít chuyến đi nhất đến cơ sở dữ liệu.

2. Xóa chú thích @Transactional khỏi Trình lập lịch biểu, chuyển id của tác vụ và tải lại thực thể nhiệm vụ trong HandlingService.

@Component 
class HandlingService { 

    @Autowired 
    private TaskRepository taskRepository; 

    @Transactional(propagation = Propagation.REQUIRES_NEW) 
    public void handle(Long taskId) { 
     Task task = taskRepository.findOne(taskId); 
     try { 
      processTask(task); // here the actual processing would take place 
      task.setStatus(Status.PROCCESED); 
     } catch (RuntimeException e) { 
      task.setStatus(Status.ERROR); 
     } 
    } 
} 
  • Có nhiều chuyến đi đến cơ sở dữ liệu (thêm một truy vấn/phần tử)
  • có thể được thực hiện bằng @Async

bạn có thể vui lòng cung cấp ý kiến ​​của bạn trên đó là cách chính xác giải quyết vấn đề này, có thể với một phương pháp khác mà tôi không biết?

Trả lời

9

Nếu ý định của bạn là xử lý từng tác vụ trong một giao dịch riêng, thì cách tiếp cận đầu tiên của bạn thực sự không hoạt động vì mọi thứ được cam kết ở cuối giao dịch lập lịch biểu.

Lý do cho điều đó là trong các giao dịch lồng nhau Task trường hợp là các thực thể tách rời cơ bản (Session s được bắt đầu trong các giao dịch lồng nhau không nhận thức được các trường hợp đó). Vào cuối giao dịch lập lịch biểu Hibernate thực hiện kiểm tra bẩn trên các cá thể được quản lý và đồng bộ hóa các thay đổi với cơ sở dữ liệu.

Cách tiếp cận này cũng rất nguy hiểm, vì có thể có sự cố nếu bạn cố truy cập proxy chưa được khởi tạo trên phiên bản Task trong giao dịch lồng nhau. Và có thể có vấn đề nếu bạn thay đổi đồ thị đối tượng Task trong giao dịch lồng nhau bằng cách thêm vào nó một số thực thể khác được nạp trong giao tác lồng nhau (vì trường hợp đó sẽ được tách ra khi điều khiển trả về giao dịch lập lịch biểu).

Mặt khác, cách tiếp cận thứ hai của bạn là chính xác và đơn giản và giúp tránh được tất cả những cạm bẫy trên. Chỉ, tôi sẽ đọc các id và cam kết giao dịch (không cần phải giữ cho nó bị đình chỉ trong khi các nhiệm vụ đang được xử lý).Cách dễ nhất để đạt được điều này là xóa chú thích Transactional khỏi trình lên lịch và thực hiện giao dịch phương thức lưu trữ (nếu nó chưa được giao dịch).

Nếu (và chỉ khi) hiệu suất của phương pháp thứ hai là một vấn đề, như bạn đã đề cập, bạn có thể xử lý không đồng bộ hoặc thậm chí song song việc xử lý ở một mức độ nào đó. Ngoài ra, bạn có thể muốn xem extended sessions (cuộc hội thoại), có thể bạn có thể tìm thấy nó phù hợp cho trường hợp sử dụng của bạn.

+0

Trong ví dụ này, bộ nhớ cache của thực thể phiên giao dịch lồng nhau sẽ được đồng bộ hóa với phiên giao dịch bên ngoài không? Ví dụ: nếu thực thể "Tác vụ" được thay đổi bên trong giao dịch lồng nhau, liệu thay đổi đó có áp dụng cho phiên giao dịch ngoài không? – froi

+0

Theo dõi câu hỏi của tôi, khi phiên giao dịch bên ngoài bị xóa, điều đó có nghĩa là các thay đổi Nhiệm vụ sẽ được tính là thay đổi "cũ"? – froi

1

Giả sử rằng processTask(task); là một phương pháp trong lớp HandlingService (giống như handle(task) phương pháp), sau đó loại bỏ @Transactional trong HandlingService sẽ không làm việc vì hành vi tự nhiên của proxy năng động Spring.

Trích dẫn từ spring.io forum:

Khi mùa xuân nạp TestService của bạn nó quấn nó với một proxy. Nếu bạn gọi một phương thức của TestService ngoài TestService, proxy sẽ được gọi thay vào đó và giao dịch của bạn sẽ được quản lý chính xác. Tuy nhiên, nếu bạn gọi phương thức giao dịch của mình trong một phương thức trong cùng một đối tượng, bạn sẽ không gọi proxy nhưng trực tiếp là đích của proxy và bạn sẽ không thực thi mã được bao quanh dịch vụ của mình để quản lý giao dịch.

This is one of SO thread về chủ đề này, và đây là một số bài viết về vấn đề này:

  1. http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html
  2. http://tutorials.jenkov.com/java-persistence/advanced-connection-and-transaction-demarcation-and-propagation.html
  3. http://blog.jhades.org/how-does-spring-transactional-really-work/

Nếu bạn thực sự không thích cách thêm chú thích @Transaction trong số @Scheduled của bạn đã gặp Hod, bạn có thể nhận được giao dịch từ EntityManager và quản lý giao dịch theo chương trình, ví dụ:

UserTransaction tx = entityManager.getTransaction(); 
try { 
    processTask(task); 
    task.setStatus(Status.PROCCESED); 
    tx.commit(); 
} catch (Exception e) { 
    tx.rollback(); 
} 

Nhưng tôi nghi ngờ rằng bạn sẽ mất theo cách này (tốt, tôi wont). Cuối cùng,

Bạn có thể xin vui lòng cung cấp ý kiến ​​của bạn trên đó là con đường đúng đắn về việc giải quyết loại vấn đề

Không có đúng cách trong trường hợp này. Ý kiến ​​cá nhân của tôi là, chú thích (ví dụ: @Transactional) chỉ là một dấu hiệu và bạn cần bộ xử lý chú thích (lò xo, trong trường hợp này) để thực hiện công việc @Transactional. Chú thích sẽ không có tác động gì cả nếu không có bộ xử lý của nó.

Tôi có nhiều lo lắng về việc, ví dụ, tại sao tôi có processTask(task)task.setStatus(Status.PROCESSED); ngoài sống của processTask(task) nếu nó trông giống như làm điều tương tự, vv

HTH.

+0

Ngay cả khi phương thức 'processTask' là một phương thức riêng trong' HandlingService', tất cả các cập nhật sẽ được truyền cho db, bởi vì một giao dịch mới được mở khi phương thức 'handle' được gọi (mặc dù một Spring proxy). Trong phiên bản thứ hai, tôi đã đề xuất xóa '@ Transctional' khỏi trình lên lịch và chuyển id' s đến dịch vụ xử lý (sẽ mở giao dịch cho mỗi tác vụ được xử lý) – mvlupan

2

Mã hiện tại xử lý tác vụ trong giao dịch lồng nhau, nhưng cập nhật trạng thái của tác vụ trong giao dịch bên ngoài (vì đối tượng Task được quản lý bởi giao dịch bên ngoài). Bởi vì đây là những giao dịch khác nhau, có thể là một trong những thành công trong khi khác không thành công, để lại cơ sở dữ liệu trong một trạng thái không nhất quán.Đặc biệt, với mã này, các tác vụ đã hoàn thành vẫn còn trong trạng thái mở nếu xử lý một nhiệm vụ khác ném một ngoại lệ, hoặc máy chủ được khởi động lại trước khi tất cả các tác vụ đã được xử lý.

Ví dụ của bạn cho thấy, chuyển các thực thể được quản lý sang giao dịch khác làm cho nó mơ hồ giao dịch nào nên cập nhật các thực thể này và do đó tốt nhất nên tránh. Thay vào đó, bạn nên chuyển id (hoặc các thực thể tách rời), và tránh việc lồng các giao dịch không cần thiết.

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