java - with - spring hibernate repository




Spring 부팅+Hibernate+JPA 트랜잭션 가능한 EntityManager 없음 (2)

최대 절전 모드에서 JPA와 함께 봄 부팅 1.2.3.RELEASE 버전을 사용하고 있습니다. 다음과 같은 예외가 발생합니다.

org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410) ~[EntityManagerFactoryUtils.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223) ~[HibernateJpaDialect.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417) ~[AbstractEntityManagerFactoryBean.class:4.1.6.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) ~[ChainedPersistenceExceptionTranslator.class:4.1.6.RELEASE]
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) ~[DataAccessUtils.class:4.1.6.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) ~[PersistenceExceptionTranslationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122) ~[CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.class:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) [ExposeInvocationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) [JdkDynamicAopProxy.class:4.1.6.RELEASE]
at com.sun.proxy.$Proxy110.deleteByCustomerId(Unknown Source) ~[na:na]

Caused by: javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:275) ~[SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.class:4.1.6.RELEASE]
at com.sun.proxy.$Proxy102.remove(Unknown Source) ~[na:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$DeleteExecution.doExecute(JpaQueryExecution.java:270) ~[JpaQueryExecution$DeleteExecution.class:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74) ~[JpaQueryExecution.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:97) ~[AbstractJpaQuery.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:88) ~[AbstractJpaQuery.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:395) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:373) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]

다음은 내 프로그램 구조입니다.
구성 클래스

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableTransactionManagement
public class WSApplication {
    public static void main(final String[] args) {
        SpringApplication.run(WSApplication.class, args);
    }
}

@Entity
@Table(Orders)
public class Order {
    @id
    @GeneratedValue
    private long id;

    @Column(name = "customerId")
    private Long customerId;

    // getter & setter methods
    // equals & hashCode methods
}

public interface OrderRepository extends JpaRepository<Order, Long> {

    List<Order> findByCustomerId(Long customerId);

    // 4- @Transactional works fine
    void deleteByCustomerId(Long cusotmerId);

}

public class OrderService {

    @Autowired
    private OrderRepository repo;

    // 3- @Transactional works fine
    public void deleteOrder(long customerId){
        //1- throws exception
        repo.deleteByCustomerId(customerId); 

        //2- following works fine
        //repo.delete(repo.findByCustomerId(customerId).get(0));
    }

}

위의 서비스 클래스 코드에서 아무도 왜 2가 작동하는지 그리고 1은 예외를 throw하는지 안내 해줄 수 있습니까?

감사


먼저 delete 메소드가 왜 당신의 경우에 효과가 있는지를 정당화하기 위해 스프링 데이터 JPA 문서 의 인용문을 만든다 (나는 옵션 2를 의미한다).

저장소 인스턴스의 CRUD 메소드는 기본적으로 트랜잭션입니다. 읽기 작업의 경우 트랜잭션 구성 readOnly 플래그가 true로 설정되고 다른 모든 트랜잭션은 기본 트랜잭션 구성이 적용되도록 일반 @Transactional 구성됩니다. 자세한 내용은 CrudRepository의 JavaDoc을 참조하십시오.

delete 메소드는 실제로 CrudRepository 의 메소드입니다. 저장소는 JpaRepository 를 확장하는 JpaRepository 를 확장하므로 CrudRepository 인터페이스에 속하며 위의 인용에 따르면 트랜잭션입니다.

Transactional Query Method 절을 읽으면 옵션 4 와 동일하다는 것을 알게되고 저장소의 모든 메소드에 대해 커스텀 트랜잭션 동작을 적용하는 방법을 알게 될 것이다. 또한 설명서의 예제 61 은 옵션 3 과 동일한 시나리오를 보여줍니다.

이제는 JDBC 로직으로 작업하지 않는다는 것을 명심하십시오.이 경우 데이터베이스는 트랜잭션을 처리하지만 ORM 기반 프레임 워크에서는 처리합니다. ORM 프레임 워크는 객체 캐시와 데이터베이스 간의 동기화를 트리거하기 위해 트랜잭션이 필요합니다. 따라서 deleteByCustomerId 와 같은 ORM 로직을 수행하는 메소드에 대해 트랜잭션 컨텍스트를 인식하고 제공해야합니다.

기본적으로 @Transactional (매개 변수없이) 전파 모드를 REQUIRED 설정하고 readOnly 플래그를 false로 설정합니다. 내부에 주석이 달린 메소드를 호출하면 아무도없는 경우 트랜잭션이 초기화됩니다. 이것이 @LucasSaldanha ( 여러 저장소 호출을 위해 트랜잭션을 정의하기 위해 facade 사용 예제와 같은)와 옵션 4해결 방법 이 작동하는 이유입니다. 트랜잭션이 없으면 옵션 1의 예외가 발생합니다.


좋아, 나는 그것이 작동하게 만드는 방법을 발견했다.

OrderServicedeleteOrder 메소드에 @Transactional 어노테이션 (org.springframework.transaction.annotation.Transactional)을 넣으면 된다.

@Transactional
public void deleteOrder(long customerId){
    repo.deleteByCustomerId(customerId);
}

나는 왜 두 번째 작품이 진짜인지 모르겠다. 아마 CrudRepository 인터페이스의 직접적인 방법이기 때문에 원자 적으로 실행하는 방법을 알고있을 것입니다.

전자는 deleteByCustomerId에 대한 호출입니다. 이 호출은 지정된 ID를 가진 고객을 찾기 위해 처리 된 다음 삭제됩니다. 어떤 이유로 그것은 명시적인 트랜잭션을 사용합니다.

다시 한번 그것은 단지 추측입니다. 일부 스프링 개발자에게 연락을 시도하고 아마도이 문제를 확인하기 위해 문제를 열어 보겠습니다.

희망이 도움이됩니다!

참조 : http://spring.io/guides/gs/managing-transactions/





spring-boot