2012-10-02 2 views
2

org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean을 사용하여 JPA 방식으로 Spring/Hibernate를 사용하고 있으며 spring xml, persistence.xml 및 JPA 2 주석을 사용하여 구성했습니다.Spring EntityManager를 사용하여 대형 컬렉션을 지속시킬 때 성능을 향상시키는 방법 최대 절전 모드

기능상 정상적으로 유지됩니다. 그러나, 가능한 한 빨리 B의 큰 컬렉션을 가진 양방향 OneToMany를 가진 엔티티 A를 저장해야한다는 요구 사항이 있습니다.

나는

<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" /> 

(응용 프로그램으로 많이 읽기로 씁니다) 삽입 속도와 메모리 사용을 줄이기 위해이 persistence.xml에서 다양한 옵션을 사용하고과는 사용하여 수행됩니다 지속

entityManager.persist(instanceOfA) 

편집 추가 정보 :

:

각 엔티티는 다음과 같이 생성 된 ID를 가지고 나는 쇼 SQL로 코드를 실행하면 내가 꽤을 복용 삽입 문을 많이 볼 수에 오라클 순서

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

관련 53,691,363,210

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

.

난 5039 행마다 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/

내가 persist()에 플러시 명확 매 50 호출을 추가 (가)

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

에 지속 해체하기 위해 필요한이 의미합니까?

더 깨끗한 방법이 있습니까? (내 실제 객체 계층 구조는 같은 관계와 B의 약 7 레이어를하고있다)

내가 삽입을 위해 JDBC를 사용하는 방법에 대한 생각,하지만 난 행 매퍼 : I에 대한 org.hibernate.StatelessSession을 들었지만이있다

을 쓰기 싫어 어떤 시점에서 SessionFactory에 캐스팅하지 않고 JPA 엔티티 관리자로부터 가져 오는 방법은 없습니다. 다시는 매우 깨끗하지는 않습니다.

미리 감사드립니다.

답변

2

내 프로젝트 중 하나에서 같은 문제가 발생했습니다. identity ID 생성기로 MySQL 백엔드에서 최대 절전 모드를 사용하고있었습니다. 이 문제는 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과 관련이 있지만 오라클과 비슷한 지침이있을 수 있습니다.

그리고 "매 n 삽입 후 호출 플러시"트릭도 작동합니다. 다음은 (google-guava 클래스를 사용하는) 샘플 코드입니다.

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) 멀티 스레드 응용 프로그램으로 만듭니다. –