복붙노트

[SPRING] Spring Data JPA를 Spring Container 외부에서 어떻게 사용합니까?

SPRING

Spring Data JPA를 Spring Container 외부에서 어떻게 사용합니까?

나는 봄 데이터 컨테이너를 사용하지 않고 DAO 프록시 (일명 리포지토리)를 생성 할 수 있도록 Spring Data JPA 객체를 수동으로 연결하려고합니다.

불가피하게 내가 왜 이것을하고 싶은지 물어볼 것입니다. 프로젝트가 이미 Google Guice (GWT에서 Gin을 사용하는 UI)를 사용하고 있기 때문에 다른 IoC 컨테이너 구성을 유지하거나 모든 결과 종속성. 우리가 Guice의 SpringIntegration을 사용할 수 있을지는 모르겠지만 이것이 최후의 수단이 될 것입니다.

모든 것을 수동으로 와이어 링 할 수있는 것 같지만 문서화가 잘되어 있지 않으므로 힘든 시간을 보내고 있습니다.

Spring Data 사용자 가이드에 따르면 독립형 저장소 팩토리를 사용할 수 있습니다. 불행히도이 예제는 추상 클래스 인 RepositoryFactorySupport를 보여줍니다. 약간의 검색 후 나는 그럭저럭 JpaRepositoryFactory를 발견 할 수 있었다.

JpaRepositoryFactory는 실제로 자동으로 트랜잭션을 생성하지 않는다는 점을 제외하고는 꽤 잘 작동합니다. 트랜잭션은 수동으로 관리해야하며 그렇지 않으면 데이터베이스에 유지되지 않습니다.

entityManager.getTransaction().begin();
repositoryInstance.save(someJpaObject);
entityManager.getTransaction().commit();

문제는 @Transactional 주석이 자동으로 사용되지 않으며 TransactionInterceptor의 도움이 필요하다는 것으로 판명되었습니다.

고맙게도, JpaRepositoryFactory는 반환되기 전에 생성 된 저장소 프록시에 더 많은 AOP 조언을 추가하기 위해 콜백을 사용할 수 있습니다.

final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
final JpaRepositoryFactory factory = new JpaRepositoryFactory(emf.createEntityManager());

factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
    @Override
    public void postProcess(ProxyFactory factory) {
        factory.addAdvice(new TransactionInterceptor(xactManager, new AnnotationTransactionAttributeSource()));
    }
});

이것은 일이 그렇게 잘되지 않는 곳입니다. 코드에서 디버거를 살펴보면 TransactionInterceptor는 실제로 트랜잭션을 생성하지만 잘못된 EntityManager를 사용합니다. Spring은 현재 실행중인 스레드를보고 활성 EntityManager를 관리합니다. TransactionInterceptor는이를 수행하여 스레드에 바인드 된 활성 EntityManager가없는 것을 확인하고 새 스레드를 작성하기로 결정합니다.

그러나이 새로운 EntityManager는 생성되어 JpaRepositoryFactory 생성자로 전달 된 인스턴스가 아니며 EntityManager가 필요합니다. 문제는 TransactionInterceptor와 JpaRepositoryFactory가 동일한 EntityManager를 사용하도록 만드는 방법입니다.

이것을 작성하는 동안 문제를 해결하는 방법을 찾았지만 여전히 이상적인 솔루션이 아닐 수도 있습니다. 나는이 해결책을 별도의 답변으로 게시 할 것이다. Spring Data JPA를 어떻게하면 해결했는지보다 독립된 방식으로 사용하는 더 좋은 방법에 대한 제안을 듣고 기쁘게 생각합니다.

해결법

  1. ==============================

    1.JpaRepositoryFactory의 디자인과 스프링 통합 JpaRepositoryFactory 빈에 대한 일반적인 원칙은 다음과 같습니다.

    JpaRepositoryFactory의 디자인과 스프링 통합 JpaRepositoryFactory 빈에 대한 일반적인 원칙은 다음과 같습니다.

    이것이 우리가 EntityManagerFactory가 아닌 EntityManager를 주입 한 이유입니다. 정의에 따르면 EntityManager는 스레드로부터 안전하지 않습니다. 따라서 EntityManagerFactory를 직접 다룰 경우 Spring이나 EJB처럼 관리되는 런타임 환경을 제공하는 모든 리소스 관리 코드를 다시 작성해야합니다.

    Spring 트랜잭션 관리와 통합하기 위해 Spring의 SharedEntityManagerCreator를 사용하는데, 실제로 당신이 수동으로 구현 한 트랜잭션 자원 바인딩 마법을 수행한다. 따라서 EntityManagerFactory에서 EntityManager 인스턴스를 생성하기 위해이 인스턴스를 사용하고 싶을 것이다. 저장소 빈에서 직접 트랜잭션을 활성화하려면 (예 : repo.save (...) 호출이 아직 활성화되지 않은 경우 트랜잭션을 생성하도록) Spring Data Commons의 TransactionalRepositoryProxyPostProcessor 구현을 살펴보십시오. Spring 데이터 저장소가 직접 사용될 때 (예 : repo.save (...)) 실제로 트랜잭션을 활성화하고 저장소 구성 인터페이스가 SimpleJpaRepository에 정의 된 트랜잭션 구성을 무시할 수 있도록 인터페이스를 선호하도록 트랜잭션 구성 조회를 약간 사용자 정의합니다.

  2. ==============================

    2.JpaRepositoryFactory를 사용하여 저장소를 만들기 전에 EntityManager와 EntityManagerFactory를 실행 스레드에 수동으로 바인딩하여이 문제를 해결했습니다. 이 작업은 TransactionSynchronizationManager.bindResource 메서드를 사용하여 수행 할 수 있습니다.

    JpaRepositoryFactory를 사용하여 저장소를 만들기 전에 EntityManager와 EntityManagerFactory를 실행 스레드에 수동으로 바인딩하여이 문제를 해결했습니다. 이 작업은 TransactionSynchronizationManager.bindResource 메서드를 사용하여 수행 할 수 있습니다.

    emf = Persistence.createEntityManagerFactory("com.foo.model", properties);
    em = emf.createEntityManager();
    
    // Create your transaction manager and RespositoryFactory
    final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
    final JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
    
    // Make sure calls to the repository instance are intercepted for annotated transactions
    factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
        @Override
        public void postProcess(ProxyFactory factory) {
            factory.addAdvice(new TransactionInterceptor(xactManager, new MatchAlwaysTransactionAttributeSource()));
        }
    });
    
    // Create your repository proxy instance
    FooRepository repository = factory.getRepository(FooRepository.class);
    
    // Bind the same EntityManger used to create the Repository to the thread
    TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
    
    try{
        repository.save(someInstance); // Done in a transaction using 1 EntityManger
    } finally {
        // Make sure to unbind when done with the repository instance
        TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
    }
    

    그래도 더 좋은 방법이 있어야합니다. RepositoryFactory가 EntityManagerFactory 대신 EnitiyManager를 사용하도록 설계된 것은 이상한 것 같습니다. EntityManger가 스레드에 바인딩되어 있는지 확인한 다음 새 스레드를 만들고 바인딩하거나 기존 스레드를 사용하는지 먼저 확인합니다.

    기본적으로 저장소 프록시를 삽입하고 모든 호출에 대해 내부적으로 새 EntityManager를 작성하여 호출이 스레드로부터 안전하도록해야합니다.

  3. from https://stackoverflow.com/questions/9123964/how-do-you-use-spring-data-jpa-outside-of-a-spring-container by cc-by-sa and MIT license