Bläddra i källkod

乐观锁新增测试:自定义类型. 解决动态参数不更新, 支持批量

小锅盖 8 år sedan
förälder
incheckning
2f4cc26009

+ 64 - 46
mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/OptimisticLockerInterceptor.java

@@ -101,7 +101,7 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 		Class<? extends Object> parameterClass = parameterObject.getClass();
 		Class<?> realClass = null;
 		if (parameterObject instanceof ParamMap) {
-			// FIXME 这里还没处理
+			// FIXME
 			ParamMap<?> tt = (ParamMap<?>) parameterObject;
 			realClass = tt.get("param1").getClass();
 		} else if (ProxyFactory.isProxyClass(parameterClass)) {
@@ -112,18 +112,18 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 		VersionCache versionPo = versionCache.get(realClass);
 		if (versionPo != null) {
 			if (versionPo.isVersionControl) {
-				processChangeSql(ms, parameterObject, versionPo);
+				return processChangeSql(ms, parameterObject, versionPo, invocation);
 			}
 		} else {
 			String versionColumn = null;
 			Field versionField = null;
-			for (Field field : realClass.getDeclaredFields()) {
+			for (final Field field : realClass.getDeclaredFields()) {
 				if (field.isAnnotationPresent(Version.class)) {
 					if (!typeHandlers.containsKey(field.getType())) {
 						throw new TypeException("乐观锁不支持" + field.getType().getName() + "类型,请自定义实现");
 					}
 					versionField = field;
-					TableName tableName = field.getAnnotation(TableName.class);
+					final TableName tableName = field.getAnnotation(TableName.class);
 					if (tableName != null) {
 						versionColumn = tableName.value();
 					} else {
@@ -136,62 +136,78 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 				versionField.setAccessible(true);
 				VersionCache cachePo = new VersionCache(true, versionColumn, versionField);
 				versionCache.put(parameterClass, cachePo);
-				processChangeSql(ms, parameterObject, cachePo);
+				return processChangeSql(ms, parameterObject, cachePo, invocation);
 			} else {
 				versionCache.put(parameterClass, new VersionCache(false));
 			}
 		}
 		return invocation.proceed();
+
 	}
 
 	@SuppressWarnings({ "rawtypes", "unchecked" })
-	private void processChangeSql(MappedStatement ms, Object parameterObject, VersionCache versionPo) throws Exception {
+	private Object processChangeSql(MappedStatement ms, Object parameterObject, VersionCache versionPo, Invocation invocation) throws Exception {
 		Field versionField = versionPo.versionField;
 		String versionColumn = versionPo.versionColumn;
 		final Object versionValue = versionField.get(parameterObject);
 		if (versionValue != null) {// 先判断传参是否携带version,没带跳过插件
 			Configuration configuration = ms.getConfiguration();
 			BoundSql originBoundSql = ms.getBoundSql(parameterObject);
-			String originalSql = originBoundSql.getSql();
+			SqlSource originSqlSource = ms.getSqlSource();
+			MetaObject metaObject = SystemMetaObject.forObject(ms);
 			// 解析sql,预处理更新字段没有version字段的情况
-			Update parse = (Update) CCJSqlParserUtil.parse(originalSql);
-			List<Column> columns = parse.getColumns();
-			List<String> columnNames = new ArrayList<String>();
-			for (Column column : columns) {
-				columnNames.add(column.getColumnName());
-			}
-			if (!columnNames.contains(versionColumn)) {
-				columns.add(new Column(versionColumn));
-				parse.setColumns(columns);
-			}
-			// 添加条件
-			BinaryExpression expression = (BinaryExpression) parse.getWhere();
-			if (expression != null && !expression.toString().contains(versionColumn)) {
-				EqualsTo equalsTo = new EqualsTo();
-				equalsTo.setLeftExpression(new Column(versionColumn));
-				Expression rightExpression = new Column("?");
-				equalsTo.setRightExpression(rightExpression);
-				parse.setWhere(new AndExpression(equalsTo, expression));
+			try {
+				Update jsqlSql = (Update) CCJSqlParserUtil.parse(originBoundSql.getSql());
+				List<Column> columns = jsqlSql.getColumns();
+				List<String> columnNames = new ArrayList<String>();
+				for (Column column : columns) {
+					columnNames.add(column.getColumnName());
+				}
+				if (!columnNames.contains(versionColumn)) {
+					columns.add(new Column(versionColumn));
+					jsqlSql.setColumns(columns);
+				}
+				// 添加条件
+				BinaryExpression expression = (BinaryExpression) jsqlSql.getWhere();
+				if (expression != null && !expression.toString().contains(versionColumn)) {
+					EqualsTo equalsTo = new EqualsTo();
+					equalsTo.setLeftExpression(new Column(versionColumn));
+					Expression rightExpression = new Column("?");
+					equalsTo.setRightExpression(rightExpression);
+					jsqlSql.setWhere(new AndExpression(equalsTo, expression));
+				}
+				// 给字段赋新值
+				VersionHandler targetHandler = typeHandlers.get(versionField.getType());
+				targetHandler.plusVersion(parameterObject, versionField, versionValue);
+				// 设置sqlSource
+				List<ParameterMapping> parameterMappings = new LinkedList<ParameterMapping>(originBoundSql.getParameterMappings());
+				parameterMappings.add(jsqlSql.getExpressions().size(), createVersionMapping(configuration));
+				Map<String, Object> additionalParameters = new HashMap<String, Object>();
+				additionalParameters.put("originVersionValue", versionValue);
+				SqlSource sqlSource = new OptimisticLockerSqlSource(configuration, jsqlSql.toString(), parameterMappings, additionalParameters);
+				metaObject.setValue("sqlSource", sqlSource);
+				return invocation.proceed();
+			} catch (Exception e) {
+				throw ExceptionFactory.wrapException("乐观锁插件执行失败", e);
+			} finally {
+				metaObject.setValue("sqlSource", originSqlSource);
 			}
-			// 给字段赋新值
-			VersionHandler targetHandler = typeHandlers.get(versionField.getType());
-			targetHandler.plusVersion(parameterObject, versionField, versionValue);
-			// 生产带动态参数的sqlSource
-			SqlSource sqlSource = getSqlSource(versionValue, configuration, originBoundSql, parse);
-			MetaObject metaObject = SystemMetaObject.forObject(ms);
-			metaObject.setValue("sqlSource", sqlSource);
+
 		}
+		return invocation.proceed();
 	}
 
-	private SqlSource getSqlSource(final Object versionValue, Configuration configuration, BoundSql originBoundSql, Update parse) {
-		// 这一句可以全局一个 TODO
-		ParameterMapping parameterMapping = new ParameterMapping.Builder(configuration, "originVersionValue", new UnknownTypeHandler(configuration.getTypeHandlerRegistry()))
-				.build();
-		List<ParameterMapping> parameterMappings = new LinkedList<ParameterMapping>(originBoundSql.getParameterMappings());
-		parameterMappings.add(parse.getExpressions().size(), parameterMapping);
-		Map<String, Object> additionalParameters = new HashMap<String, Object>();
-		additionalParameters.put("originVersionValue", versionValue);
-		return new MySqlSource(configuration, parse.toString(), parameterMappings, additionalParameters);
+	private ParameterMapping parameterMapping;
+
+	private ParameterMapping createVersionMapping(Configuration configuration) {
+		if (parameterMapping == null) {
+			synchronized (OptimisticLockerInterceptor.class) {
+				if (parameterMapping == null) {
+					parameterMapping = new ParameterMapping.Builder(configuration, "originVersionValue", new UnknownTypeHandler(configuration.getTypeHandlerRegistry())).build();
+				}
+			}
+		}
+		return parameterMapping;
 	}
 
 	public Object plugin(Object target) {
@@ -212,7 +228,6 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 				} catch (Exception e) {
 					throw ExceptionFactory.wrapException("乐观锁插件自定义处理器注册失败", e);
 				}
-
 			}
 		}
 	}
@@ -246,17 +261,20 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 		}
 	}
 
-	private class MySqlSource implements SqlSource {
+	/**
+	 * 乐观锁数据源,主要是为动态参数设计
+	 */
+	private class OptimisticLockerSqlSource implements SqlSource {
 
+		private Configuration configuration;
 		private String sql;
 		private List<ParameterMapping> parameterMappings;
-		private Configuration configuration;
 		private Map<String, Object> additionalParameters;
 
-		public MySqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Map<String, Object> additionalParameters) {
+		public OptimisticLockerSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Map<String, Object> additionalParameters) {
+			this.configuration = configuration;
 			this.sql = sql;
 			this.parameterMappings = parameterMappings;
-			this.configuration = configuration;
 			this.additionalParameters = additionalParameters;
 		}
 

+ 7 - 4
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/CreateDB.sql

@@ -4,10 +4,15 @@ IF EXISTS version_user;
 CREATE TABLE version_user (
 	id bigint (11) NOT NULL ,
 	NAME VARCHAR (20),
+	age INT (11),
 	version INT (11),
 	PRIMARY KEY (`id`)
 ) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
 
+insert into version_user (id,name,version) values(1,"zhangsan",15);
+insert into version_user (id,name,version) values(2,"wangwu",109);
+insert into version_user (id,name,version) values(3,"lisi",null);
+
 DROP TABLE
 IF EXISTS time_version_user;
 
@@ -18,7 +23,5 @@ CREATE TABLE time_version_user (
 	PRIMARY KEY (`id`)
 ) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
 
-insert into version_user (id,name,version) values(1,"zhangsan",15);
-insert into version_user (id,name,version) values(2,"lisi",null);
-insert into version_user (id,name,version) values(3,"wangwu",109);
-
+DROP TABLE
+IF EXISTS string_version_user;

+ 46 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/OptimisticLockerInterceptorTest.java

@@ -21,11 +21,13 @@ import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.DateVersion
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.IntVersionUser;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.LongVersionUser;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.ShortVersionUser;
+import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.StringVersionUser;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.TimestampVersionUser;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.mapper.DateVersionUserMapper;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.mapper.IntVersionUserMapper;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.mapper.LongVersionUserMapper;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.mapper.ShortVersionUserMapper;
+import com.baomidou.mybatisplus.test.plugins.optimisticLocker.mapper.StringVersionUserMapper;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.mapper.TimestampVersionUserMapper;
 
 @RunWith(SpringJUnit4ClassRunner.class)
@@ -42,6 +44,8 @@ public class OptimisticLockerInterceptorTest {
 	@Autowired
 	private TimestampVersionUserMapper timestampVersionUserMapper;
 	@Autowired
+	private StringVersionUserMapper stringersionUserMapper;
+	@Autowired
 	private SqlSessionTemplate sqlSessionTemplate;
 
 	@Before
@@ -76,6 +80,24 @@ public class OptimisticLockerInterceptorTest {
 		versionUser.setName("苗神");
 		intVersionUserMapper.updateById(versionUser);
 		Assert.assertTrue(versionUser.getVersion() == originVersion + 1);
+
+		// 重复测试一次,验证动态参数覆盖
+		// 查询数据
+		IntVersionUser versionUser2 = intVersionUserMapper.selectById(2);
+		Integer originVersion2 = versionUser2.getVersion();
+		versionUser2.setAge(16);
+		// 更新数据
+		versionUser2.setName("苗神");
+		intVersionUserMapper.updateById(versionUser2);
+		Assert.assertTrue(versionUser2.getVersion() == originVersion2 + 1);
+
+		// 测试一次数据库中version为null
+		IntVersionUser versionUser3 = intVersionUserMapper.selectById(3);
+		// 更新数据
+		versionUser3.setName("苗神");
+		intVersionUserMapper.updateById(versionUser3);
+		Assert.assertTrue(versionUser3.getVersion() == null);
+
 	}
 
 	@Test
@@ -93,6 +115,7 @@ public class OptimisticLockerInterceptorTest {
 	public void dateVersionTest() {
 		// 插入数据
 		DateVersionUser versionUser = new DateVersionUser();
+		versionUser.setId(15L);
 		versionUser.setName("苗神");
 		Date originVersion = new Date();
 		versionUser.setVersion(originVersion);
@@ -107,6 +130,7 @@ public class OptimisticLockerInterceptorTest {
 	public void timestampVersionTest() {
 		// 插入数据
 		TimestampVersionUser versionUser = new TimestampVersionUser();
+		versionUser.setId(15L);
 		versionUser.setName("苗神");
 		Timestamp originVersion = new Timestamp(new Date().getTime());
 		versionUser.setVersion(originVersion);
@@ -116,4 +140,26 @@ public class OptimisticLockerInterceptorTest {
 		timestampVersionUserMapper.updateById(versionUser);
 		Assert.assertTrue(versionUser.getVersion().after(originVersion));
 	}
+
+	@Test
+	public void stringVersionTest() {
+		// 查询数据
+		StringVersionUser versionUser = stringersionUserMapper.selectById(1);
+		String originVersion = versionUser.getVersion();
+		// 更新数据
+		versionUser.setName("苗神");
+		stringersionUserMapper.updateById(versionUser);
+		Assert.assertEquals(versionUser.getVersion(), String.valueOf(Long.parseLong(originVersion) + 1));
+	}
+
+	@Test
+	public void multiParamVersionTest() {
+		// 查询数据
+		IntVersionUser versionUser = intVersionUserMapper.selectById(2);
+		Integer originVersion = versionUser.getVersion();
+		// 更新数据
+		versionUser.setName("苗神");
+		intVersionUserMapper.updateById(versionUser);
+		Assert.assertTrue(versionUser.getVersion() == originVersion + 1);
+	}
 }

+ 14 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/StringTypeHandler.java

@@ -0,0 +1,14 @@
+package com.baomidou.mybatisplus.test.plugins.optimisticLocker;
+
+import java.lang.reflect.Field;
+
+import com.baomidou.mybatisplus.plugins.VersionHandler;
+
+public class StringTypeHandler implements VersionHandler<String> {
+
+	public void plusVersion(Object paramObj, Field field, String versionValue) throws Exception {
+		field.set(paramObj, String.valueOf(Long.parseLong(versionValue) + 1));
+
+	}
+
+}

+ 10 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/entity/IntVersionUser.java

@@ -16,6 +16,8 @@ public class IntVersionUser implements Serializable {
 
 	private String name;
 
+	private Integer age;
+
 	@Version
 	private Integer version;
 
@@ -35,6 +37,14 @@ public class IntVersionUser implements Serializable {
 		this.name = name;
 	}
 
+	public Integer getAge() {
+		return age;
+	}
+
+	public void setAge(Integer age) {
+		this.age = age;
+	}
+
 	public Integer getVersion() {
 		return version;
 	}

+ 46 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/entity/StringVersionUser.java

@@ -0,0 +1,46 @@
+package com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity;
+
+import java.io.Serializable;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.annotations.Version;
+
+@TableName("version_user")
+public class StringVersionUser implements Serializable {
+
+	@TableField(exist = false)
+	private static final long serialVersionUID = 1L;
+
+	private Long id;
+
+	private String name;
+
+	@Version
+	private String version;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getVersion() {
+		return version;
+	}
+
+	public void setVersion(String version) {
+		this.version = version;
+	}
+
+}

+ 8 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/mapper/StringVersionUserMapper.java

@@ -0,0 +1,8 @@
+package com.baomidou.mybatisplus.test.plugins.optimisticLocker.mapper;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.StringVersionUser;
+
+public interface StringVersionUserMapper extends BaseMapper<StringVersionUser> {
+
+}

+ 8 - 4
mybatis-plus/src/test/resources/plugins/optimisticLockerInterceptor.xml

@@ -15,14 +15,18 @@
 	<bean id="sqlSessionFactory"
 		class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
 		<property name="dataSource" ref="dataSource" />
-		<property name="configLocation" value="classpath:mybatis-config.xml" />
 		<property name="typeAliasesPackage"
-			value="com.baomidou.mybatisplus.test.mysql.entity;com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity" />
-		<property name="mapperLocations" value="classpath:mysql/*Mapper.xml" />
+			value="com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity" />
 		<property name="plugins">
 			<array>
 				<bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor" />
-				<bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor" />
+				<bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">
+					<property name="properties">
+						<value>
+							versionHandlers=com.baomidou.mybatisplus.test.plugins.optimisticLocker.StringTypeHandler
+						</value>
+					</property>
+				</bean>
 			</array>
 		</property>
 	</bean>