MybatisPlus乐观锁











代码片段

java 复制代码
TestOneMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hk.mapper.TestOneMapper">
    <select id="query" resultType="com.hk.entity.TestOneInfo">
        select * from test_testone
    </select>
    <select id="queryList" resultType="com.hk.entity.TestOneInfo">
        select * from test_testone
        ${ew.customSqlSegment}
    </select>
</mapper>



TestOneInfo.java

package com.hk.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;

import java.io.Serializable;
import java.util.Date;

@Data
@TableName("test_testone")
public class TestOneInfo implements Serializable{
	@TableId(type = IdType.ASSIGN_ID)
	private String id = null;
	private String name = null;
	@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private Date createTime = new Date();
	private String createId = null;
	@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private Date updateTime = null;
	private String updateId = null;
	@Version
	private Long version = 0L;
}


TestOneMapper.java

package com.hk.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.hk.entity.TestOneInfo;
import org.apache.ibatis.annotations.Param;

public interface TestOneMapper extends BaseMapper<TestOneInfo> {
    public IPage<TestOneInfo> queryList(IPage<TestOneInfo> page ,
                                        @Param(Constants.WRAPPER) Wrapper<TestOneInfo> wrapper);
}


ITestOneService.java

package com.hk.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hk.entity.TestOneInfo;
import com.hk.entity.TestTwoInfo;

public interface ITestOneService extends IService<TestOneInfo> {
    boolean save(TestOneInfo t);
    TestOneInfo get(String id) throws Exception;
    boolean delete(TestOneInfo t) throws Exception;
    boolean deleteById(String id) throws Exception;

    /**
     * 操作本表并通过Service操作第二张表,代码设置除0操作,检查两张表上的事务一致性
     * @return
     * @throws Exception
     */
    boolean testTx1() throws Exception;

    /**
     * testTx2,TestTx3分别操作表1和表2,它们同在一个Service实现类上
     * 检查两张表上的事务一致性
     * @return
     * @throws Exception
     */
    boolean testTx2(TestOneInfo t) throws Exception;
    boolean testTx3(TestTwoInfo t) throws Exception;

    /**
     * testTx4是公共方法,其中调用私有方法testTx5,两个方法分别操作两个表,
     * 代码抛出异常,检查两张表上的事务一致性
     * @param t
     * @return
     * @throws Exception
     */
    boolean testTx4(TestOneInfo t) throws Exception;

    /**
     * 根据数据库列定制Where条件,分页查询
     * @param pageNo 页码
     * @param pageSize 每页显示记录数
     * @param name 查询列
     * @param orderColumn 排序列
     * @param order 升序或降序,其值为CommonEnum.ASC或CommonEnum.DESC
     * @return
     * @throws Exception
     */
    IPage<TestOneInfo> queryName(Integer pageNo, Integer pageSize,String name,
                                    String orderColumn, String order)
            throws Exception;

    /**
     * 根据数据库列定制Where条件,分页查询Mapper中定义的方法
     * @param pageNo 页码
     * @param pageSize 每页显示记录数
     * @param orderColumn 排序列
     * @param order 升序或降序,其值为CommonEnum.ASC或CommonEnum.DESC
     * @param queryWrapper 查询条件封装类
     * @return
     * @throws Exception
     */
    IPage<TestOneInfo> queryMethod(Integer pageNo, Integer pageSize,
               String orderColumn, String order,
               QueryWrapper<TestOneInfo> queryWrapper)
            throws Exception;
}


TestOneServiceImpl.java

