2012-10-02 35 views
2

我正在使用Spring/Hibernate使用org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean完成JPA方式,並使用spring xml,persistence.xml和JPA 2註釋進行配置。如何在使用Spring EntityManager持久化大集合時提高性能休眠

在功能上它很好,並保持正確。但是,我有一個要求,即儘可能快地存儲具有大量B集合的雙向OneToMany的實體A.

我使用的各種選項persistence.xml中要儘量加快插入和減少內存使用(應用程序寫的一樣,因爲它讀取)

<property name="hibernate.id.new_generator_mappings" value="true" /> 
<property name="hibernate.jdbc.batch_size" value="50" /> 
<property name="hibernate.order_inserts" value="true" /> 
<property name="hibernate.order_updates" value="true" /> 
<property name="hibernate.cache.use_query_cache" value="false" /> 
<property name="hibernate.cache.use_second_level_cache" value="false" /> 

和堅持用做

entityManager.persist(instanceOfA) 

編輯附加信息:

每個實體都有一個生成的ID是這樣的:

@Id 
    @Column(name="ID") 
    @GeneratedValue(strategy=GenerationType.AUTO, generator="SEQUENCE_GENERATOR") 
    @SequenceGenerator(name="SEQUENCE_GENERATOR", sequenceName="MY_SEQUENCE", allocationSize=50) 
    private Long id; 

當我運行顯示SQL代碼它涉及到Oracle序列

CREATE SEQUENCE MY_SEQUENCE MINVALUE 1 MAXVALUE 999999999999999999999999999 START WITH 1 INCREMENT BY 50 NOCYCLE NOCACHE NOORDER; 

上我可以看到很多以相當長的一段插入語句的。

我已經讀過,我需要每插入50行就撥打entityManager.flush(); entityManager.clear();

http://abramsm.wordpress.com/2008/04/23/hibernate-batch-processing-why-you-may-not-be-using-it-even-if-you-think-you-are/

這是否意味着我需要,打破了持續到

entityManager.persist(instanceOfA); 
instanceOfA.addB(instanceOfB); 
entityManager.persist(instanceofB); 

加入沖洗每50個呼叫清楚persist()

有沒有更乾淨的方法呢? (我的實際對象層次有大約7層如A關係和B)

我想使用JDBC的插入,但我討厭寫作的行映射器:)

我聽說過org.hibernate.StatelessSession但沒有任何方法可以從JPA實體管理器中獲取,而不需要在某個時刻投射到SessionFactory--再次不是很乾淨。

在此先感謝!

回答

2

我在我的一個項目中遇到了同樣的問題。我使用Hibernate與MySQL後端和一個identity ID生成器。與此相關的問題是,Hibernate需要爲保存的每個實體訪問數據庫一次,以便實際獲得該實體的ID。我切換到increment發電機,並看到了立竿見影的好處(所有插入物都被批量處理)。

@Id 
@GeneratedValue(generator = "increment") 
@GenericGenerator(name = "increment", strategy = "increment") 
@Column(name = "id", nullable = false) 
private long id; 

increment發生器內存生成的ID,也不需要打數據庫。我猜測sequence生成器也需要按照數據庫中定義的那樣命中數據庫。使用increment的概念是,Hibernate應該具有對數據庫的獨佔插入訪問權限,並且在集羣設置中可能會失敗。

我使用的另一個技巧是將rewriteBatchedStatements=true附加到JDBC URL。這是MySQL特有的,但我認爲可能有類似的Oracle指令。

而且「每插入一次後都會調用flush」技巧。下面是一個示例代碼(使用谷歌番石榴類):

public List<T> saveInBatches(final Iterable<? extends T> entities, final int batchSize) { 
    return ImmutableList.copyOf(
     Iterables.concat(
      Iterables.transform(
       Iterables.partition(entities, batchSize), 
       new Function<List<? extends T>, Iterable<? extends T>>() { 
        @Override 
        public Iterable<? extends T> apply(final List<? extends T> input) { 
         List<T> saved = save(input); flush(); return saved; 
        }}))); 
} 

public List<T> save(Iterable<? extends T> entities) { 
    List<T> result = new ArrayList<T>(); 
    for (T entity : entities) { 
     entityManager.persist(entity); 
     result.add(entity); 
    } 
    return result; 
} 
2

使用純JDBC進行批量/大型插入。不要使用任何ORM框架。

+1

- 爲每個表插入一次PreparedStatement並重復使用它們。 2)使它成爲多線程的應用程序。 –