spring 引导隔离级别 READ_UNCOMMITTED 不工作

spring boot Isolation level READ_UNCOMMITTED not working

我将隔离级别设置为 READ_UNCOMMITTED,使用调试点更新两个 select 查询之间的值,我断言第一个和第二个读取值不相同,但实际上是第二个 select 值等于第一个。

下面是我的源代码

public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    @Override
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void readUncommitted() {
        String firstReadPassword = userRepository.findFirstByUsername("admin").map(User::getPassword).get();
        log.info("first read user admin password is {}", firstReadPassword);
        // debug point below, and do update password
        String secondReadPassword = userRepository.findFirstByUsername("admin").map(User::getPassword).get();
        log.info("second read user admin password is {}", secondReadPassword);
        assertNotEquals(firstReadPassword, secondReadPassword);
    }
}

public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    Optional<User> findFirstByUsername(String username);
}

下面是我的测试代码

@SpringBootTest
public class ReadUncommittedTest {
    @Configuration
    @ComponentScan("com.pohvii.playground.transaction.readuncommitted.service")
    @EntityScan("com.pohvii.playground.users.entity")
    @EnableJpaRepositories(basePackages = {"com.pohvii.playground.transaction.readuncommitted.repository"})
    @Import({
            DataSourceAutoConfiguration.class,
            HibernateJpaAutoConfiguration.class,
    })
    @EnableTransactionManagement
    static class TestConfiguration {
    }

    @Autowired
    private UserService userService;

    @Test
    void readUncommittedTest() throws Exception {
        userService.readUncommitted();
    }
}

下面是我的测试日志

2021-05-08 10:15:27.939 TRACE 45788 --- [           main] t.a.AnnotationTransactionAttributeSource : Adding transactional method 'com.pohvii.playground.transaction.readuncommitted.service.impl.UserServiceImpl.readUncommitted' with attribute: PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED
2021-05-08 10:15:31.497 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.jdbc.datasource.ConnectionHolder@7b5021d1] for key [HikariDataSource (NotebookHikariCP)] to thread [main]
2021-05-08 10:15:31.498 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] to thread [main]
2021-05-08 10:15:31.498 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Initializing transaction synchronization
2021-05-08 10:15:31.498 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [com.pohvii.playground.transaction.readuncommitted.service.impl.UserServiceImpl.readUncommitted]
2021-05-08 10:15:32.681 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] bound to thread [main]
2021-05-08 10:15:32.681 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@7b5021d1] for key [HikariDataSource (NotebookHikariCP)] bound to thread [main]
2021-05-08 10:15:32.681 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByUsername]
2021-05-08 10:15:32.686 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] bound to thread [main]
2021-05-08 10:15:32.769 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] bound to thread [main]
2021-05-08 10:15:32.777 DEBUG 45788 --- [           main] org.hibernate.SQL                        : select user0_.id as id1_0_, user0_.password as password2_0_, user0_.password_salt as password3_0_, user0_.role as role4_0_, user0_.username as username5_0_, user0_.version as version6_0_ from users user0_ where user0_.username=? limit ?
2021-05-08 10:15:32.914 TRACE 45788 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [admin]
2021-05-08 10:15:32.977 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByUsername]
2021-05-08 10:15:32.977  INFO 45788 --- [           main] c.p.p.t.r.service.impl.UserServiceImpl   : first read user admin password is TohH
2021-05-08 10:15:33.038 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-05-08 10:15:33.045 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-05-08 10:16:11.336 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] bound to thread [main]
2021-05-08 10:16:11.337 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@7b5021d1] for key [HikariDataSource (NotebookHikariCP)] bound to thread [main]
2021-05-08 10:16:11.337 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByUsername]
2021-05-08 10:16:11.337 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] bound to thread [main]
2021-05-08 10:16:11.337 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] bound to thread [main]
2021-05-08 10:16:11.345 DEBUG 45788 --- [           main] org.hibernate.SQL                        : select user0_.id as id1_0_, user0_.password as password2_0_, user0_.password_salt as password3_0_, user0_.role as role4_0_, user0_.username as username5_0_, user0_.version as version6_0_ from users user0_ where user0_.username=? limit ?
2021-05-08 10:16:11.345 TRACE 45788 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [admin]
2021-05-08 10:16:11.567 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByUsername]
2021-05-08 10:16:11.589 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-05-08 10:16:11.597 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-05-08 10:16:33.829  INFO 45788 --- [           main] c.p.p.t.r.service.impl.UserServiceImpl   : second read user admin password is TohH
2021-05-08 10:16:33.859 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-05-08 10:16:35.791 TRACE 45788 --- [           main] o.s.t.i.TransactionInterceptor           : Completing transaction for [com.pohvii.playground.transaction.readuncommitted.service.impl.UserServiceImpl.readUncommitted] after exception: org.opentest4j.AssertionFailedError: expected: not equal but was: <TohH>
2021-05-08 10:16:35.791 TRACE 45788 --- [           main] o.s.t.i.RuleBasedTransactionAttribute    : Applying rules to determine whether transaction should rollback on org.opentest4j.AssertionFailedError: expected: not equal but was: <TohH>
2021-05-08 10:16:35.791 TRACE 45788 --- [           main] o.s.t.i.RuleBasedTransactionAttribute    : Winning rollback rule is: null
2021-05-08 10:16:35.791 TRACE 45788 --- [           main] o.s.t.i.RuleBasedTransactionAttribute    : No relevant rollback rule found: applying default rules
2021-05-08 10:16:35.878 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Clearing transaction synchronization
2021-05-08 10:16:35.878 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.orm.jpa.EntityManagerHolder@21d9cd04] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@260e3837] from thread [main]
2021-05-08 10:16:35.878 TRACE 45788 --- [           main] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.jdbc.datasource.ConnectionHolder@7b5021d1] for key [HikariDataSource (NotebookHikariCP)] from thread [main]

将两次调用的结果分配给 findFirstByUsername().get() 以分隔变量,并在调试时比较它们的引用 ID。你会有你的答案。

正确的测试包括在两次调用之间调用 EntityManager.clear()