2015-01-28 3 views
2

여러 스레드에서 여러 서버 노드에서 실행되는 가져 오기 서비스에서 여러 개체 생성을 방지하려고합니다. 저의 유일한 기회는 모든 것을 다시 모으는 유일한 단일 지점 인 데이터베이스에 고유 한 제한 조건을 사용하는 것 같습니다.이론적으로 사용할 수 있지만 중첩 된 트랜잭션 중첩 된 트랜잭션

이제 내 서비스가 개체 생성을 시도하고 DataIntegrityViolationException을 수신합니다. 그 후에 다른 프로세스에 의해 이미 생성 된 객체는 쿼리되고 사용되어야한다. 여태까지는 그런대로 잘됐다.

하지만이 개체에 대한 쿼리는 현재 트랜잭션이 중단되었다는 오류 메시지 SQL state [25P02]; error code [0]과 함께 Postgres에 의해 거부됩니다. 짧은 내 코드에서

은 다음과 같습니다

@Transactional 
public void connectToCollections(Product product) { 
    for (Collection coll : product.getCollections()) { 
     Set<CollectionMaster> collCandidates = new HashSet<CollectionMaster>(); 
     CollectionMaster oldMaster = coll.getMaster(); 

     newMaster = collectionMasterRepository.findByTitleAndSubtitle(product, coll); 

     if (newMaster == null) { 
      newMaster = createNewMaster(product,coll); 
      if(newMaster == null) { 
       // Maybe already created by another process; search again! 
       newMaster = collectionMasterRepository.findByTitleAndSubtitle(product, coll); 
      } 
     } 

     // Do something with newMaster 
    } 
} 

private CollectionMaster createNewMaster(Product product, Collection coll) { 
    CollectionMaster oldMaster = coll.getMaster(); 
    CollectionMaster newMaster = null; 

    oldMaster.setCreatedBy(product.getCreatedBy()); 
    oldMaster.setLastModifiedBy(product.getCreatedBy()); 

    try { 
     newMaster = create(oldMaster); 
    } 
    catch(DataIntegrityViolationException e) { 
     return null; 
    } 
    coll.setMaster(newMaster); 

    return newMaster; 
} 


@Transactional(propagation=Propagation.NESTED) 
public CollectionMaster create(CollectionMaster collMaster) { 
    CollectionMaster result = null; 
    result = collectionMasterRepository.saveAndFlush(collMaster); 
    return result; 
} 

진입 점은 connectToCollections입니다. CollectionMaster가 조회되면 발견되지 않으면 작성하려고 시도하고, 실패하면 다시 조회합니다. NESTED (또는 REQUIRES_NEW)와 같은 전파에 대한 @Transactional 속성은 무시되고 데이터베이스에 저장 점이 설정되거나 사용되지 않는 것으로 보입니다. 일반적으로 사용 된 기술 스택이 그렇게 할 수 있어야합니다. 내가 뭘 놓치고 있니?

pom.xml 파일의 관련 부분은 다음과 같습니다

<hibernate.version>4.1.7.Final</hibernate.version> 
<postgresql.version>9.3-1102-jdbc41</postgresql.version> 
<commons-dbcp2.version>2.0.1</commons-dbcp2.version> 
<spring.version>3.2.9.RELEASE</spring.version> 
<spring-data-jpa.version>1.6.0.RELEASE</spring-data-jpa.version> 
<aspectj.version>1.8.0</aspectj.version> 

<dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-core</artifactId> 
    <version>${spring.version}</version> 
</dependency> 
<dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-beans</artifactId> 
    <version>${spring.version}</version> 
</dependency> 
<dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-orm</artifactId> 
    <version>${spring.version}</version> 
</dependency> 
<dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-jdbc</artifactId> 
    <version>${spring.version}</version> 
</dependency> 
<dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-tx</artifactId> 
    <version>${spring.version}</version> 
</dependency> 
<dependency> 
    <groupId>org.springframework.data</groupId> 
    <artifactId>spring-data-jpa</artifactId> 
    <version>${spring-data-jpa.version}</version> 
    <exclusions> 
     <exclusion> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-beans</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-aop</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-context</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-expression</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-orm</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-jdbc</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-tx</artifactId> 
     </exclusion> 
    </exclusions> 
</dependency> 
<dependency> 
    <groupId>org.apache.commons</groupId> 
    <artifactId>commons-dbcp2</artifactId> 
    <version>${commons-dbcp2.version}</version> 
