










代码片段
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();
}
}
}