使用mysql做資料庫,mybatis做orm的系統中,mybatis的樂觀鎖和悲觀鎖實際上就是mysql的樂觀鎖和悲觀鎖。
執行個體中使用springboot整合mybatis,一并記錄了。
添加依賴:
- <dependency>
- <groupId>mysql </groupId>
- <artifactId>mysql-connector-java </artifactId>
- </dependency>
- <groupId>org.mybatis.spring.boot </groupId>
- <artifactId>mybatis-spring-boot-starter </artifactId>
- <version>1.1.1 </version>
application.properties配置:
- mybatis. type-aliases- package=com.lin.learn.mysql
- spring.datasource.driverClassName = com.mysql.jdbc.Driver
- spring.datasource.url = jdbc:mysql: //192.168.0.103:3306/mysql?useUnicode=true&characterEncoding=utf-8
- spring.datasource.username = root
- spring.datasource.password = root
實體類:
- publicclassTestEntity{
- privateint id;
- privateint count;
- privateint version;
- publicintgetId(){
- return id;
- }
- publicvoidsetId(int id){
- this.id = id;
- publicintgetCount(){
- return count;
- publicvoidsetCount(int count){
- this.count = count;
- publicintgetVersion(){
- return version;
- publicvoidsetVersion(int version){
- this.version = version;
加上相關注解:
- @SpringBootApplication
- @MapperScan( "com.lin.learn.mysql")
- publicclassApp{
- publicstaticvoidmain(String[] args){
- ApplicationContext applicationContext = SpringApplication.run(App.class, args);
樂觀鎖
就是使用一個version字段辨別資料的目前版本,每次更新資料的時候同時更新version = version + 1,where條件中需要加上version等于目前事務查詢出的資料的version,如果version的值已經改變,則更新失敗。
Mapper:
- publicinterfaceTestMapper{
- @Select("select * from `test` where `id` = #{id} for update")
- @Results({
- @Result(column = "id", property = "id", javaType = Integer.class),
- @Result(column = "count", property = "count", javaType = Integer.class),
- @Result(column = "version", property = "version", javaType = Integer.class)
- })
- public TestEntity getById( @Param("id") int id);
- @Update("update `test` set `count` = #{count}, `version` = #{version} + 1 where `id` = #{id} and `version` = #{version}")
- public int update(TestEntity testEntity);
業務代碼,就是資料庫一個資料修改:
- import org.springframework.stereotype.Component;
- import org.springframework.transaction.annotation.Transactional;
- import javax.annotation.Resource;
- @Component
- publicclassTestService{
- @Resource
- private TestMapper mapper;
- publicvoidincreaseCount(int id){
- while ( true) {
- try {
- TestEntity testEntity = mapper.getById(id);
- testEntity.setCount(testEntity.getCount() + 1);
- int updateCount = mapper.update(testEntity);
- if(updateCount > 0) {
- break;
- } catch (Exception e) {
- e.printStackTrace();
測試:
- import com.lin.learn.mysql.TestService;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.context.ApplicationContext;
- TestService service = applicationContext.getBean(TestService.class);
- new Thread( new Runnable() {
- @Override
- publicvoidrun(){
- for( int i = 0; i < 10; i++) {
- service.increaseCount( 3);
- }).start();
上面代碼執行後資料庫資料最終結果得到正确的資料。
悲觀鎖
本質上就是用select for update鎖行,需要注意的是如果where子句條件沒有命中索引将導緻鎖表。并且查詢和更新操作都需要在同一個事務裡裡面。
在Mapper添加新的更新方法:
- import org.apache.ibatis.annotations.*;
- @Update("update `test` set `count` = #{count} where `id` = #{id}")
- public void updateNoSafe(TestEntity testEntity);
在業務代碼裡面添加新的方法:
- @Transactional
- publicvoidincreaseNoSafe(int id){
- mapper.updateNoSafe(testEntity);
這裡是用@Transaction注解聲明這個更新資料的事務。
- service.increaseNoSafe( 3);