簡體   English   中英

如何使用多個@Transactional 注解的方法?

[英]How do I use multiple @Transactional annotated methods?

我有一個 Spring-boot 項目,其中有一個帶有 2 個@Transactional注釋方法的服務 bean。

這些方法執行只讀 JPA(休眠)操作以從 HSQL 文件數據庫中獲取數據,同時使用 JPA 存儲庫和實體中的延遲加載 getter。

我還有一個處理命令的 cli bean(使用 PicoCLI)。 從這些命令之一,我嘗試調用兩個@Transactional注釋方法,但在執行第二個方法期間出現以下錯誤:

org.hibernate.LazyInitializationException - could not initialize proxy - no Session
        at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
        at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
        at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581)
        at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148)
        at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
        at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
        at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
        at <mypackage>.SomeImpl.getThings(SomeImpl.java:<linenr>)
...

如果我用@Transactional本身標記調用兩個@Transactional注釋方法的方法,代碼似乎可以工作(因為現在只有 1 個頂級事務,我假設?)。

我只是想找出為什么我不能在單個 session 中啟動多個事務,或者如果沒有,為什么第二個事務不啟動新的 session。

所以我的問題是:

  • 這是否與 hibernate 如何啟動 session、事務如何關閉會話或與 HSQL 數據庫相關的任何事情有關?
  • 添加包含事務是解決問題的正確方法,還是只是治標不治本?
  • 能夠從一個方法中使用多個@Transactional注釋方法的最佳方法是什么?

編輯:我想明確表示,我不會在事務方法之外公開實體,所以從表面上看,在我看來,這兩種事務方法應該相互獨立地工作。

EDIT2:為了更清楚:交易方法需要在 api 中可用,並且 api 的用戶應該能夠調用這些交易方法中的多個,而無需使用交易注釋,也無需獲得LazyInitializationException

Api:

public interface SomeApi {
    List<String> getSomeList();
    List<Something> getThings(String somethingGroupName);
}

執行:

public class SomeImpl implements SomeApi {

    @Transactional
    public List<String> getSomeList() {
        return ...; //Do jpa stuff to get the list
    }

    @Transactional
    public List<Something> getThings(String somethingGroupName) {
        return ...; //Do other jpa stuff to get the result from the group name
    }
}

第三方的使用(他們可能不知道什么是事務性):

public someMethod(String somethingGroupName) {
    ...

    SomeApi someApi = ...; // Get an implementation of the api in some way

    List<String> someList = someApi.someList();
    if (someList.contains(somethingGroupName) {
        System.out.println(someApi.getThings(somethingGroupName));
    }

    ...
}

交易結束后,您似乎正在從您的實體訪問一些未初始化的數據。 在那種情況下,持久性提供者可能會拋出惰性初始化異常。

如果您需要檢索一些未隨實體急切加載的信息,您可以使用以下兩種策略之一:

  • 像您所做的那樣,也使用@Transactional 注釋對調用方法進行注釋:它不會為每次調用啟動一個新事務,而是使打開的事務處於活動狀態,直到您的調用方法結束,從而避免異常; 要么
  • 使用 JOIN FETCH JPQL 習慣用法使被調用的方法急切地加載所需的字段。

事務邊界需要對您的場景進行一些分析。 請閱讀此答案並搜索更好的書籍或教程來掌握它。 可能只有您才能恰當地定義您的要求。

我發現開箱即用的 hibernate 不會重新打開 session,因此在第一個事務結束后不會啟用延遲加載,無論后續 jpa 語句是否在事務中。 然而,hibernate 中有一個屬性可以啟用此功能:

spring:
  jpa:
    properties:
      hibernate.enable_lazy_load_no_trans: true

這將確保如果沒有 session,則會創建臨時 session。 我相信它也會阻止 session 在交易后結束,但我不確定這一點。

部分功勞來自其他 StackOverflow 問題的以下答案:

警告:在 hibernate 4.1.8 中存在一個可能導致數據丟失的錯誤。 確保您使用的是 4.2、12。 hibernate 的 4.3.5 或更新版本:請參閱: https://hibernate.atlassian.net/browse/HHH-7971

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM