Search code examples
javaspringspring-data-jpaspring-transactionstransactional

Why I can't effect READ_UNCOMMITTED Isolation in my test


I's just want to test the Transactional annotation support by spring with spring data jpa,and spring boot.
Here's my controller method.

@GetMapping(path = "testreaduncommit")
public void testReadUnCommit()
{
    new Thread(new Runnable() {
        @Override
        public void run() {
        testService.testReadUncommitted1();
        }
    }).start();
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    testService.testReadUncommitted2();
}

And There is my service implement

@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void testReadUncommitted1() {
    log.info("testUncommit1 begin");
    User u=userRepository.findOne(1L);
    u.setUserPwd("654321");
    userRepository.save(u);
    log.info("testUncommit1 sleep");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("testUncommit1 will be commit");
}

@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void testReadUncommitted2() {
    log.info("testUncommit2 begin");
    User u=userRepository.findOne(1L);
    log.info(u);
}


And the result is:
testUncommit1 begin;
testUncommit1 sleep;
testUncommit2 begin;
User(userId=1, userName=testuser, userPhone=15888888888, userStatus=2, userPwd=123456);
testUncommit1 will be commit;
So I wonder why the annotaion @Transactional's property 'isolation = Isolation.READ_UNCOMMITTED' don't effect,I think the result should be userPwd=654321 because there should be 'dirty read' occur for the READ_UNCOMMITTED.
And I had try to add userRepository.flush();,It effected but after I change the isolation level to SERIALIZABLE,the dirty read happened too,why?
My database is Mysql.

So when I add the flush(); I change the method's isolation;

@Override
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void testReadCommit1() {
        log.info("testReadCommit1 begin");
        User u=userRepository.findOne(1L);
        u.setUserPwd("654321");
        userRepository.save(u);
        userRepository.flush();
        log.info("testReadCommit1 sleep");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("testReadCommit1 will be commit");
    }

    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void testReadCommit2() {
        log.info("testUncommit2 begin");
        User u=userRepository.findOne(1L);
        log.info(u);
    }

The result is:
User(userId=1, userName=testuser, userPhone=15888888888, userStatus=2, userPwd=654321)
Why SERIALIZABLE didn't prevent the dirty read?


Solution

  • Following API docs says call to flush() will flush all pending changes to the database. And hence when your testReadCommit2() method runs after 3 seconds, it isn't reading the dirty record.

    https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html#flush--