2016-11-01 3 views
0

Hibernate (Hibernate)에서 Hibernate (Hibernate) 또는 Hibernate (JPA) + HikariCP 연결 풀로 마이그레이션 중이며 일괄 처리에 문제가 있습니다. 끼워 넣다. 큰 목록을 1000에서 527 000 개의 요소로 저장해야합니다.

의존성없이 하나의 엔티티 만 배치하면 (예 : 5000 요소 Category 클래스) 모든 것이 정상입니다. 내가 (그냥에 대한 Category 설정과 Website를) 아동 및 부모 개체 모두를 저장하려고하면
그러나, 나는 예외를 얻을 : 목록의 크기가 적은 다음hibernate.jdbc.batch_sizeorg.hibernate.PersistentObjectException: detached entity passed to persist: com.example.entities.Category

그러나 을, 모든 것이 괜찮습니다 너무 !!! 두 개체는 모두 부모와 자식으로 올바르게 저장됩니다. 그것은 정말로 이상하게 보인다!
나는 충분히 봤 거든, StackOverflow에서 검색하지만, 특히 내 문제에 대한 해결책을 찾을 수 없습니다. 연결 풀을 Apache DBCP로 대체하려고 시도했거나 전혀 포함하지 않은 경우), 최대 절전 모드 성능 설정이 변경되어 PostgreSQL db가 H2로 바뀌 었습니다. 그러나 결과는 없습니다.

어디에서 오류가있을 수 있습니까?
JPA에서 내가 원하는 것을 할 수 있습니까?
아니면 엔티티를 별도로 저장해야합니까 (첫째, 부모와 자식)? 또 다른 방법은 내 프로그램에서 flush()clear()을 제외하는 것입니다.하지만 성능이 좋지 않습니다.
도움을 받으시기 바랍니다. 여기에 세부 사항이 있습니다. 그건 내가 엔티티를 저장하기 위해 노력하고있어 방법은 다음과 같습니다
JPA : 동시에 하위 및 상위 엔티티를 저장합니다.

@Repository("experimentDao") 
    @Transactional 
    public class Dao { 
     @PersistenceContext 
     private EntityManager entityManager; 

     @Value("${spring.jpa.properties.hibernate.jdbc.batch_size}") 
     private int batchSize; 

     public <T extends Entity> void bulkSave(Collection<T> entities) { 
      int i = 0; 
      for (Entity entity : entities) { 
       persistOrMerge(entity); 
       i++; 
       if (i % batchSize == 0) { 
        // Flush a batch of inserts and release memory. 
        entityManager.flush(); 
        entityManager.clear(); 
       } 
      } 
     } 

    private <T extends Entity> void persistOrMerge(T entity) { 
     if (entity.getId() == null) { 
      entityManager.persist(entity); 
     } else { 
      entityManager.merge(entity); 
     } 
    } 
} 


(예를에서 발견되었다 : http://frightanic.com/software-development/jpa-batch-inserts/). 내가 스택 트레이스 다음 을 받고 있어요 :

java.lang.IllegalStateException: Failed to execute CommandLineRunner 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:803) [spring-boot-1.4.1.RELEASE.jar:1.4.1.RELEASE] 
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:784) [spring-boot-1.4.1.RELEASE.jar:1.4.1.RELEASE] 
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:771) [spring-boot-1.4.1.RELEASE.jar:1.4.1.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-1.4.1.RELEASE.jar:1.4.1.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1186) [spring-boot-1.4.1.RELEASE.jar:1.4.1.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1175) [spring-boot-1.4.1.RELEASE.jar:1.4.1.RELEASE] 
    at com.example.DemoApplication.main(DemoApplication.java:24) [classes/:na] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_101] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_101] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_101] 
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_101] 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) [idea_rt.jar:na] 
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.example.entities.Category; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.entities.Category 
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:299) ~[spring-orm-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244) ~[spring-orm-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:491) ~[spring-orm-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at com.example.Dao$$EnhancerBySpringCGLIB$$79470367.bulkSave(<generated>) ~[classes/:na] 
    at com.example.DemoApplication.insertWebsites(DemoApplication.java:44) [classes/:na] 
    at com.example.DemoApplication.run(DemoApplication.java:30) [classes/:na] 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spring-boot-1.4.1.RELEASE.jar:1.4.1.RELEASE] 
    ... 11 common frames omitted 
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.entities.Category 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:765) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:758) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:398) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:111) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:425) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:249) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:178) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final] 
    at sun.reflect.GeneratedMethodAccessor11.invoke(Unknown Source) ~[na:na] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_101] 
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_101] 
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298) ~[spring-orm-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at com.sun.proxy.$Proxy62.persist(Unknown Source) ~[na:na] 
    at com.example.Dao.persistOrMerge(Dao.java:36) ~[classes/:na] 
    at com.example.Dao.bulkSave(Dao.java:24) ~[classes/:na] 
    at com.example.Dao$$FastClassBySpringCGLIB$$2ada4001.invoke(<generated>) ~[classes/:na] 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE] 
    ... 21 common frames omitted 


WebsiteCategory 기관은 일대일 관계가, Category 부모입니다.

public abstract class Entity { 
    public abstract Long getId(); 
} 

@javax.persistence.Entity 
@Table(name = "websites") 
public class Website extends Entity implements Serializable { 
    @Id 
    @GeneratedValue(strategy = SEQUENCE) 
    @Column(name = "website_id") 
    private Long id; 

    @Column(name = "url", unique = true) 
    private String url; 

    @OneToOne(cascade = CascadeType.ALL) 
    @JoinColumn(name="category_id") 
    private Category category; 
    // getters and setters, toString 
} 