</dependency> 
<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-core</artifactId> 
    <version>${hibernate.version}</version> 
    <exclusions> 
     <exclusion> 
      <groupId>cglib</groupId> 
      <artifactId>cglib</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>net.sf.ehcache</groupId> 
      <artifactId>ehcache</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>asm</groupId> 
      <artifactId>asm</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>asm</groupId> 
      <artifactId>asm-attrs</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>javax.transaction</groupId> 
      <artifactId>jta</artifactId> 
     </exclusion> 
    </exclusions> 
</dependency> 
<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-entitymanager</artifactId> 
    <version>${hibernate.version}</version> 
    <exclusions> 
     <exclusion> 
      <groupId>cglib</groupId> 
      <artifactId>cglib</artifactId> 
     </exclusion> 
     <exclusion> 
      <groupId>dom4j</groupId> 
      <artifactId>dom4j</artifactId> 
     </exclusion> 
    </exclusions> 
</dependency> 
<dependency> 
    <groupId>javax.transaction</groupId> 
    <artifactId>transaction-api</artifactId> 
    <version>1.1</version> 
</dependency> 
<dependency> 
    <groupId>org.postgresql</groupId> 
    <artifactId>postgresql</artifactId> 
    <version>${postgresql.version}</version> 
</dependency> 

그리고

마침내 Spring 애플리케이션 컨텍스트의 관련 부분 :

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> 
    <property name="driverClassName" value="${database.driverClassName}" /> 
    <property name="url" value="${database.url}" /> 
    <property name="username" value="${database.username}" /> 
    <property name="password" value="${database.password}" /> 
</bean> 

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="jpaVendorAdapter"> 
     <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
      <property name="generateDdl" value="true" /> 
      <property name="database" value="${database.dialect}" /> 
     </bean> 
    </property> 
    <property name="jpaPropertyMap"> 
     <map> 
      <entry key="javax.persistence.validation.factory" value-ref="validator" /> 
      <entry key="hibernate.show_sql" value="${hibernate.show_sql}"/> 
      <entry key="hibernate.hbm2ddl.auto" value="${hibernate.hbm2ddl}"/> 
      <entry key="org.hibernate.envers.track_entities_changed_in_revision" value="true"/> 
     </map> 
    </property> 
    <property name="persistenceUnitName" value="mvb.vlx.model" /> 
    <property name="persistenceUnitManager" ref="persistenceUnitManager" /> 
</bean> 

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
    <property name="nestedTransactionAllowed" value="true"/> 
</bean> 

<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" /> 

Java 버전 jdk1.7.0_40

입니다

답변

3

면책 조항 : 저는 Postgres를 사용한 적이 없습니다. 중첩 된 트랜잭션 지원은 DB 공급 업체와 드라이버간에 상당히 다를 수 있습니다. 데이터베이스에 의해 실패로

(가) 실패 작성하는 시점에서
try { 
    ... 
} catch(DataIntegrityViolationException e) { 
    return null; 
} 

이, 트랜잭션이 표시되어 있지만 자바에서 이것을 무시하고 :

그러나, 나는 문제이 될 것 같아요. 그럼 당신이 그걸 저지하려고 할 때 나중에 실패 할 것 같아요.

귀하의 옵션은 전체 블록 (connectToCollections())에서 실패하거나 다시 시도하거나 새/중첩 트랜잭션에서 createNewMaster()를 수행하는 것입니다.

후자의 경우 @Transactional이 프록시 생성 방법 때문에 개인 메서드에서 작동하지 않는다는 것을 기억하십시오. Spring의 TransactionTemplate과 RetryTemplate은 이것을 프로그래밍을 통해 구현할 수 있습니다.

또한 올바르게 이해할 수 있는지 확실하지 않지만 connectToCollections()에 '중첩'을 넣으려는 의도가 있다면 물론 차이가 없습니다 (createNewMaster()를 중첩하려는 경우).

+0

개인 트랜잭션의 @Transactional은 우리가 사용하는 aspectj와 작동합니다. 명시 적으로 트랜잭션 관리자를 autowire하고 트랜잭션을 수동으로 시작하면 HibernateDialect를 구성하더라도 "JPA dialect는 중첩 트랜잭션을 지원하지 않습니다"예외가 발생합니다. 그것은 이상한 현상으로 남아 ... –

+0

끝날 때까지 나는 중첩 된 또는 "정말로"새로운 거래를 만들 수 없었다. 그러나 RetryTemplate이 트릭을했습니다! 감사. –

관련 문제