天天看點

資料庫悲觀鎖和樂觀鎖使用Mybatis

使用mysql做資料庫,mybatis做orm的系統中,mybatis的樂觀鎖和悲觀鎖實際上就是mysql的樂觀鎖和悲觀鎖。

執行個體中使用springboot整合mybatis,一并記錄了。

添加依賴:

  1. <dependency>
  2. <groupId>mysql </groupId>
  3. <artifactId>mysql-connector-java </artifactId>
  4. </dependency>
  5. <groupId>org.mybatis.spring.boot </groupId>
  6. <artifactId>mybatis-spring-boot-starter </artifactId>
  7. <version>1.1.1 </version>

application.properties配置:

  1. mybatis. type-aliases- package=com.lin.learn.mysql
  2. spring.datasource.driverClassName = com.mysql.jdbc.Driver
  3. spring.datasource.url = jdbc:mysql: //192.168.0.103:3306/mysql?useUnicode=true&characterEncoding=utf-8
  4. spring.datasource.username = root
  5. spring.datasource.password = root

實體類:

  1. publicclassTestEntity{
  2. privateint id;
  3. privateint count;
  4. privateint version;
  5. publicintgetId(){
  6. return id;
  7. }
  8. publicvoidsetId(int id){
  9. this.id = id;
  10. publicintgetCount(){
  11. return count;
  12. publicvoidsetCount(int count){
  13. this.count = count;
  14. publicintgetVersion(){
  15. return version;
  16. publicvoidsetVersion(int version){
  17. this.version = version;

加上相關注解:

  1. @SpringBootApplication
  2. @MapperScan( "com.lin.learn.mysql")
  3. publicclassApp{
  4. publicstaticvoidmain(String[] args){
  5. ApplicationContext applicationContext = SpringApplication.run(App.class, args);

樂觀鎖

就是使用一個version字段辨別資料的目前版本,每次更新資料的時候同時更新version = version + 1,where條件中需要加上version等于目前事務查詢出的資料的version,如果version的值已經改變,則更新失敗。

Mapper:

  1. publicinterfaceTestMapper{
  2. @Select("select * from `test` where `id` = #{id} for update")
  3. @Results({
  4. @Result(column = "id", property = "id", javaType = Integer.class),
  5. @Result(column = "count", property = "count", javaType = Integer.class),
  6. @Result(column = "version", property = "version", javaType = Integer.class)
  7. })
  8. public TestEntity getById( @Param("id") int id);
  9. @Update("update `test` set `count` = #{count}, `version` = #{version} + 1 where `id` = #{id} and `version` = #{version}")
  10. public int update(TestEntity testEntity);

業務代碼,就是資料庫一個資料修改:

  1. import org.springframework.stereotype.Component;
  2. import org.springframework.transaction.annotation.Transactional;
  3. import javax.annotation.Resource;
  4. @Component
  5. publicclassTestService{
  6. @Resource
  7. private TestMapper mapper;
  8. publicvoidincreaseCount(int id){
  9. while ( true) {
  10. try {
  11. TestEntity testEntity = mapper.getById(id);
  12. testEntity.setCount(testEntity.getCount() + 1);
  13. int updateCount = mapper.update(testEntity);
  14. if(updateCount > 0) {
  15. break;
  16. } catch (Exception e) {
  17. e.printStackTrace();

測試:

  1. import com.lin.learn.mysql.TestService;
  2. import org.mybatis.spring.annotation.MapperScan;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.context.ApplicationContext;
  6. TestService service = applicationContext.getBean(TestService.class);
  7. new Thread( new Runnable() {
  8. @Override
  9. publicvoidrun(){
  10. for( int i = 0; i < 10; i++) {
  11. service.increaseCount( 3);
  12. }).start();

上面代碼執行後資料庫資料最終結果得到正确的資料。

悲觀鎖

本質上就是用select for update鎖行,需要注意的是如果where子句條件沒有命中索引将導緻鎖表。并且查詢和更新操作都需要在同一個事務裡裡面。

在Mapper添加新的更新方法:

  1. import org.apache.ibatis.annotations.*;
  2. @Update("update `test` set `count` = #{count} where `id` = #{id}")
  3. public void updateNoSafe(TestEntity testEntity);

在業務代碼裡面添加新的方法:

  1. @Transactional
  2. publicvoidincreaseNoSafe(int id){
  3. mapper.updateNoSafe(testEntity);

這裡是用@Transaction注解聲明這個更新資料的事務。

  1. service.increaseNoSafe( 3);