복붙노트

[SPRING] 최대 절전 모드 1 캐시를 사용하거나 수동 세션 플러시를 수행하지 않고 Spring @ Transaction을 어떻게 테스트합니까?

SPRING

최대 절전 모드 1 캐시를 사용하거나 수동 세션 플러시를 수행하지 않고 Spring @ Transaction을 어떻게 테스트합니까?

Spring + Hibernate와 트랜잭션 어노테이션 사용하기.

나는 다음과 같은 것을 시험하려고 노력하고있다.

내가 가진 첫 번째 문제는 2 단계에서 User 객체를 읽는 것이고 최대 절전 모드 레벨 1 캐시에있는 하나의 객체를 반환하고 실제로 데이터베이스에서 읽지는 않았다.

따라서 세션을 사용하여 캐시에서 개체를 수동으로 축출하여 데이터베이스에서 강제로 읽습니다. 그러나이 작업을 수행 할 때 개체 값은 단위 테스트 내에서 유지되지 않습니다 (지정된 설정으로 인해 테스트가 완료된 후에 롤백된다는 것을 알고 있습니다).

@Transactional 서비스 메소드를 호출 한 후 수동으로 세션을 플러시하고 DID가 변경 내용을 커밋하려고했습니다. 그러나 그것은 내가 기대했던 것이 아니었다. @Transactional 서비스 메소드는 트랜잭션이 커밋되고 세션이 반환되기 전에 플러시되도록 보장한다고 생각했습니다. 나는 일반적으로 Spring이이 관리를 언제 할 것인지를 결정할 것이다. 그러나 @Transactional 메소드에서 "작업 단위"는 그 방법이라고 생각했다.

어쨌든 이제는 일반적으로 @Transactional 메서드를 테스트하는 방법을 알아 내려고 노력하고 있습니다.

실패한 junit 테스트 방법은 다음과 같습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@TransactionConfiguration(transactionManager = "userTransactionManager", defaultRollback = true)
@WebAppConfiguration()
@ContextConfiguration(locations = { "classpath:test-applicationContext.xml",
        "classpath:test-spring-servlet.xml",
        "classpath:test-applicationContext-security.xml" })
public class HibernateTest {

    @Autowired
    @Qualifier("userSessionFactory")
    private SessionFactory sessionFactory;

    @Autowired
    private UserService userService;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private QueryService queryService;

    @Autowired
    private NodeService nodeService;

    @Autowired
    private UserUtils userUtils;

    @Autowired
    private UserContext userContext;

  @Test
    public void testTransactions() {
        // read the user
        User user1 = userService.readUser(new Long(77));
        // change the display name
        user1.setDisplayName("somethingNew");
        // update the user using service method that is marked @Transactional
        userService.updateUserSamePassword(user1);
        // when I manually flush the session everything works, suggesting the
        // @Transactional has not flushed it at the end of the method marked
        // @Transactional, which implies it is leaving the transaction open?
        // session.flush();
        // evict the user from hibernate level 1 cache to insure we are reading
        // raw from the database on next read
        sessionFactory.getCurrentSession().evict(user1);
        // try to read the user again
        User user2 = userService.readUser(new Long(77));
        System.out.println("user1 displayName is " + user1.getDisplayName());
        System.out.println("user2 displayName is " + user2.getDisplayName());
        assertEquals(user1.getDisplayName(), user2.getDisplayName());
    }
}

수동으로 세션을 플러시하면 테스트가 성공합니다. 그러나 @Transactional 메서드가 세션을 커밋하고 플러시 할 것이라고 예상했을 것입니다.

updateUserSamePassword의 서비스 메소드는 다음과 같습니다.

@Transactional("userTransactionManager")
@Override
public void updateUserSamePassword(User user) {
    userDAO.updateUser(user);
}

DAO 방법은 다음과 같습니다.

@Override
public void updateUser(User user) {
    Session session = sessionFactory.getCurrentSession();
    session.update(user);
}

SessionFactory는 자동으로 실행됩니다 :

@Autowired
@Qualifier("userSessionFactory")
private SessionFactory sessionFactory;

XML 응용 프로그램 컨텍스트 구성을 사용하고 있습니다. 나는 가지고있다:

<context:annotation-config />
<tx:annotation-driven transaction-manager="userTransactionManager" />

<bean id="userDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 
    <property name="driverClass" value="${user.jdbc.driverClass}"/>
    <property name="jdbcUrl" value="${user.jdbc.jdbcUrl}" />
    <property name="user" value="${user.jdbc.user}" />
    <property name="password" value="${user.jdbc.password}" />
    <property name="initialPoolSize" value="3" />
    <property name="minPoolSize" value="1" />
    <property name="maxPoolSize" value="17" />
</bean>

<bean id="userSessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="userDataSource" />
    <property name="configLocation" value="classpath:user.hibernate.cfg.xml" />
</bean>

<bean id="userTransactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="dataSource" ref="userDataSource" />
    <property name="sessionFactory" ref="userSessionFactory" />
</bean>

