2010-05-05 4 views
22

다음은 간단한 예제입니다. jpa 대량 삽입에 대한 여러 항목을 읽은 후 만들어졌습니다. 2 개의 영구 객체 User 및 Site가 있습니다. 한 사용자는 많은 사이트를 가질 수 있으므로 여기에 일대일 관계가 있습니다. 사용자를 만들고 여러 사이트를 사용자 계정에 만들거나 연결하려고한다고 가정합니다. 다음은 Site 객체에 대량 삽입을 사용할 의향을 고려한 코드의 모양입니다.JPA/Hibernate 벌크 (배치) 삽입

User user = new User("John Doe"); 

user.getSites().add(new Site("google.com", user)); 
user.getSites().add(new Site("yahoo.com", user)); 

EntityTransaction tx = entityManager.getTransaction(); 
tx.begin(); 
entityManager.persist(user); 
tx.commit(); 

하지만이 코드를 실행하면 내가 SQL 출력은 다음을 참조하십시오 (내가 JPA 구현 공급자로 최대 절전 모드 사용하고 있습니다) : 그래서

Hibernate: insert into User (id, name) values (null, ?) 
Hibernate: call identity() 
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?) 
Hibernate: call identity() 
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?) 
Hibernate: call identity() 

, 나는 의미에게 "진짜"대부분은 없습니다 작품 또는 I를 삽입 혼란 스럽습니까?

여기 예제 프로젝트의 경우 source code입니다. maven 프로젝트이므로 mvn install을 다운로드하고 실행하여 출력을 확인하십시오.

업데이트 :

DEBUG : 조직

User user = new User("John Doe"); 
    user.getSites().add(new Site(1, "google.com", user)); 
    user.getSites().add(new Site(2, "yahoo.com", user)); 
    entityManager.setFlushMode(FlushModeType.COMMIT); 
    EntityTransaction tx = entityManager.getTransaction(); 
    tx.begin(); 
    entityManager.persist(user); 
    tx.commit(); 

지금 내가 디버그 출력에 다음 줄을 : 켄 리우가 친절하게 조언 한 후

, 나는 장애인 사이트 개체 ID를 자동 생성했습니다. hibernate.jdbc.AbstractBatcher - 배치 크기 실행 : 2

작동합니다!

답변

19

ID를 생성하기 위해 데이터베이스를 사용한다면, Hibernate는 쿼리를 실행하여 각 엔티티에 대한 기본 키를 생성해야한다.

+0

아,이 말이 맞습니다! 감사합니다. – abovesun

+0

질문이 업데이트되었습니다. 감사합니다. – abovesun

+5

어떻게 지금 키를 생성하고 있습니까? 키가 고유해야합니다. –

5

벌크 삽입을 위해 최대 절전 모드를 우회하는 것이 훨씬 효율적이라는 것을 발견했습니다. ORM (객체 관계형 매핑)을 제거해야하지만 현재 세션 및 트랜잭션 관리와 관련된 연결을 계속 활용할 수 있습니다.

일시적으로 ORM의 편리함을 잃어 버리는 반면, 최대 절전 모드가 일반적으로 각 INSERT에 대해 하나의 SELECT을 수행하기 때문에 원래 생성 된 ID가있는 경우에는 결과가 중요합니다.

Session.doWork은이를 쉽게 처리 할 수 ​​있습니다.

private MyParentObject saveMyParentObject(final MyParentObject parent, final List<MyChildObject> children) 
{ 
    transaction = session.beginTransaction(); 
    try 
    { 
     session.save(parent); // NOTE: parent.parentId assigned and returned here 

     session.doWork(new Work() 
     { 
      public void execute(Connection con) throws SQLException 
      { 
       // hand written insert SQL - can't use hibernate 
       PreparedStatement st = con.prepareStatement("INSERT INTO my_child (parent_id, name, ...) values (?, ?, ...)"); 

       for (MyChildObject child : children) 
       { 
        MyChildObject child = new MyChildObject(); 
        child.setParentId(parent.getParentId()); // assign parent id for foreign key 

        // hibernate can't help, determine jdbc parameters manually 
        st.setLong(1, child.getParentId()); 
        st.setString(2, child.getName()); 
        ... 
        st.addBatch(); 
       } 

       // NOTE: you may want to limit the size of the batch 
       st.executeBatch(); 
      } 
     }); 

     // if your parent has a OneToMany relationship with child(s), refresh will populate this 
     session.refresh(parent); 
     transaction.commit(); 
     return parent; 
    } 
    catch(Throwable e) 
    { 
     transaction.rollback(); 
     throw new RuntimeException(e); 
    } 
} 
+1

제공하신 것과 동일한 기술을 사용하고 있습니다. 그러나 여전히 문제가 있습니다. 이 메서드는 SQL 프로파일 러에 표시 될 수 있으므로 각 삽입에 대해 다른 문을 준비합니다. 성능을 높이려면 명령문을 컴파일하거나 준비한 다음 나머지 명령문에 대해 컴파일 된 명령문을 호출해야합니다. – Max

관련 문제