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()
。
我将隔离级别设置为 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()
。