首页 > 解决方案 > 使用隔离级别的脏读示例

问题描述

出于教学目的,我想提供一个脏读示例。使用 READ_UNCOMMITED 隔离级别。但是我无法让这个例子工作。我尝试了很多不同的东西,但它仍然不起作用。所以我希望你能帮助我。

设想:

有关更多信息,请参见下面的代码。

我还针对 Postgres 数据库进行了测试,但这并没有什么不同。还尝试使用 JDBC 模板而不是 ORM 映射器来做所有事情,但这也没有什么区别。

提前致谢。

最好的问候, 马里努斯

IsolationLevelTest (src/test/java/test)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringBootTestApplication.class})
public class IsoliationLevelTest {

    @Autowired
    private TestService testService;

    @Autowired
    private UtilService serviceUtil;

    @After
    public void tearDown() {
        serviceUtil.deleteTestPersons();
    }

    @Test
    public void testIsolationLevel() {

        try {
            testService.testIsolationLevel("Piet", "PietUpdated");

        } catch (TestService.TestException e) {

            List<PersonJPAEntity> persons = serviceUtil.retrieveTestPersons();
            assertEquals(1, persons.size());
            assertEquals("Piet", persons.get(0).getName());

            assertEquals("PietUpdated", e.getPersonReadInNewTransaction().getName());
        }
    }
}

SpringBootTestApplication (src/main/java/test)

@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class SpringBootTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootTestApplication.class, args);
    }
}

测试服务

@Service
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public class TestService {

    Logger logger = LoggerFactory.getLogger(TestService.class);

    @Autowired
    private PersonRepository personRepository;

    @Transactional(propagation = REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
    public void testIsolationLevel(String initialName, String newName) {

        //Requires_new propagation so transaction is committed after person is save
        TestService self = (TestService) AopContext.currentProxy();
        self.insertPerson(new PersonJPAEntity(1, initialName));

        //Required propagation so this update runs in current transaction (which is not committed yet)
        self.updatePerson(newName);

        PersonJPAEntity personReadInNewTransaction = self.findPersonInNewTransaction();

        logger.info("Throw exception and thereby rollback transaction");

        throw new TestException("Rollback transaction", personReadInNewTransaction);
    }

    @Transactional(propagation = REQUIRES_NEW)
    public void insertPerson(PersonJPAEntity person) {

        personRepository.save(person);

        logger.info("Person inserted {}", person);
    }

    @Transactional(propagation = REQUIRED)
    public void updatePerson(String newName) {

        Optional<PersonJPAEntity> personToUpdate = personRepository.findById(1);
        personToUpdate.get().setName(newName);

        logger.info("Person updated {}", personToUpdate);

        personRepository.flush();

        logger.info("Repository flushed");
    }

    @Transactional(propagation = REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
    public PersonJPAEntity findPersonInNewTransaction() {

        Optional<PersonJPAEntity> person = personRepository.findById(1);

        logger.info("Person found in new transaction {}", person);

        return person.get();
    }

    public class TestException extends RuntimeException {

        private final PersonJPAEntity personReadInNewTransaction;

        public TestException(String message, PersonJPAEntity personReadInNewTransaction) {

            super(message);

            this.personReadInNewTransaction = personReadInNewTransaction;
        }

        public PersonJPAEntity getPersonReadInNewTransaction() {
            return personReadInNewTransaction;
        }
    }
}

个人资料库

public interface PersonRepository extends JpaRepository<PersonJPAEntity, Integer> {

    List<PersonJPAEntity> findByOrderByIdAsc();
}

实用服务

@Service
public class UtilService {

    @Autowired
    PersonRepository personRepository;

    @Transactional(propagation = REQUIRES_NEW)
    public List<PersonJPAEntity> retrieveTestPersons() {

        return personRepository.findByOrderByIdAsc();
    }

    @Transactional(propagation = REQUIRES_NEW)
    public void deleteTestPersons() {

        personRepository.deleteAll();
    }
}

PersonJPAEntity (src/main/java/test)

@Entity(name = "person")
public class PersonJPAEntity {

@Id
private int id;
private String name;

private PersonJPAEntity() {
}

PersonJPAEntity(int id, String name) {
    this.id = id;
    this.name = name;
}

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof PersonJPAEntity)) return false;
    PersonJPAEntity person = (PersonJPAEntity) o;
    return id == person.id &&
            Objects.equals(name, person.name);
}

@Override
public int hashCode() {
    return Objects.hash(id, name);
}

@Override
public String toString() {
    return "Person{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
}

}

application.properties (src/main/resources)

# spring.jpa.show-sql=true
logging.level.org.springframework.transaction=TRACE

标签: javaspringspring-data-jpaisolation-leveldirtyread

解决方案


推荐阅读