天天看点

数据库悲观锁和乐观锁使用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);