또한 서비스 및 DAO 클래스에 대한 구성 요소 검사도 있습니다. 제가 말했듯이, 이것은 생산에서 일하고 있습니다.

그 방법의 끝 (예 : 여기에서 update 메소드)에 의해 @Transactional이라고 표시된 메소드가 있다면 Spring은 Session을 강제로 플러시하도록 강요했을 것입니다.

몇 가지 옵션 만 볼 수 있습니다.

이것이 예상 된 행동인지 누구라도 확인할 수 있습니까? @Transaction이 세션을 강제로 커밋하고 플러시해야합니까? 그렇지 않다면, @Transactional로 표시된 메소드를 테스트하고 메소드가 트랜잭션과 실제로 작동하는 방법은 무엇입니까?

즉, 여기에 내 단위 테스트를 어떻게 다시 작성해야합니까?

다른 아이디어?

해결법

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

    1.여기에 내가 뛰어 다니는 것이있다. 테스트 메소드에서 다음 코드를 고려하십시오.

    여기에 내가 뛰어 다니는 것이있다. 테스트 메소드에서 다음 코드를 고려하십시오.

        String testDisplayNameChange = "ThisIsATest";
        User user = userService.readUser(new Long(77));
        user.setDisplayName(testDisplayNameChange);
        user = userService.readUser(new Long(77));
        assertNotEquals(user.getDisplayName(), testDisplayNameChange);
    

    메소드 userService.readUser는 서비스 클래스에서 @Transactional로 표시됩니다.

    해당 테스트 메소드가 @Transactional로 표시되면 테스트가 실패합니다. 그렇지 않다면 성공합니다. 이제는 Hibernate 캐시가 실제로 참여할 지 확신 할 수 없다. 테스트 메소드가 트랜잭션 방식이라면 각각의 읽기는 하나의 트랜잭션에서 일어나고, 나는 그들이 하이버 네이트 레벨 1 캐시를 쳤을뿐 (실제로 데이터베이스로부터 읽지는 않는다) 믿는다. 그러나 테스트 메소드가 트랜잭션 적이 지 않으면 각각의 읽기는 자체 트랜잭션에서 일어나고 각각은 데이터베이스에 충돌합니다. 따라서, 동면 레벨 1 캐시는 세션 / 트랜잭션 관리에 연결된다.

    어웨이 :

    @Transactional 테스트 메소드를 가지고 테스트 클래스의 다른 메소드에서 @AfterTransaction을 사용하고 원시 SQL을 제출하여 데이터베이스의 값을 평가하려고했습니다. 이것은 ORM을 피하고 레벨 1 캐시를 최대 절전 모드로 설정하여 DB의 실제 값을 비교할 수있게합니다.

    간단한 대답은 @Transactional을 테스트 클래스에서 꺼내는 것이 었습니다. 예.

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

    2.큐

    에이

    이것은 예상되는 행동입니다. 설계에 의한 스프링 인식 트랜잭션 유닛 테스트 지원은 테스트가 끝난 후 트랜잭션을 롤백합니다. 이는 의도적으로 설계된 동작입니다.

    테스트 당 암시 적 트랜잭션 경계가 생성되고 (@Test가있는 각 메소드) 테스트가 완료되면 롤백이 수행됩니다.

    이 결과는 모든 테스트가 완료된 후에 실제로 변경된 데이터가없는 것입니다. 즉 목표는 더 "통합"과 같이 "더 적은"통합이되는 것입니다. 이것이 유익한 이유에 대한 봄 문서를 읽어야한다.

    지속되는 데이터를 테스트하고 해당 데이터를 트랜잭션 경계 외부에서 보려면 셀렌과 같은 기능 / 통합 테스트와 같은 엔드 투 엔드 테스트를 권장하거나 외부 WS / REST API를 사용하는 경우 해당 테스트를 수행하는 것이 좋습니다 .

    에이

    session.evict가 플러시되지 않기 때문에 단위 테스트가 작동하지 않습니다. 세션에서 이미 session.update를 호출하고 일괄 작업을 최대 절전 모드로 전환 했음에도 불구하고 변경 사항이 동기화되지 않도록합니다. 최대 절전 모드 지연 기능은 모든 작업을 일괄 처리하여 세션이 플러시되거나 닫혀 성능을 위해 데이터베이스와 통신 할 때까지 기다릴 수 있으므로 원시 SQL에서는 더 명확합니다. session.flush SQL이 즉시 실행됩니다 (즉, 업데이트가 실제로 발생합니다). 그런 다음 다시 읽지 만 트랜잭션 내에서 다시 읽으려는 경우 제거 할 수 있습니다. 나는 실제로 플러시가 강제 퇴거를 일으킬 것이라는 것을 확신하므로 재방송을 강제하도록 퇴거를 호출 할 필요가 없지만 잘못되었을 수도 있습니다.

  3. from https://stackoverflow.com/questions/26597440/how-do-you-test-spring-transactional-without-just-hitting-hibernate-level-1-cac by cc-by-sa and MIT license