Уровень изоляции транзакции SpringBoot - PullRequest
2 голосов
/ 24 февраля 2020

У меня есть этот проект, пытающийся проверить изоляцию транзакции. Я начал с уровня READ_UNCOMMITTED, и он не работает. Код довольно прост.

Основной класс

@SpringBootApplication
@EnableTransactionManagement
public class HibernateTransactionsLocksTestApplication {

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

}

Контроллер

    @RestController
    public class HomeController {

        private final AccountService accountService;

        public HomeController(AccountService accountService) {
            this.accountService = accountService;
        }

        @GetMapping("/updateAccount2RU")
        public String updateAccount2RU() throws InterruptedException {
            accountService.updateAccount2RU();
            return "done!";
        }

        @GetMapping("/update2Account2RU")
        public String update2Account2RU() throws InterruptedException {
            accountService.update2Account2RU();
            return "done!";
        }
}

Служба

@Service
public class AccountService {

    private final AccountRepository accountRepository;

    public AccountService(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void updateAccount2RU() throws InterruptedException {
        Account a = accountRepository.findById(2).get();
        System.out.println("Account amount: " + a.getAmount());
        a.setAmount(a.getAmount()+1);
        accountRepository.save(a);
        Thread.currentThread().sleep(5000);
    }

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void update2Account2RU() throws InterruptedException {
        Account a = accountRepository.findById(2).get();
        System.out.println("Account amount: " + a.getAmount());
        a.setAmount(a.getAmount()+1);
        Thread.currentThread().sleep(5000);
    }

}

Репозиторий представляет собой простой репозиторий SpringData

@Repository
public interface AccountRepository extends JpaRepository<Account, Integer> {

    @Transactional(propagation = Propagation.MANDATORY, isolation = Isolation.READ_UNCOMMITTED)
    Account findByName(String name);
}

application.properties

server.contextPath=/

spring.datasource.url=jdbc:mysql://localhost:3306/trasactions-locks-tests
spring.datasource.username=
spring.datasource.password=
spring.jpa.properties.hibernate.dialect =org.hibernate.dialect.MySQL5Dialect

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
#spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

spring.jpa.open-in-view=false

#Check transactions behaviour
logging.level.org.springframework.transaction.interceptor=TRACE

По сути, я открываю 2 вкладки в Chrome и открываю updateAccount2RU (это должно прочитать учетную запись и увеличить сумму, и поток должен спать в течение 5 секунд, прежде чем проснуться и совершение транзакции), и тем временем я получаю доступ ко второму методу update2Account2RU, который читает ту же учетную запись, и сумма равна той же самой считанной из первого метода, а не обновленной.

1 Ответ

0 голосов
/ 24 февраля 2020

Скорее всего, вам потребуется использовать accountRepository.save(a); в первом методе.

SQL обновления обычно буферизуются Hibernate до тех пор, пока транзакция не будет завершена, т.е. когда ваш транзакционный метод вернется.

Таким образом, в момент считывания записи T2 нет ожидающих незафиксированных изменений базы данных.

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void updateAccount2RU() throws InterruptedException {
    Account a = accountRepository.findById(2).get();
    System.out.println("Account amount: " + a.getAmount());
    a.setAmount(a.getAmount()+1);

    // force explicit flush so T2 should now see the uncommitted change
    accountRepository.saveAndFlush(a);

    Thread.currentThread().sleep(5000);
}

См .:

https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/objectstate-flushing.html

Время от времени Session будет выполнять операторы SQL, необходимые для синхронизации состояния соединения JDB C с состоянием объектов, хранящихся в памяти. Этот процесс, flu sh, по умолчанию происходит в следующих точках

  • перед выполнением некоторых запросов
  • из org.hibernate.Transaction.commit ()
  • от Session.flu sh ()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...