@javax.persistence.Entity 
@Table(name = "categories") 
public class Category extends Entity implements Serializable { 
    @Id 
    @GeneratedValue(strategy = SEQUENCE) 
    @Column(name = "category_id") 
    private Long id; 

    @Column(name = "category_name", unique = true) 
    private String categoryName; 
    // getters, setters, toString 
} 

는 지금은 같은 Category에 속하는 5000 개 Website의 저장 싶어요 : 그리고 지속 할 shouln't하는뿐만 아니라, 추상 슈퍼 클래스가 있습니다.

@SpringBootApplication 
public class DemoApplication implements CommandLineRunner { 

    private Dao dao; 

    @Autowired 
    public void setDao(Dao dao) { 
     this.dao = dao; 
    } 

    public static void main(String[] args) { 
     SpringApplication.run(DemoApplication.class, args); 
    } 

    @Override 
    public void run(String... strings) throws Exception { 
     insertWebsites(); 
    } 

    private void insertWebsites() { 
     List<Website> entities = new ArrayList<>(5000); 
     Category category = new Category(); 
     category.setCategoryName("my category"); 

     for (int i = 0; i < 5000; i++) { 
      Website website = new Website(); 
      website.setUrl("http://example.com" + i); 
      website.setCategory(category); 
      entities.add(website); 
     } 
     dao.bulkSave(entities); 
    } 
} 

설정 및 매개 변수 : 여기가 대량 삽입 할 방법 자세한 것에 대해
(application.properties)

spring.datasource.driver-class-name = org.postgresql.Driver 
spring.datasource.url = jdbc:postgresql://localhost:5432/spring_web 
spring.datasource.username = postgres 
spring.datasource.password = postgresql 
spring.datasource.type = com.zaxxer.hikari.HikariDataSource 

# Hibernate 
spring.jpa.hibernate.ddl-auto = update 
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQL94Dialect 
spring.jpa.properties.hibernate.max_fetch_depth = 3 
spring.jpa.properties.hibernate.jdbc.fetch_size = 30 
spring.jpa.properties.hibernate.jdbc.batch_size = 30 
spring.jpa.properties.hibernate.order_inserts = true 
spring.jpa.properties.hibernate.order_updates = true 
spring.jpa.properties.hibernate.cache.use_second_level_cache = false 

(pom.xml 파일)

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>com.example</groupId> 
    <artifactId>demo</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 
    <packaging>jar</packaging> 

    <name>demo</name> 
    <description>Demo project for Spring Boot</description> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <version>1.4.1.RELEASE</version> 
     <relativePath/> <!-- lookup parent from repository --> 
    </parent> 

    <properties> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 
     <java.version>1.8</java.version> 
    </properties> 

    <dependencies> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-jpa</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-jdbc</artifactId> 
     </dependency> 

     <dependency> 
      <groupId>org.postgresql</groupId> 
      <artifactId>postgresql</artifactId> 
      <!--<scope>runtime</scope>--> 
     </dependency> 

     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-test</artifactId> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>com.zaxxer</groupId> 
      <artifactId>HikariCP</artifactId> 
      <version>2.5.1</version> 
     </dependency> 

    </dependencies> 

    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-maven-plugin</artifactId> 
      </plugin> 
     </plugins> 
    </build> 
</project> 

미안, I 모든 세부 사항을 주려고했다. 도움이 필요하십니까?

답변

0

당신이 실행에 따라 일단 문제는 분명하게 될 것이다 : 하나의 카테고리가

  • batch_size 웹 사이트가 만들어지고 유지됩니다 작성되는

    • 을; 그들은 위에서 만든 하나의 카테고리에 대한 모든 점
      • 즉시 최초의 웹 사이트가, 카테고리 때문에
    • 모든 batch_size 웹 사이트 및 카테고리 이후 지속되는 관계에있는 폭포의도 유지됩니다 유지됩니다으로 , 그들은 영속성 문맥 안에 산다. 그러나 entityManager.clear()으로 전화하자마자 영속성 컨텍스트에서 벗어나 버려 메모리를 비 웁니다. 따라서 단일 카테고리가 분리되어 (즉, 지속성 컨텍스트에서 제거됨) 예외가 발생합니다. 언급 한 사이트의 예는 관계를 통해 지속되지 않으므로 작동합니다.

    저는 bulkSave()과 같은 일반적인 방식으로 작업 할 수 있다고 생각하지 않습니다. 웹 사이트를 대량 삽입하려면 다음 행을 따라 무언가를하십시오.

    @Value("${spring.jpa.properties.hibernate.jdbc.batch_size}") 
    private int batchSize; 
    
    @PersistenceContext 
    private EntityManager em; // you will need the EntityManager 
    
    private void insertWebsites() { 
        Category category = new Category(); 
        category.setCategoryName("my category"); 
        // persist it separately to keep the id for later 
        em.persist(category); 
        Long categoryId = category.getId(); 
    
        for (int i = 1; i <= 5000; i++) { 
         Website website = new Website(); 
         website.setUrl("http://example.com" + i); 
         website.setCategory(category); 
         if (i % batchSize == 0) { 
          em.flush(); 
          em.clear(); 
          // get an attached reference to the category (you know 
          // that it exists) without querying the database again 
          category = em.getReference(Category.class, categoryId); 
         } 
        } 
    } 
    
  • +0

    답장을 보내 주셔서 감사합니다. 나는 당신의 지시를 따랐고 모든 것이 좋습니다. 그러나 캐스케이드에 또 하나의 엔티티 '블랙리스트'를 추가하려고하면 '전달 된 분리 된 엔티티'가 다시 발생합니다. 제발, 내 예제 코드를 참조하십시오 pastebin : [link] (http://pastebin.com/Y26xjS7A) – insanecoding

    관련 문제