package com.hk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hk.entity.TestOneInfo;
import com.hk.entity.TestTwoInfo;
import com.hk.mapper.TestOneMapper;
import com.hk.service.ITestOneService;
import com.hk.service.ITestTwoService;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@Transactional(readOnly=false,rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public class TestOneServiceImpl extends ServiceImpl<TestOneMapper, TestOneInfo>
        implements ITestOneService
{
    @Autowired
    private ITestTwoService testTwoService;
    @Autowired
    private TestOneMapper testOneMapper;

    @Override
    public boolean save(TestOneInfo t) {
        try
        {
            return super.save(t);
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    @Override
    public TestOneInfo get(String id) throws Exception {
        try
        {
            return super.getById(id);
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    @Override
    public boolean delete(TestOneInfo t) throws Exception {
        try
        {
            return super.removeById(t.getId());
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    @Override
    public boolean deleteById(String id) throws Exception {
        try
        {
            return super.removeById(id);
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    /**
     * 测试回滚两个保存方法,其中一个方法是另外服务实例的方法
     * @return
     * @throws Exception
     */
    @Override
    public boolean testTx1() throws Exception {
        try
        {
            TestOneInfo one = new TestOneInfo();
            one.setId("1");
            one.setName("TestOne");
            save(one);
            TestTwoInfo two = new TestTwoInfo();
            two.setId("2");
            two.setName("TestTwo");
            testTwoService.save(two);
            int i=1/0;
            return false;
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    /**
     * 测试回滚两个保存方法,其中一个方法是调用另外服务实例中的方法
     * @param t
     * @return
     * @throws Exception
     */
    @Override
    public boolean testTx2(TestOneInfo t) throws Exception {
        try
        {
            save(t);
            TestTwoInfo two = new TestTwoInfo();
            two.setId("2");
            two.setName("TestTwo");
            testTx3(two);
            int i=1/0;
            return false;
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    @Override
    public boolean testTx3(TestTwoInfo t) throws Exception {
        try
        {
            testTwoService.save(t);
            return false;
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    /**
     * 测试回滚两个保存方法,其中一个方法是私有方法
     * @param t
     * @return
     * @throws Exception
     */
    @Override
    public boolean testTx4(TestOneInfo t) throws Exception {
        try
        {
            save(t);
            TestTwoInfo two = new TestTwoInfo();
            two.setId("2");
            two.setName("TestTwo");
            testTx5(two);
            int i=1/0;
            return false;
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    private boolean testTx5(TestTwoInfo t) throws Exception {
        try
        {
            testTwoService.save(t);
            return false;
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    @Override
    public IPage<TestOneInfo> queryName(Integer pageNo, Integer pageSize,
            String name, String orderColumn, String order) throws Exception
    {
        try
        {
            Page<TestOneInfo> p = new Page<TestOneInfo>(pageNo, pageSize);
            QueryWrapper wrapper = new QueryWrapper<TestOneInfo>();
            wrapper.like("name",name);
            return page(p,wrapper);
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

    @Override
    public IPage<TestOneInfo> queryMethod(Integer pageNo, Integer pageSize, String orderColumn, String order, QueryWrapper<TestOneInfo> queryWrapper) throws Exception {
        try
        {
            Page<TestOneInfo> p = new Page<TestOneInfo>(pageNo, pageSize);
            IPage<TestOneInfo> pageList = testOneMapper.queryList(p,queryWrapper);
            return pageList;
        }
        catch(Exception e)
        {
            log.error(e.getMessage(),e);
            throw e;
        }
    }

}


OptimisticLockTest.java

package com.hk.user;

import com.hk.Starter;
import com.hk.entity.TestOneInfo;
import com.hk.service.ITestOneService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.Assert;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = Starter.class)
public class OptimisticLockTest {
    @Autowired
    private ITestOneService testOneServ;

    /**
     * 使用默认数据源,测试Mybatis乐观锁控制
     * @throws Exception
     */
    @Test
    public void testOptimisticLock() throws Exception {
        List<TestOneInfo> lst = testOneServ.list();
        for(TestOneInfo t:lst)
            testOneServ.delete(t);
        TestOneInfo one = new TestOneInfo();
        one.setId("1");
        one.setName("testone1");
        testOneServ.save(one);

        TestOneInfo saveOne = testOneServ.get("1");
        saveOne.setName("testone1-saveonce"+Math.random());
        testOneServ.saveOrUpdate(saveOne);
        //保存一次后检查版本是否变为1
        saveOne = testOneServ.get("1");
        System.out.println("saveOne.version========="+saveOne.getVersion());
        Assert.isTrue(saveOne.getVersion() == 1);

        //保存版本为0的对象,捕捉乐观锁异常
        try {
            //此时one版本为0,应该保存失败
            boolean b = testOneServ.saveOrUpdate(one);
            System.out.println("saveOne.b========="+b);
            Assert.isTrue(b,"数据记录版本冲突");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}
相关推荐
傻啦嘿哟3 小时前
某招聘平台爬虫:爬取招聘岗位数据,分析各城市薪资水平
开发语言·爬虫·python
2501_933670793 小时前
2026秋招量化分析岗技能栈:Python、SQL、统计建模、回测项目怎么准备
开发语言·python·sql
yxlalm4 小时前
Spring AI+RAG 01-项目背景与技术选型
java·人工智能·spring
程序员清风4 小时前
Java 后端高并发设计:线程池、限流、熔断与降级
java·数据库·oracle
李少兄4 小时前
JavaScript 数据类型完全指南
开发语言·javascript·ecmascript
Seoyoneh5 小时前
Agentic Workflow编排架构:云客服从“被动响应”迈向“主动执行”的技术实现
java·开发语言·架构
海南java第二人5 小时前
对外 API 如何保证幂等性?插入数据 / 上传表格场景全方案汇总
java·幂等性
tryxr5 小时前
Chat2Excel 文件服务模块上传文件功能开发
java·项目·oss·easyexcel·文件服务
Tangyuewei5 小时前
Java 写 Agent:模型只是组件
java·开发语言
小玮看世界6 小时前
[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地
开发语言·python