2016-09-26 228 views
0

我有一個JPA實體與Lazy加載集合。我不需要收集每一次。OpenSessionInView與交易? (春季/休眠/ JPA)

@Entity(name = "Foo") 
@Access(AccessType.FIELD) 
@Table(name = "TEST", schema = "TEST") 
public class Foo implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @OneToMany(mappedBy="foo", targetEntity=Bar.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    private List<Bar> bars; 
} 

@Entity(name = "Bar") 
@Access(AccessType.FIELD) 
@Table(name = "TEST", schema = "TEST") 
public class Bar implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @ManyToOne(targetEntity = Foo.class) 
    @JoinColumn(name = "FOO_ID", referencedColumnName = "ID") 
    private Foo foo; 
} 

我有一個服務類的幾個方法,執行大量的數據庫交互,並在最後保存一個Foo實體到數據庫。我需要在集合中發生大約100個項目。

@Service 
public class FooService { 

    @Autowired 
    private FooRepository fooRepository; 

    public void processAllFoos() { 
     fooRepository.findAll().forEach(foo -> { 
      processFoo(foo); 
     }); 
    } 

    private void processFoo(Foo foo) { 
     foo.getBars().forEach(bar -> { 
      // Do a lot of time consuming stuff here that involves 
      // entities of other types and modify each bar object 
     }); 
     fooRepository.save(foo); 
    } 
} 

processAllFoos被從@RESTController一旦進入一個請求調用。

但是,我不希望processAllFoos被封裝在單個數據庫事務中,因爲這會鎖定整個Foo表,直到爲所有Foos執行業務邏輯爲止。

如果我使processFoo方法@Transactional我得到LazyInitializationException,它抱怨Hibernate會話不存在。爲了使這個工作,我需要在調用堆棧@Transactional中的所有方法,以便嵌套的方法可以加入到調用方法的事務。但是,如上所述,這將鎖定整個Foo表。

添加爲dispatcher servlet一個OpenSessionInViewFilter解決我的問題,但我讀過,有與業績和實體分離/重新連接(這是我在應用程序的其他部分一樣)使用這種方法的問題。

有沒有一種方法可以在不使用OpenSessionInView方法的情況下做我想做的事情?我使用這種方法添加了哪些其他漏洞?

Spring/Hibernate 4.x


基於下面的答案,我能夠做到以下幾點:

@Service 
public class FooService { 

    @Autowired 
    private FooRepository fooRepository; 

    @Autowired 
    private TransactionTemplate transactionTemplate; 

    public void processAllFoos() { 
     fooRepository.findAll().forEach(foo -> { 
      transactionTemplate.execute(new TransactionCallback<Object>() { 
       public Object doInTransaction(TransactionStatus status) { 
        try { 
         processFoo(foo); 
         status.flush(); 
        } catch(Exception e) { 
         status.setRollbackOnly(); 
        } 
        return null; 
       } 
      }); 
     }); 
    } 

    private void processBar(Foo foo) { 
     foo.getBars().foreEach(bar -> { 
      // Do a lot of time consuming stuff here that involves 
      // entities of other types and modify each bar object 
     }); 
     fooRepository.save(foo); 
    } 
} 

回答

2

常用解決視圖層惰性初始模式問題(UI組件或頁面模板)OpenSessionInViewFilter ,因爲View層不能直接管理事務。 在你的情況下,可以應用另一種方式來獲取所有Bar對象。

第一個你得到所有的Foo對象id來取得完全的對象。

使用Foo IDS收集遍歷通相關Bar對象。

第三如果您不想要一個BIG事務,那麼您可以使用Spring Transaction模板來顯式管理事務。

你的代碼示例可能看起來像這樣:下面

@Service 
public class FooService { 

    @Autowired 
    private FooRepository fooRepository; 

    @Autowired 
    private BarRepository barRepository; 

    @Autowired 
    private TransactionTemplate transactionTemplate; 

    public void processAllFoos() { 
     final List <Long> fooIdList = transactionTemplate.execute(new TransactionCallback() { 
      public Object doInTransaction(TransactionStatus status) { 

       return fooRepository.findIdList(); 
      } 
     }); 

     transactionTemplate.execute(new TransactionCallback() { 
      public Object doInTransaction(TransactionStatus status) { 
       barRepository.findByFooIdList(fooIdList).forEach(bar - > { 
        processBar(bar); 
       }); 
       return null; 
      } 
     }); 

    } 

    private void processBar(Bar bar) { 
     // Do a lot of time consuming stuff here that involves 
     // entities of other types and modify each bar object 
     barRepository.save(bar); 
    } 
} 

示例演示如何解決你的任務沒有一些性能方面的開銷。但是你應該明白,如果FooBar表與外鍵約束相關聯,那麼Foo表中的相關記錄可能被RDBMS阻塞,每次更新表時Bar表中的行。

+0

謝謝@SergeyBespalov這使整個過程快得令人難以置信。這種方法有什麼需要注意的嗎?你明確管理事務時不應該做的事情? – battle2048

+0

我可以建議你基於這個例子的兩條簡單規則: 1.不要使用聲明式事務管理,如果它不適合你; 2.如果只需要一個屬性,請不要使用完全對象; –