2016-11-01 15 views
0

私は、クラシックなSpring 4.3.2 + Hibernate(HQL)からSpring 1.4.1 + Hibernate(HikariCP)+ HikariCP接続プールに移行しています。インサート。私は1000から527 000要素の大きなリストを保存する必要があります。

1つのエンティティのみのバッチを依存関係なしで保存しているとき(たとえば、5000の要素のCategoryクラス)、すべてが問題ありません。私は(ちょうどそれのためにCategoryを設定してWebsite)を子と親エンティティの両方を保存しようとした場合
しかし、私は例外を取得:リストのサイズは少ないし、hibernate.jdbc.batch_sizeときorg.hibernate.PersistentObjectException: detached entity passed to persist: com.example.entities.Category

しかしを、すべてがOKです too !!!両方のエンティティは、親と子の適切に保存されます。それは本当に奇妙なようです!
私は十分にgoogled、StackOverflowで検索したが、具体的に私の問題の解決策を見つけることができませんでした。私は接続プールをApache DBCPに置き換えようとしましたが、まったく含まなかった)、Hibernateのパフォーマンス設定が変更され、PostgreSQL dbがH2に置き換えられましたが、結果はありません。

ここでエラーが発生する可能性がありますか?
JPAで何ができるのですか?
または、エンティティを別々に保存する必要があります(まず、親と子の両方)。もう1つの方法は、私のプログラムから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エンティティは、1対1の関係を持って、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ウェブサイトやカテゴリが永続化されているので、できるだけ早く最初のWebサイトが永続化されるよう
    • 上記で作成した単一のカテゴリへのすべてのポイントは、カテゴリは関係
  • であるためカスケードのあまりに永続化されます彼らは永続性の文脈の中に生きています。しかし、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

ありがとうございました!私はあなたの指示に従いました。 しかし、もう1つのエンティティ 'Blacklist'をカスケードに追加しようとすると、' detached entities passed to persist'が再び発生します。 pastebinで私のサンプルコードを見てください:[link](http://pastebin.com/Y26xjS7A) – insanecoding

関連する問題