Browse Source

Update Opt Locker

yuxiaobin 8 years ago
parent
commit
3482ccc169
31 changed files with 1691 additions and 466 deletions
  1. 2 2
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/enums/SqlMethod.java
  2. 8 2
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/AutoSqlInjector.java
  3. 2 2
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/BaseMapper.java
  4. 14 6
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/LogicSqlInjector.java
  5. 227 310
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/OptimisticLockerInterceptor.java
  6. 4 1
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/service/impl/ServiceImpl.java
  7. 7 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/GlobalConfigurationTest.java
  8. 119 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserAddrJoinTest.java
  9. 210 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserDateVersionTest.java
  10. 236 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserNoVersionTest.java
  11. 170 5
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java
  12. 13 3
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisPlusConfig.java
  13. 16 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/mapper/H2UserDateVersionMapper.java
  14. 19 1
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/mapper/H2UserMapper.java
  15. 16 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/mapper/H2UserNoVersionMapper.java
  16. 59 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/persistent/H2Addr.java
  17. 184 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/persistent/H2UserDateVersion.java
  18. 170 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/persistent/H2UserNoVersion.java
  19. 8 11
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/service/IH2UserNoVersionService.java
  20. 36 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/service/impl/H2UserNoVersionServiceImpl.java
  21. 14 22
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/OptimisticLockerInterceptorTest.java
  22. 0 13
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/StringTypeHandler.java
  23. 6 0
      mybatis-plus/src/test/resources/h2/addr.ddl.sql
  24. 5 0
      mybatis-plus/src/test/resources/h2/addr.insert.sql
  25. 28 0
      mybatis-plus/src/test/resources/h2/optlock/CreateDB.sql
  26. 35 0
      mybatis-plus/src/test/resources/h2/optlock/mybatis-config.xml
  27. 67 68
      mybatis-plus/src/test/resources/h2/spring-jdbc.xml
  28. 9 8
      mybatis-plus/src/test/resources/h2/spring-test-h2.xml
  29. 4 4
      mybatis-plus/src/test/resources/h2/user.insert.sql
  30. 1 7
      mybatis-plus/src/test/resources/plugins/optimisticLockerInterceptor.xml
  31. 2 1
      mybatis-plus/src/test/resources/properties/jdbc-h2.properties

+ 2 - 2
mybatis-plus/src/main/java/com/baomidou/mybatisplus/enums/SqlMethod.java

@@ -49,8 +49,8 @@ public enum SqlMethod {
     /**
      * 修改
      */
-    UPDATE_BY_ID("updateById", "根据ID 选择修改数据", "<script>UPDATE %s %s WHERE %s=#{%s}</script>"),
-    UPDATE_ALL_COLUMN_BY_ID("updateAllColumnById", "根据ID 修改全部数据", "<script>UPDATE %s %s WHERE %s=#{%s}</script>"),
+    UPDATE_BY_ID("updateById", "根据ID 选择修改数据", "<script>UPDATE %s %s WHERE %s=#{%s} %s</script>"),
+    UPDATE_ALL_COLUMN_BY_ID("updateAllColumnById", "根据ID 修改全部数据", "<script>UPDATE %s %s WHERE %s=#{%s} %s</script>"),
     UPDATE("update", "根据 whereEntity 条件,更新记录", "<script>UPDATE %s %s %s</script>"),
 
     /**

+ 8 - 2
mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/AutoSqlInjector.java

@@ -333,8 +333,14 @@ public class AutoSqlInjector implements ISqlInjector {
      */
     protected void injectUpdateByIdSql(boolean selective, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
         SqlMethod sqlMethod = selective ? SqlMethod.UPDATE_BY_ID : SqlMethod.UPDATE_ALL_COLUMN_BY_ID;
-        String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(selective, table, null), table.getKeyColumn(),
-                table.getKeyProperty());
+        String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(selective, table, "et."), table.getKeyColumn(),
+                "et."+table.getKeyProperty(),
+                "<if test=\"et instanceof java.util.Map\">"+
+                        "<if test=\"et.MP_OPTLOCK_VERSION_ORIGINAL!=null\">"
+                        +"and ${et.MP_OPTLOCK_VERSION_COLUMN}=#{et.MP_OPTLOCK_VERSION_ORIGINAL}"
+                        + "</if>"
+                +"</if>"
+        );
         SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
         this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
     }

+ 2 - 2
mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/BaseMapper.java

@@ -110,7 +110,7 @@ public interface BaseMapper<T> {
      *            实体对象
      * @return int
      */
-    Integer updateById(T entity);
+    Integer updateById(@Param("et") T entity);
 
     /**
      * <p>
@@ -121,7 +121,7 @@ public interface BaseMapper<T> {
      *            实体对象
      * @return int
      */
-    Integer updateAllColumnById(T entity);
+    Integer updateAllColumnById(@Param("et") T entity);
 
     /**
      * <p>

+ 14 - 6
mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/LogicSqlInjector.java

@@ -15,16 +15,17 @@
  */
 package com.baomidou.mybatisplus.mapper;
 
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.scripting.defaults.RawSqlSource;
+
 import com.baomidou.mybatisplus.entity.TableFieldInfo;
 import com.baomidou.mybatisplus.entity.TableInfo;
 import com.baomidou.mybatisplus.enums.SqlMethod;
 import com.baomidou.mybatisplus.toolkit.SqlReservedWords;
 import com.baomidou.mybatisplus.toolkit.StringUtils;
-import org.apache.ibatis.mapping.SqlSource;
-import org.apache.ibatis.scripting.defaults.RawSqlSource;
-
-import java.util.List;
-import java.util.Map;
 
 /**
  * <p>
@@ -148,7 +149,14 @@ public class LogicSqlInjector extends AutoSqlInjector {
 		if (table.isLogicDelete()) {
 			SqlMethod sqlMethod = selective ? SqlMethod.LOGIC_UPDATE_BY_ID : SqlMethod.LOGIC_UPDATE_ALL_COLUMN_BY_ID;
 			String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(selective, table, null),
-					table.getKeyColumn(), table.getKeyProperty(), getLogicDeleteSql(table));
+					table.getKeyColumn(), table.getKeyProperty(),
+					"<if test=\"et instanceof java.util.Map\">"+
+						"<if test=\"et.MP_OPTLOCK_VERSION_ORIGINAL!=null\">"
+							+"and ${et.MP_OPTLOCK_VERSION_COLUMN}=#{et.MP_OPTLOCK_VERSION_ORIGINAL}"
+						+ "</if>"
+					+"</if>"+
+					getLogicDeleteSql(table)
+			);
 			SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
 			this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
 		} else {

+ 227 - 310
mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/OptimisticLockerInterceptor.java

@@ -1,337 +1,254 @@
-/**
- * Copyright (c) 2011-2014, hubin (jobob@qq.com).
- * <p>
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
 package com.baomidou.mybatisplus.plugins;
 
 import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.sql.Statement;
 import java.sql.Timestamp;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.ibatis.binding.MapperMethod.ParamMap;
-import org.apache.ibatis.exceptions.ExceptionFactory;
-import org.apache.ibatis.executor.statement.StatementHandler;
-import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.binding.MapperMethod;
+import org.apache.ibatis.executor.Executor;
 import org.apache.ibatis.mapping.MappedStatement;
-import org.apache.ibatis.mapping.ParameterMapping;
 import org.apache.ibatis.mapping.SqlCommandType;
 import org.apache.ibatis.plugin.Interceptor;
 import org.apache.ibatis.plugin.Intercepts;
 import org.apache.ibatis.plugin.Invocation;
 import org.apache.ibatis.plugin.Plugin;
 import org.apache.ibatis.plugin.Signature;
-import org.apache.ibatis.reflection.MetaObject;
-import org.apache.ibatis.reflection.SystemMetaObject;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.defaults.DefaultSqlSession;
-import org.apache.ibatis.type.TypeException;
-import org.apache.ibatis.type.UnknownTypeHandler;
 
-import com.baomidou.mybatisplus.annotations.TableField;
 import com.baomidou.mybatisplus.annotations.Version;
-import com.baomidou.mybatisplus.mapper.EntityWrapper;
-import com.baomidou.mybatisplus.toolkit.PluginUtils;
-import com.baomidou.mybatisplus.toolkit.StringUtils;
-
-import net.sf.jsqlparser.expression.BinaryExpression;
-import net.sf.jsqlparser.expression.Expression;
-import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
-import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
-import net.sf.jsqlparser.parser.CCJSqlParserUtil;
-import net.sf.jsqlparser.schema.Column;
-import net.sf.jsqlparser.statement.update.Update;
+import com.baomidou.mybatisplus.entity.TableFieldInfo;
+import com.baomidou.mybatisplus.entity.TableInfo;
+import com.baomidou.mybatisplus.mapper.Wrapper;
+import com.baomidou.mybatisplus.toolkit.ReflectionKit;
+import com.baomidou.mybatisplus.toolkit.TableInfoHelper;
 
 /**
  * <p>
- * MyBatis乐观锁插件
+ * Optimistic Lock Light version<BR>
+ *     Intercept on {@link Executor}.update;<BR>
+ *     Support version types: int/Integer, long/Long, java.util.Date, java.sql.Timestamp<BR>
+ *     For extra types, please define a subclass and override {@code getUpdatedVersionVal}() method.<BR>
+ * <BR>
+ * How to use?<BR>
+ *     (1) Define an Entity and add {@link Version} annotation on one entity field.<BR>
+ *     (2) Add {@link OptimisticLockerInterceptor} into mybatis plugin.
+ *
+ * How to work?<BR>
+ *     if update entity with version column=1:<BR>
+ *         (1) no {@link OptimisticLockerInterceptor}:<BR>
+ *             SQL: update tbl_test set name='abc' where id=100001;
+ *         (2) add {@link OptimisticLockerInterceptor}:<BR>
+ *             SQL: update tbl_test set name='abc',version=2 where id=100001 and version=1;
  * </p>
- * 
- * <pre>
- * 之前:update user set name = ?, password = ? where id = ?
- * 之后:update user set name = ?, password = ?, version = version+1 where id = ? and version = ?
- * 对象上的version字段上添加{@link Version}注解
- * sql可以不需要写version字段,只要对象version有值就会更新
- * 支持,int Integer, long Long, Date,Timestamp
- * 其他类型可以自定义实现,注入versionHandlers,多个以逗号分隔
- * </pre>
  *
- * @author TaoYu 小锅盖 tantan
- * @since 2017-04-08
+ * @author yuxiaobin
+ * @date 2017/5/24
  */
 @Intercepts({
-		@Signature(type = StatementHandler.class, method = "prepare", args = {MappedStatement.class, Object.class}),
-		@Signature(type = StatementHandler.class, method = "parameterize", args = { Statement.class })  })
-public final class OptimisticLockerInterceptor implements Interceptor {
-
-	/**
-	 * 根据对象类型缓存version基本信息
-	 */
-	private static final Map<Class<?>, LockerCache> versionCache = new ConcurrentHashMap<>();
-
-	/**
-	 * 根据version字段类型缓存的处理器
-	 */
-	private static final Map<Type, VersionHandler<?>> typeHandlers = new HashMap<>();
-
-	private static final Expression RIGHT_EXPRESSION = new Column("?");
-
-	static {
-		IntegerTypeHandler integerTypeHandler = new IntegerTypeHandler();
-		typeHandlers.put(int.class, integerTypeHandler);
-		typeHandlers.put(Integer.class, integerTypeHandler);
-
-		LongTypeHandler longTypeHandler = new LongTypeHandler();
-		typeHandlers.put(long.class, longTypeHandler);
-		typeHandlers.put(Long.class, longTypeHandler);
-
-		typeHandlers.put(Date.class, new DateTypeHandler());
-		typeHandlers.put(Timestamp.class, new TimestampTypeHandler());
-	}
-
-	public Object intercept(Invocation invocation) throws Exception {
-		//判断是prepare还是parameterize
-		//prepare的时候变更update 条件
-		//parameterize的时候赋值
-		Method m = invocation.getMethod();
-		String methodName ="";
-		if ("prepare".equals(m.getName())) {
-			methodName="prepare";
-		} else if ("parameterize".equals(m.getName())) {
-			methodName="parameterize";
-		}
-		StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
-		MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
-		// 先判断是不是真正的UPDATE操作
-		MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
-		if (!ms.getSqlCommandType().equals(SqlCommandType.UPDATE)) {
-			return invocation.proceed();
-		}
-		BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
-		// 获得参数类型,去缓存中快速判断是否有version注解才继续执行
-		Class<?> parameterClass = ms.getParameterMap().getType();
-		LockerCache lockerCache = versionCache.get(parameterClass);
-		if (lockerCache != null) {
-			if (lockerCache.lock) {
-				processChangeSql(ms, boundSql, lockerCache,methodName);
-			}
-		} else {
-			Field versionField = getVersionField(parameterClass);
-			if (versionField != null) {
-				Class<?> fieldType = versionField.getType();
-				if (!typeHandlers.containsKey(fieldType)) {
-					throw new TypeException("乐观锁不支持" + fieldType.getName() + "类型,请自定义实现");
-				}
-				final TableField tableField = versionField.getAnnotation(TableField.class);
-				String versionColumn = versionField.getName();
-				if (tableField != null) {
-					versionColumn = tableField.value();
-				}
-				LockerCache lc = new LockerCache(true, versionColumn, versionField, typeHandlers.get(fieldType));
-				versionCache.put(parameterClass, lc);
-				processChangeSql(ms, boundSql, lc,methodName);
-			} else {
-				versionCache.put(parameterClass, LockerCache.INSTANCE);
-			}
-		}
-		return invocation.proceed();
-
-	}
-
-	private Field getVersionField(Class<?> parameterClass) {
-		if (parameterClass != Object.class) {
-			for (Field field : parameterClass.getDeclaredFields()) {
-				if (field.isAnnotationPresent(Version.class)) {
-					field.setAccessible(true);
-					return field;
-				}
-			}
-			return getVersionField(parameterClass.getSuperclass());
-		}
-		return null;
-
-	}
-
-	private void processChangeSql(MappedStatement ms, BoundSql boundSql, LockerCache lockerCache,String methodName) throws Exception {
-		Object parameterObject = boundSql.getParameterObject();
-		if (parameterObject instanceof ParamMap) {
-			ParamMap<?> paramMap = (ParamMap<?>) parameterObject;
-			parameterObject = paramMap.get("et");
-			EntityWrapper<?> entityWrapper = (EntityWrapper<?>) paramMap.get("ew");
-			if (entityWrapper != null) {
-				Object entity = entityWrapper.getEntity();
-				if (entity != null && lockerCache.field.get(entity) == null) {
-					changSql(ms, boundSql, parameterObject, lockerCache,methodName);
-				}
-			}
-		} else if(!(parameterObject instanceof DefaultSqlSession.StrictMap)) {
-			//如果是有逻辑删,且DELETE传入字段为ID或IDS的话,MYBATIS就会使用StrictMap
-			//这里是判断如量不为ParamMap县城不为StrictMap类型就进行乐观锁
-			changSql(ms, boundSql, parameterObject, lockerCache,methodName);
-		}
-	}
-
-	@SuppressWarnings("unchecked")
-	private void changSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, LockerCache lockerCache,String methodName)
-			throws Exception {
-		Field versionField = lockerCache.field;
-		String versionColumn = lockerCache.column;
-		final Object versionValue = versionField.get(parameterObject);
-		if (versionValue != null) {// 先判断传参是否携带version,没带跳过插件
-			Configuration configuration = ms.getConfiguration();
-
-			// 处理where条件,添加?
-			Update jsqlSql = (Update) CCJSqlParserUtil.parse(boundSql.getSql());
-			BinaryExpression expression = (BinaryExpression) jsqlSql.getWhere();
-			if (expression != null && !expression.toString().contains(versionColumn)) {
-				EqualsTo equalsTo = new EqualsTo();
-				equalsTo.setLeftExpression(new Column(versionColumn));
-				equalsTo.setRightExpression(RIGHT_EXPRESSION);
-				jsqlSql.setWhere(new AndExpression(equalsTo, expression));
-				List<ParameterMapping> parameterMappings = new LinkedList<>(boundSql.getParameterMappings());
-				parameterMappings.add(jsqlSql.getExpressions().size(), getVersionMappingInstance(configuration));
-				MetaObject boundSqlMeta = configuration.newMetaObject(boundSql);
-				boundSqlMeta.setValue("sql", jsqlSql.toString());
-				boundSqlMeta.setValue("parameterMappings", parameterMappings);
-			}
-			//只有parameterize才给字段赋值
-			if("parameterize".equals(methodName)) {
-				// 给字段赋新值
-				lockerCache.versionHandler.plusVersion(parameterObject, versionField, versionValue);
-				// 设置参数
-				boundSql.setAdditionalParameter("originVersionValue", versionValue);
-			}
-		}
-	}
-	
-	private volatile ParameterMapping parameterMapping;
-	
-	private ParameterMapping getVersionMappingInstance(Configuration configuration) {
-		if (parameterMapping == null) {
-			synchronized (OptimisticLockerInterceptor.class) {
-				if (parameterMapping == null) {
-					parameterMapping = new ParameterMapping.Builder(configuration, "originVersionValue",
-							new UnknownTypeHandler(configuration.getTypeHandlerRegistry())).build();
-				}
-			}
-		}
-		return parameterMapping;
-	}
-
-	@Override
-	public Object plugin(Object target) {
-		if (target instanceof StatementHandler) {
-			return Plugin.wrap(target, this);
-		}
-		return target;
-	}
-
-	@Override
-	public void setProperties(Properties properties) {
-		String versionHandlers = properties.getProperty("versionHandlers");
-		if (StringUtils.isNotEmpty(versionHandlers)) {
-			for (String handlerClazz : versionHandlers.split(",")) {
-				try {
-					registerHandler(Class.forName(handlerClazz));
-				} catch (Exception e) {
-					throw ExceptionFactory.wrapException("乐观锁插件自定义处理器注册失败", e);
-				}
-			}
-		}
-	}
-
-	/**
-	 * 注册处理器
-	 */
-	private static void registerHandler(Class<?> versionHandlerClazz) throws Exception {
-		ParameterizedType parameterizedType = (ParameterizedType) versionHandlerClazz.getGenericInterfaces()[0];
-		Object versionInstance = versionHandlerClazz.newInstance();
-		if (!(versionInstance instanceof VersionHandler)) {
-			throw new TypeException("参数未实现VersionHandler,不能注入");
-		} else {
-			Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
-			if (actualTypeArguments.length == 0) {
-				throw new IllegalArgumentException("处理器泛型未定义");
-			} else if (Object.class.equals(actualTypeArguments[0])) {
-				throw new IllegalArgumentException("处理器泛型不能为Object");
-			} else {
-				typeHandlers.put(actualTypeArguments[0], (VersionHandler<?>) versionInstance);
-			}
-		}
-	}
-
-	// *****************************基本类型处理器*****************************
-	private static class IntegerTypeHandler implements VersionHandler<Integer> {
-
-		public void plusVersion(Object paramObj, Field field, Integer versionValue) throws Exception {
-			field.set(paramObj, versionValue + 1);
-		}
-	}
-
-	private static class LongTypeHandler implements VersionHandler<Long> {
-
-		public void plusVersion(Object paramObj, Field field, Long versionValue) throws Exception {
-			field.set(paramObj, versionValue + 1);
-		}
-	}
-
-	// ***************************** 时间类型处理器*****************************
-	private static class DateTypeHandler implements VersionHandler<Date> {
-
-		public void plusVersion(Object paramObj, Field field, Date versionValue) throws Exception {
-			field.set(paramObj, new Date());
-		}
-	}
-
-	private static class TimestampTypeHandler implements VersionHandler<Timestamp> {
-
-		public void plusVersion(Object paramObj, Field field, Timestamp versionValue) throws Exception {
-			field.set(paramObj, new Timestamp(new Date().getTime()));
-		}
-	}
-
-	/**
-	 * 缓存对象
-	 */
-	@SuppressWarnings("rawtypes")
-	private static class LockerCache {
-
-		public static final LockerCache INSTANCE = new LockerCache();
-
-		private boolean lock;
-		private String column;
-		private Field field;
-		private VersionHandler versionHandler;
-
-		public LockerCache() {
-		}
-
-		LockerCache(Boolean lock, String column, Field field, VersionHandler versionHandler) {
-			this.lock = lock;
-			this.column = column;
-			this.field = field;
-			this.versionHandler = versionHandler;
-		}
-	}
+        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
+public class OptimisticLockerInterceptor implements Interceptor {
+
+    private final Map<Class<?>, EntityField> versionFieldCache = new HashMap<>();
+    private final Map<Class<?>, List<EntityField>> entityFieldsCache = new HashMap<>();
+
+    @Override
+    public Object intercept(Invocation invocation) throws Throwable {
+        Object[] args = invocation.getArgs();
+        MappedStatement ms = (MappedStatement) args[0];
+        if(SqlCommandType.UPDATE.compareTo(ms.getSqlCommandType())!=0){
+            return invocation.proceed();
+        }
+        Object param = args[1];
+        if(param instanceof MapperMethod.ParamMap){
+            MapperMethod.ParamMap map = (MapperMethod.ParamMap) param;
+            Wrapper ew = null;
+            if(map.containsKey("ew")){
+                //mapper.update(updEntity, EntityWrapper<>(whereEntity);
+                ew = (Wrapper) map.get("ew");
+            }//else updateById(entity) -->> change updateById(entity) to updateById(@Param("et") entity)
+            Object et = map.get("et");
+            if(ew!=null){
+                Object entity = ew.getEntity();
+                if(entity!=null){
+                    EntityField ef = getVersionField(entity.getClass());
+                    Field versionField = ef==null?null:ef.getField();
+                    if (versionField != null) {
+                        Object originalVersionVal = versionField.get(entity);
+                        if(originalVersionVal!=null){
+                            versionField.set(et, getUpdatedVersionVal(originalVersionVal));
+                        }
+                    }
+                }
+            }else{
+                EntityField entityField = getVersionField(et.getClass());
+                Field versionField = entityField==null?null:entityField.getField();
+                if(versionField!=null) {
+                    Object originalVersionVal = versionField.get(et);
+                    if (originalVersionVal != null) {
+                        TableInfo tableInfo = TableInfoHelper.getTableInfo(et.getClass());
+                        Map<String,Object> entityMap = new HashMap<>();
+                        List<EntityField> fields = getEntityFields(et.getClass());
+                        for(EntityField ef : fields){
+                            Field fd = ef.getField();
+                            if(fd.isAccessible()) {
+                                entityMap.put(fd.getName(), fd.get(et));
+                                if (ef.isVersion()) {
+                                    versionField = fd;
+                                }
+                            }
+                        }
+                        String versionPropertyName = versionField.getName();
+                        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
+                        String versionColumnName = entityField.getColumnName();
+                        if(versionColumnName==null) {
+                            for (TableFieldInfo tf : fieldList) {
+                                if (versionPropertyName.equals(tf.getProperty())) {
+                                    versionColumnName = tf.getColumn();
+                                }
+                            }
+                        }
+                        if (versionColumnName != null) {
+                            entityField.setColumnName(versionColumnName);
+                            entityMap.put(versionField.getName(), getUpdatedVersionVal(originalVersionVal));
+                            entityMap.put("MP_OPTLOCK_VERSION_ORIGINAL", originalVersionVal);
+                            entityMap.put("MP_OPTLOCK_VERSION_COLUMN", versionColumnName);
+                            map.put("et", entityMap);
+                        }
+                    }
+                }
+            }
+        }
+        return invocation.proceed();
+    }
+
+    /**
+     * This method provides the control for version value.<BR>
+     * Returned value type must be the same as original one.
+     *
+     * @param originalVersionVal
+     * @return updated version val
+     */
+    protected Object getUpdatedVersionVal(Object originalVersionVal){
+        Class<?> versionValClass = originalVersionVal.getClass();
+        if(long.class.equals(versionValClass)){
+            return ((long)originalVersionVal)+1;
+        }else if(Long.class.equals(versionValClass)){
+            return ((Long)originalVersionVal)+1;
+        }else if(int.class.equals(versionValClass)){
+            return ((int)originalVersionVal)+1;
+        }else if(Integer.class.equals(versionValClass)){
+            return ((Integer)originalVersionVal)+1;
+        }else if(Date.class.equals(versionValClass)){
+            return new Date();
+        }else if(Timestamp.class.equals(versionValClass)){
+            return new Timestamp(System.currentTimeMillis());
+        }else{
+            return originalVersionVal;//not supported type, return original val.
+        }
+    }
+
+    @Override
+    public Object plugin(Object target) {
+        if (target instanceof Executor) {
+            return Plugin.wrap(target, this);
+        }
+        return target;
+    }
+
+    @Override
+    public void setProperties(Properties properties) {
+
+    }
+
+    private EntityField getVersionField(Class<?> parameterClass) {
+        synchronized (parameterClass.getName()) {
+            if (versionFieldCache.containsKey(parameterClass)) {
+                return versionFieldCache.get(parameterClass);
+            }else{
+                EntityField field = getVersionFieldRegular(parameterClass);
+                versionFieldCache.put(parameterClass, field);
+                return field;
+            }
+        }
+    }
+
+    private EntityField getVersionFieldRegular(Class<?> parameterClass){
+        if (parameterClass != Object.class) {
+            for (Field field : parameterClass.getDeclaredFields()) {
+                if (field.isAnnotationPresent(Version.class)) {
+                    field.setAccessible(true);
+                    return new EntityField(field, true);
+                }
+            }
+            return getVersionFieldRegular(parameterClass.getSuperclass());
+        }
+        return null;
+    }
+
+    private List<EntityField> getEntityFields(Class<?> parameterClass){
+        if(entityFieldsCache.containsKey(parameterClass)){
+            return entityFieldsCache.get(parameterClass);
+        }else{
+            List<EntityField> fields = getFieldsFromClazz(parameterClass, null);
+            entityFieldsCache.put(parameterClass, fields);
+            return fields;
+        }
+    }
+
+    private List<EntityField> getFieldsFromClazz(Class<?> parameterClass, List<EntityField> fieldList){
+        if(fieldList==null){
+            fieldList = new ArrayList<>();
+        }
+        List<Field> fields = ReflectionKit.getFieldList(parameterClass);
+        for(Field field:fields){
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(Version.class)) {
+                fieldList.add(new EntityField(field, true));
+            }else{
+                fieldList.add(new EntityField(field, false));
+            }
+        }
+        return fieldList;
+    }
+
+}
+class EntityField{
+
+    private Field field;
+    private boolean version;
+    private String columnName;
+
+    public EntityField(Field field, boolean version) {
+        this.field = field;
+        this.version = version;
+    }
+
+    public Field getField() {
+        return field;
+    }
+
+    public void setField(Field field) {
+        this.field = field;
+    }
+
+    public boolean isVersion() {
+        return version;
+    }
+
+    public void setVersion(boolean version) {
+        this.version = version;
+    }
+
+    public String getColumnName() {
+        return columnName;
+    }
+
+    public void setColumnName(String columnName) {
+        this.columnName = columnName;
+    }
+}
 
-}

+ 4 - 1
mybatis-plus/src/main/java/com/baomidou/mybatisplus/service/impl/ServiceImpl.java

@@ -19,6 +19,7 @@ import java.io.Serializable;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.ibatis.binding.MapperMethod;
 import org.apache.ibatis.logging.Log;
 import org.apache.ibatis.logging.LogFactory;
 import org.apache.ibatis.session.SqlSession;
@@ -247,7 +248,9 @@ public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
             int size = entityList.size();
             String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
             for (int i = 0; i < size; i++) {
-                batchSqlSession.update(sqlStatement, entityList.get(i));
+                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
+                param.put("et",entityList.get(i));
+                batchSqlSession.update(sqlStatement, param);
                 if (i >= 1 && i % batchSize == 0) {
                     batchSqlSession.flushStatements();
                 }

+ 7 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/GlobalConfigurationTest.java

@@ -98,4 +98,11 @@ public class GlobalConfigurationTest {
         session.rollback();
         sqlSession.commit();
     }
+
+
+    @org.junit.Test
+    public void testStringFormat(){
+        String str = "'%s'";
+        System.out.println(String.format(str,"abc"));
+    }
 }

+ 119 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserAddrJoinTest.java

@@ -0,0 +1,119 @@
+package com.baomidou.mybatisplus.test.h2;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
+import javax.sql.DataSource;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.baomidou.mybatisplus.plugins.Page;
+import com.baomidou.mybatisplus.test.h2.entity.mapper.H2UserMapper;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2Addr;
+
+/**
+ * <p>
+ * Mybatis Plus H2 Junit Test
+ * </p>
+ *
+ * @author Caratacus
+ * @date 2017/4/1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"})
+public class H2UserAddrJoinTest {
+
+    @Autowired
+    private H2UserMapper userMapper;
+
+    @BeforeClass
+    public static void initDB() throws SQLException, IOException {
+        @SuppressWarnings("resource")
+        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:h2/spring-test-h2.xml");
+        DataSource ds = (DataSource) context.getBean("dataSource");
+        try (Connection conn = ds.getConnection()) {
+            Statement stmt = conn.createStatement();
+            stmt.execute(readFile("user.ddl.sql"));
+            stmt.execute("truncate table h2user");
+            stmt.execute(readFile("addr.ddl.sql"));
+            stmt.execute("truncate table h2address");
+            insertUsers(stmt);
+            insertAddr(stmt);
+            conn.commit();
+        }
+    }
+
+    private static void insertUsers(Statement stmt) throws SQLException, IOException {
+        String filename = "user.insert.sql";
+        String filePath = H2UserAddrJoinTest.class.getClassLoader().getResource("").getPath() + "/h2/" + filename;
+        try (
+                BufferedReader reader = new BufferedReader(new FileReader(filePath))
+        ) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if(line.isEmpty()){
+                    continue;
+                }
+                stmt.execute(line.replace(";", ""));
+            }
+        }
+    }
+    private static void insertAddr(Statement stmt) throws SQLException, IOException {
+        String filename = "addr.insert.sql";
+        String filePath = H2UserAddrJoinTest.class.getClassLoader().getResource("").getPath() + "/h2/" + filename;
+        try (
+                BufferedReader reader = new BufferedReader(new FileReader(filePath))
+        ) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if(line.isEmpty()){
+                    continue;
+                }
+                stmt.execute(line.replace(";", ""));
+            }
+        }
+    }
+
+    private static String readFile(String filename) {
+        StringBuilder builder = new StringBuilder();
+        String filePath = H2UserAddrJoinTest.class.getClassLoader().getResource("").getPath() + "/h2/" + filename;
+        try (
+                BufferedReader reader = new BufferedReader(new FileReader(filePath))
+        ) {
+            String line;
+            while ((line = reader.readLine()) != null)
+                builder.append(line).append(" ");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return builder.toString();
+    }
+
+
+    @Test
+    public void testJoinTableWithoutPagination(){
+        List<H2Addr> addrList = userMapper.getAddrListByUserId(101L);
+        Assert.assertEquals(5, addrList.size());
+    }
+    @Test
+    public void testJoinTableWithPagination(){
+        List<H2Addr> addrList = userMapper.getAddrListByUserId(101L, new Page<H2Addr>(0,3));
+        Assert.assertEquals(3, addrList.size());
+    }
+
+
+
+}

+ 210 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserDateVersionTest.java

@@ -0,0 +1,210 @@
+package com.baomidou.mybatisplus.test.h2;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.sql.DataSource;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.baomidou.mybatisplus.test.h2.entity.mapper.H2UserDateVersionMapper;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2UserDateVersion;
+
+/**
+ * <p>
+ * Mybatis Plus H2 Junit Test
+ * </p>
+ *
+ * @author Caratacus
+ * @date 2017/4/1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"})
+public class H2UserDateVersionTest {
+
+    @Autowired
+    private H2UserDateVersionMapper userMapper;
+
+    @BeforeClass
+    public static void initDB() throws SQLException, IOException {
+        @SuppressWarnings("resource")
+        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:h2/spring-test-h2.xml");
+        DataSource ds = (DataSource) context.getBean("dataSource");
+        try (Connection conn = ds.getConnection()) {
+            String createTableSql = readFile("user.ddl.sql");
+            Statement stmt = conn.createStatement();
+            stmt.execute(createTableSql);
+            stmt.execute("truncate table h2user");
+            insertUsers(stmt);
+            conn.commit();
+        }
+    }
+
+    private static void insertUsers(Statement stmt) throws SQLException, IOException {
+        String filename = "user.insert.sql";
+        String filePath = H2UserDateVersionTest.class.getClassLoader().getResource("").getPath() + "/h2/" + filename;
+        try (
+                BufferedReader reader = new BufferedReader(new FileReader(filePath))
+        ) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                stmt.execute(line.replace(";", ""));
+            }
+        }
+    }
+
+    private static String readFile(String filename) {
+        StringBuilder builder = new StringBuilder();
+        String filePath = H2UserDateVersionTest.class.getClassLoader().getResource("").getPath() + "/h2/" + filename;
+        try (
+                BufferedReader reader = new BufferedReader(new FileReader(filePath))
+        ) {
+            String line;
+            while ((line = reader.readLine()) != null)
+                builder.append(line).append(" ");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return builder.toString();
+    }
+
+
+    @Test
+    public void testUpdateByIdNoDateVersion(){
+        Long id = 991L;
+        H2UserDateVersion user = new H2UserDateVersion();
+        user.setId(id);
+        user.setName("991");
+        user.setAge(91);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userMapper.insertAllColumn(user);
+
+        H2UserDateVersion userDB = userMapper.selectById(id);
+        Assert.assertEquals(null, userDB.getTestDate());
+
+        userDB.setName("991");
+        userMapper.updateById(userDB);
+
+        userDB = userMapper.selectById(id);
+        Assert.assertEquals("991", userDB.getName());
+    }
+
+
+    @Test
+    public void testUpdateByEntityWrapperNoDateVersion(){
+        Long id = 992L;
+        H2UserDateVersion user = new H2UserDateVersion();
+        user.setId(id);
+        user.setName("992");
+        user.setAge(92);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userMapper.insertAllColumn(user);
+
+        H2UserDateVersion userDB = userMapper.selectById(id);
+
+        H2UserDateVersion updUser = new H2UserDateVersion();
+        updUser.setName("999");
+
+        userMapper.update(updUser, new EntityWrapper<>(userDB));
+
+        userDB = userMapper.selectById(id);
+        Assert.assertEquals("999", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateByIdWithDateVersion(){
+        Long id = 994L;
+        H2UserDateVersion user = new H2UserDateVersion();
+        user.setId(id);
+        user.setName("994");
+        user.setAge(91);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        Calendar cal = Calendar.getInstance();
+        cal.add(Calendar.DAY_OF_MONTH,-1);
+        user.setTestDate(cal.getTime());
+        userMapper.insertAllColumn(user);
+
+        System.out.println("before update: testDate="+user.getTestDate());
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH");
+        H2UserDateVersion userDB = userMapper.selectById(id);
+
+        Assert.assertNotNull(userDB.getTestDate());
+        String originalDateVersionStr =sdf.format(cal.getTime());
+        Assert.assertEquals(originalDateVersionStr, sdf.format(userDB.getTestDate()));
+
+        userDB.setName("991");
+        userMapper.updateById(userDB);
+        userDB = userMapper.selectById(id);
+        Assert.assertEquals("991", userDB.getName());
+        Date versionDate = userDB.getTestDate();
+        System.out.println("after update: testDate="+versionDate);
+        String versionDateStr = sdf.format(versionDate);
+        Assert.assertEquals(sdf.format(new Date()), versionDateStr);
+
+        Assert.assertNotEquals(originalDateVersionStr, versionDateStr);
+
+    }
+
+    @Test
+    public void testUpdateByEntityWrapperWithDateVersion(){
+        Long id = 993L;
+        H2UserDateVersion user = new H2UserDateVersion();
+        user.setId(id);
+        user.setName("992");
+        user.setAge(92);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        Calendar cal = Calendar.getInstance();
+        cal.add(Calendar.DAY_OF_MONTH,-1);
+        user.setTestDate(cal.getTime());
+        userMapper.insertAllColumn(user);
+
+        H2UserDateVersion userDB = userMapper.selectById(id);
+
+        H2UserDateVersion updUser = new H2UserDateVersion();
+        updUser.setName("999");
+        userDB.setVersion(null);
+        userMapper.update(updUser, new EntityWrapper<>(userDB));
+
+        System.out.println("before update: testDate="+userDB.getTestDate());
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH");
+
+        userDB = userMapper.selectById(id);
+        Assert.assertEquals("999", userDB.getName());
+
+        Date versionDate = userDB.getTestDate();
+        System.out.println("after update: testDate="+versionDate);
+        String versionDateStr = sdf.format(versionDate);
+        Assert.assertEquals(sdf.format(new Date()), versionDateStr);
+    }
+
+
+}

+ 236 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserNoVersionTest.java

@@ -0,0 +1,236 @@
+package com.baomidou.mybatisplus.test.h2;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.baomidou.mybatisplus.plugins.Page;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2UserNoVersion;
+import com.baomidou.mybatisplus.test.h2.entity.service.IH2UserNoVersionService;
+
+/**
+ * <p>
+ * Mybatis Plus H2 Junit Test
+ * </p>
+ *
+ * @author Caratacus
+ * @date 2017/4/1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"})
+public class H2UserNoVersionTest {
+
+    @Autowired
+    private IH2UserNoVersionService userService;
+
+    @BeforeClass
+    public static void initDB() throws SQLException, IOException {
+        @SuppressWarnings("resource")
+        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:h2/spring-test-h2.xml");
+        DataSource ds = (DataSource) context.getBean("dataSource");
+        try (Connection conn = ds.getConnection()) {
+            String createTableSql = readFile("user.ddl.sql");
+            Statement stmt = conn.createStatement();
+            stmt.execute(createTableSql);
+            stmt.execute("truncate table h2user");
+            insertUsers(stmt);
+            conn.commit();
+        }
+    }
+
+    private static void insertUsers(Statement stmt) throws SQLException, IOException {
+        String filename = "user.insert.sql";
+        String filePath = H2UserNoVersionTest.class.getClassLoader().getResource("").getPath() + "/h2/" + filename;
+        try (
+                BufferedReader reader = new BufferedReader(new FileReader(filePath))
+        ) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                stmt.execute(line.replace(";", ""));
+            }
+        }
+    }
+
+    private static String readFile(String filename) {
+        StringBuilder builder = new StringBuilder();
+        String filePath = H2UserNoVersionTest.class.getClassLoader().getResource("").getPath() + "/h2/" + filename;
+        try (
+                BufferedReader reader = new BufferedReader(new FileReader(filePath))
+        ) {
+            String line;
+            while ((line = reader.readLine()) != null)
+                builder.append(line).append(" ");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return builder.toString();
+    }
+
+    @Test
+    public void testInsert() {
+        H2UserNoVersion user = new H2UserNoVersion();
+        user.setAge(1);
+        user.setPrice(new BigDecimal("9.99"));
+        userService.insert(user);
+        Assert.assertNotNull(user.getId());
+        user.setDesc("Caratacus");
+        userService.insertOrUpdate(user);
+        H2UserNoVersion userFromDB = userService.selectById(user.getId());
+        Assert.assertEquals("Caratacus", userFromDB.getDesc());
+    }
+
+    @Test
+    public void testDelete() {
+        H2UserNoVersion user = new H2UserNoVersion();
+        user.setAge(1);
+        user.setPrice(new BigDecimal("9.99"));
+        userService.insert(user);
+        Long userId = user.getId();
+        Assert.assertNotNull(userId);
+        userService.deleteById(userId);
+        Assert.assertNull(userService.selectById(userId));
+    }
+
+    @Test
+    public void testSelectByid() {
+        Long userId = 101L;
+        Assert.assertNotNull(userService.selectById(userId));
+    }
+
+    @Test
+    public void testSelectOne() {
+        H2UserNoVersion user = new H2UserNoVersion();
+        user.setId(105L);
+        EntityWrapper<H2UserNoVersion> ew = new EntityWrapper<>(user);
+        H2UserNoVersion userFromDB = userService.selectOne(ew);
+        Assert.assertNotNull(userFromDB);
+    }
+
+    @Test
+    public void testSelectList() {
+        H2UserNoVersion user = new H2UserNoVersion();
+        EntityWrapper<H2UserNoVersion> ew = new EntityWrapper<>(user);
+        List<H2UserNoVersion> list = userService.selectList(ew);
+        Assert.assertNotNull(list);
+        Assert.assertNotEquals(0, list.size());
+    }
+
+    @Test
+    public void testSelectPage() {
+        Page<H2UserNoVersion> page = userService.selectPage(new Page<H2UserNoVersion>(1, 3));
+        Assert.assertEquals(3, page.getRecords().size());
+    }
+
+    @Test
+    public void testUpdateById(){
+        Long id = 991L;
+        H2UserNoVersion user = new H2UserNoVersion();
+        user.setId(id);
+        user.setName("991");
+        user.setAge(91);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userService.insertAllColumn(user);
+
+        H2UserNoVersion userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+
+        userDB.setName("991");
+        userService.updateById(userDB);
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+        Assert.assertEquals("991", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateByEntityWrapper(){
+        Long id = 992L;
+        H2UserNoVersion user = new H2UserNoVersion();
+        user.setId(id);
+        user.setName("992");
+        user.setAge(92);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userService.insertAllColumn(user);
+
+        H2UserNoVersion userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+
+        H2UserNoVersion updUser = new H2UserNoVersion();
+        updUser.setName("999");
+
+        userService.update(updUser, new EntityWrapper<>(userDB));
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+        Assert.assertEquals("999", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateByEntityWrapper2(){
+        Long id = 993L;
+        H2UserNoVersion user = new H2UserNoVersion();
+        user.setId(id);
+        user.setName("992");
+        user.setAge(92);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userService.insertAllColumn(user);
+
+        H2UserNoVersion userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+
+        H2UserNoVersion updUser = new H2UserNoVersion();
+        updUser.setName("999");
+        userDB.setVersion(null);
+        userService.update(updUser, new EntityWrapper<>(userDB));
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+        Assert.assertEquals("999", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateBatch(){
+        List<H2UserNoVersion> list = userService.selectList(new EntityWrapper<H2UserNoVersion>());
+        Map<Long, Integer> userVersionMap = new HashMap<>();
+        for(H2UserNoVersion u:list){
+            userVersionMap.put(u.getId(),u.getVersion());
+        }
+        userService.updateBatchById(list);
+
+        list = userService.selectList(new EntityWrapper<H2UserNoVersion>());
+        for(H2UserNoVersion user:list){
+            Assert.assertEquals(userVersionMap.get(user.getId()).intValue(), user.getVersion().intValue());
+        }
+    }
+
+
+}

+ 170 - 5
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java

@@ -7,7 +7,10 @@ import java.math.BigDecimal;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Random;
 
 import javax.sql.DataSource;
 
@@ -97,11 +100,6 @@ public class H2UserTest {
         Assert.assertEquals("Caratacus", userFromDB.getDesc());
     }
 
-    @Test
-    public void testUpdate() {
-
-    }
-
     @Test
     public void testDelete() {
         H2User user = new H2User();
@@ -143,4 +141,171 @@ public class H2UserTest {
         Page<H2User> page = userService.selectPage(new Page<H2User>(1, 3));
         Assert.assertEquals(3, page.getRecords().size());
     }
+
+    @Test
+    public void testUpdateByIdOptLock(){
+        Long id = 991L;
+        H2User user = new H2User();
+        user.setId(id);
+        user.setName("991");
+        user.setAge(91);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userService.insertAllColumn(user);
+
+        H2User userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+
+        userDB.setName("991");
+        userService.updateById(userDB);
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(2, userDB.getVersion().intValue());
+        Assert.assertEquals("991", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateAllColumnByIdOptLock(){
+        Long id = 997L;
+        H2User user = new H2User();
+        user.setId(id);
+        user.setName("991");
+        user.setAge(91);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userService.insertAllColumn(user);
+
+        H2User userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+
+        userDB.setName("991");
+        userService.updateAllColumnById(userDB);
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(2, userDB.getVersion().intValue());
+        Assert.assertEquals("991", userDB.getName());
+
+        userDB.setName("990");
+        userService.updateById(userDB);
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(3, userDB.getVersion().intValue());
+        Assert.assertEquals("990", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateByEntityWrapperOptLock(){
+        Long id = 992L;
+        H2User user = new H2User();
+        user.setId(id);
+        user.setName("992");
+        user.setAge(92);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userService.insertAllColumn(user);
+
+        H2User userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+
+        H2User updUser = new H2User();
+        updUser.setName("999");
+
+        userService.update(updUser, new EntityWrapper<>(userDB));
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(2, userDB.getVersion().intValue());
+        Assert.assertEquals("999", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateByEntityWrapperOptLockWithoutVersion(){
+        Long id = 993L;
+        H2User user = new H2User();
+        user.setId(id);
+        user.setName("992");
+        user.setAge(92);
+        user.setPrice(BigDecimal.TEN);
+        user.setDesc("asdf");
+        user.setTestType(1);
+        user.setVersion(1);
+        userService.insertAllColumn(user);
+
+        H2User userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+
+        H2User updUser = new H2User();
+        updUser.setName("999");
+        userDB.setVersion(null);
+        userService.update(updUser, new EntityWrapper<>(userDB));
+
+        userDB = userService.selectById(id);
+        Assert.assertEquals(1, userDB.getVersion().intValue());
+        Assert.assertEquals("999", userDB.getName());
+    }
+
+    @Test
+    public void testUpdateBatch(){
+        List<H2User> list = userService.selectList(new EntityWrapper<H2User>());
+        Map<Long, Integer> userVersionMap = new HashMap<>();
+        for(H2User u:list){
+            userVersionMap.put(u.getId(),u.getVersion());
+        }
+
+        Assert.assertTrue(userService.updateBatchById(list));
+        list = userService.selectList(new EntityWrapper<H2User>());
+        for(H2User user:list){
+            Assert.assertEquals(userVersionMap.get(user.getId())+1, user.getVersion().intValue());
+        }
+
+    }
+
+    @Test
+    public void testUpdateInLoop(){
+        List<H2User> list = userService.selectList(new EntityWrapper<H2User>());
+        Map<Long,Integer> versionBefore = new HashMap<>();
+        Map<Long,String> nameExpect = new HashMap<>();
+        for (H2User h2User : list) {
+            Long id = h2User.getId();
+            Integer versionVal = h2User.getVersion();
+            versionBefore.put(id, versionVal);
+            String randomName = h2User.getName()+"_"+new Random().nextInt(10);
+            nameExpect.put(id, randomName);
+            h2User.setName(randomName);
+            userService.updateById(h2User);
+        }
+
+        list = userService.selectList(new EntityWrapper<H2User>());
+        for(H2User u:list){
+            Assert.assertEquals(u.getName(), nameExpect.get(u.getId()));
+            Assert.assertEquals(versionBefore.get(u.getId())+1, u.getVersion().intValue());
+        }
+    }
+    @Test
+    public void testUpdateAllColumnInLoop(){
+        List<H2User> list = userService.selectList(new EntityWrapper<H2User>());
+        Map<Long,Integer> versionBefore = new HashMap<>();
+        Map<Long,String> nameExpect = new HashMap<>();
+        for (H2User h2User : list) {
+            Long id = h2User.getId();
+            Integer versionVal = h2User.getVersion();
+            versionBefore.put(id, versionVal);
+            String randomName = h2User.getName()+"_"+new Random().nextInt(10);
+            nameExpect.put(id, randomName);
+            h2User.setName(randomName);
+            userService.updateAllColumnById(h2User);
+        }
+
+        list = userService.selectList(new EntityWrapper<H2User>());
+        for(H2User u:list){
+            Assert.assertEquals(u.getName(), nameExpect.get(u.getId()));
+            Assert.assertEquals(versionBefore.get(u.getId())+1, u.getVersion().intValue());
+        }
+    }
+
 }

+ 13 - 3
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisPlusConfig.java

@@ -13,12 +13,15 @@ import org.springframework.core.io.ResourceLoader;
 import com.baomidou.mybatisplus.MybatisConfiguration;
 import com.baomidou.mybatisplus.MybatisXMLLanguageDriver;
 import com.baomidou.mybatisplus.entity.GlobalConfiguration;
+import com.baomidou.mybatisplus.mapper.LogicSqlInjector;
+import com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor;
 import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
+import com.baomidou.mybatisplus.plugins.PerformanceInterceptor;
 import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
 
 /**
  * <p>
- * TODO class
+ * Mybatis Plus Config
  * </p>
  *
  * @author Caratacus
@@ -40,8 +43,11 @@ public class MybatisPlusConfig {
         sqlSessionFactory.setConfiguration(configuration);
         PaginationInterceptor pagination = new PaginationInterceptor();
         pagination.setDialectType("h2");
+        OptimisticLockerInterceptor optLock = new OptimisticLockerInterceptor();
         sqlSessionFactory.setPlugins(new Interceptor[]{
-                pagination
+                pagination,
+                optLock,
+                new PerformanceInterceptor()
         });
         sqlSessionFactory.setGlobalConfig(globalConfiguration);
         return sqlSessionFactory.getObject();
@@ -49,6 +55,10 @@ public class MybatisPlusConfig {
 
     @Bean
     public GlobalConfiguration globalConfiguration() {
-        return new GlobalConfiguration();
+        GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
+        conf.setLogicDeleteValue("D");
+        conf.setLogicNotDeleteValue("A");
+        conf.setIdType(2);
+        return conf;
     }
 }

+ 16 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/mapper/H2UserDateVersionMapper.java

@@ -0,0 +1,16 @@
+package com.baomidou.mybatisplus.test.h2.entity.mapper;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2UserDateVersion;
+
+/**
+ * <p>
+ * H2User without version column
+ * </p>
+ *
+ * @author Caratacus
+ * @date 2017/4/1
+ */
+public interface H2UserDateVersionMapper extends BaseMapper<H2UserDateVersion> {
+
+}

+ 19 - 1
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/mapper/H2UserMapper.java

@@ -1,11 +1,17 @@
 package com.baomidou.mybatisplus.test.h2.entity.mapper;
 
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
 import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.baomidou.mybatisplus.plugins.Page;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2Addr;
 import com.baomidou.mybatisplus.test.h2.entity.persistent.H2User;
 
 /**
  * <p>
- * TODO class
  * </p>
  *
  * @author Caratacus
@@ -13,4 +19,16 @@ import com.baomidou.mybatisplus.test.h2.entity.persistent.H2User;
  */
 public interface H2UserMapper extends BaseMapper<H2User> {
 
+    @Select(
+            "select a.addr_id as addrId, a.addr_name as addrName from h2address a" +
+                    " join h2user u on u.test_id=a.test_id and u.test_id=#{userId}"
+    )
+    public List<H2Addr> getAddrListByUserId(@Param("userId") Long userId);
+
+    @Select(
+            "select a.addr_id as addrId, a.addr_name as addrName from h2address a" +
+                    " join h2user u on u.test_id=a.test_id and u.test_id=#{userId}"
+    )
+    public List<H2Addr> getAddrListByUserId(@Param("userId") Long userId, Page<H2Addr> page);
+
 }

+ 16 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/mapper/H2UserNoVersionMapper.java

@@ -0,0 +1,16 @@
+package com.baomidou.mybatisplus.test.h2.entity.mapper;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2UserNoVersion;
+
+/**
+ * <p>
+ * H2User without version column
+ * </p>
+ *
+ * @author Caratacus
+ * @date 2017/4/1
+ */
+public interface H2UserNoVersionMapper extends BaseMapper<H2UserNoVersion> {
+
+}

+ 59 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/persistent/H2Addr.java

@@ -0,0 +1,59 @@
+package com.baomidou.mybatisplus.test.h2.entity.persistent;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+
+/**
+ * <p>
+ * h2address entity.
+ * </p>
+ *
+ * @author yuxiaobin
+ * @date 2017/5/25
+ */
+@TableName("h2address")
+public class H2Addr {
+
+    @TableId("addr_id")
+    private Long addrId;
+
+    @TableField("addr_name")
+    private String addrName;
+
+    @TableField("test_id")
+    private Long testId;
+
+    public Long getAddrId() {
+        return addrId;
+    }
+
+    public void setAddrId(Long addrId) {
+        this.addrId = addrId;
+    }
+
+    public String getAddrName() {
+        return addrName;
+    }
+
+    public void setAddrName(String addrName) {
+        this.addrName = addrName;
+    }
+
+    public Long getTestId() {
+        return testId;
+    }
+
+    public void setTestId(Long testId) {
+        this.testId = testId;
+    }
+
+    @Override
+    public String toString() {
+        return "H2Addr{" +
+                "addrId=" + addrId +
+                ", addrName='" + addrName + '\'' +
+                ", testId=" + testId +
+                '}';
+    }
+}

+ 184 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/persistent/H2UserDateVersion.java

@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2011-2014, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.test.h2.entity.persistent;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.annotations.Version;
+import com.baomidou.mybatisplus.enums.FieldStrategy;
+
+/**
+ * <p>
+ * 测试用户类
+ * </p>
+ *
+ * @author hubin sjy
+ */
+/* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */
+@TableName("h2user")
+public class H2UserDateVersion implements Serializable {
+
+    /* 表字段注解,false 表中不存在的字段,可无该注解 默认 true */
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    /* 主键ID 注解,value 字段名,type 用户输入ID */
+    @TableId(value = "test_id")
+    private Long id;
+
+    /* 测试忽略验证 */
+    private String name;
+
+    private Integer age;
+
+    /*BigDecimal 测试*/
+    private BigDecimal price;
+
+    /* 测试下划线字段命名类型, 字段填充 */
+    @TableField(value = "test_type", validate = FieldStrategy.IGNORED)
+    private Integer testType;
+
+    private String desc;
+
+    private Integer version;
+
+    @TableField(value = "test_date")
+    @Version
+    private Date testDate;
+
+    public H2UserDateVersion() {
+
+    }
+
+    public H2UserDateVersion(String name) {
+        this.name = name;
+    }
+
+    public H2UserDateVersion(Integer testType) {
+        this.testType = testType;
+    }
+
+    public H2UserDateVersion(String name, Integer age) {
+        this.name = name;
+        this.age = age;
+    }
+
+    public H2UserDateVersion(Long id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    public H2UserDateVersion(Long id, Integer age) {
+        this.id = id;
+        this.age = age;
+    }
+
+    public H2UserDateVersion(Long id, String name, Integer age, Integer testType) {
+        this.id = id;
+        this.name = name;
+        this.age = age;
+        this.testType = testType;
+    }
+
+    public H2UserDateVersion(String name, Integer age, Integer testType) {
+        this.name = name;
+        this.age = age;
+        this.testType = testType;
+    }
+
+    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 BigDecimal getPrice() {
+        return price;
+    }
+
+    public void setPrice(BigDecimal price) {
+        this.price = price;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public Integer getTestType() {
+        return testType;
+    }
+
+    public void setTestType(Integer testType) {
+        this.testType = testType;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public Integer getVersion() {
+        return version;
+    }
+
+    public void setVersion(Integer version) {
+        this.version = version;
+    }
+
+    public Date getTestDate() {
+        return testDate;
+    }
+
+    public void setTestDate(Date testDate) {
+        this.testDate = testDate;
+    }
+
+    @Override
+    public String toString() {
+        return "H2UserDateVersion{" +
+                "id=" + id +
+                ", name='" + name + '\'' +
+                ", age=" + age +
+                ", price=" + price +
+                ", testType=" + testType +
+                ", desc='" + desc + '\'' +
+                ", version=" + version +
+                ", testDate=" + testDate +
+                '}';
+    }
+}

+ 170 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/persistent/H2UserNoVersion.java

@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2011-2014, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.test.h2.entity.persistent;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.enums.FieldStrategy;
+
+/**
+ * <p>
+ * 测试用户类
+ * </p>
+ *
+ * @author hubin sjy
+ */
+/* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */
+@TableName("h2user")
+public class H2UserNoVersion implements Serializable {
+
+    /* 表字段注解,false 表中不存在的字段,可无该注解 默认 true */
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    /* 主键ID 注解,value 字段名,type 用户输入ID */
+    @TableId(value = "test_id")
+    private Long id;
+
+    /* 测试忽略验证 */
+    private String name;
+
+    private Integer age;
+
+    /*BigDecimal 测试*/
+    private BigDecimal price;
+
+    /* 测试下划线字段命名类型, 字段填充 */
+    @TableField(value = "test_type", validate = FieldStrategy.IGNORED)
+    private Integer testType;
+
+    private String desc;
+
+    private Integer version;
+
+
+    public H2UserNoVersion() {
+
+    }
+
+    public H2UserNoVersion(String name) {
+        this.name = name;
+    }
+
+    public H2UserNoVersion(Integer testType) {
+        this.testType = testType;
+    }
+
+    public H2UserNoVersion(String name, Integer age) {
+        this.name = name;
+        this.age = age;
+    }
+
+    public H2UserNoVersion(Long id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    public H2UserNoVersion(Long id, Integer age) {
+        this.id = id;
+        this.age = age;
+    }
+
+    public H2UserNoVersion(Long id, String name, Integer age, Integer testType) {
+        this.id = id;
+        this.name = name;
+        this.age = age;
+        this.testType = testType;
+    }
+
+    public H2UserNoVersion(String name, Integer age, Integer testType) {
+        this.name = name;
+        this.age = age;
+        this.testType = testType;
+    }
+
+    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 BigDecimal getPrice() {
+        return price;
+    }
+
+    public void setPrice(BigDecimal price) {
+        this.price = price;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public Integer getTestType() {
+        return testType;
+    }
+
+    public void setTestType(Integer testType) {
+        this.testType = testType;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public Integer getVersion() {
+        return version;
+    }
+
+    public void setVersion(Integer version) {
+        this.version = version;
+    }
+
+    @Override
+    public String toString() {
+        return "H2User{" +
+                "id=" + id +
+                ", name='" + name + '\'' +
+                ", age=" + age +
+                ", price=" + price +
+                ", testType=" + testType +
+                ", desc='" + desc + '\'' +
+                ", version=" + version +
+                '}';
+    }
+}

+ 8 - 11
mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/VersionHandler.java → mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/service/IH2UserNoVersionService.java

@@ -13,22 +13,19 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.baomidou.mybatisplus.plugins;
+package com.baomidou.mybatisplus.test.h2.entity.service;
 
-import java.lang.reflect.Field;
+import com.baomidou.mybatisplus.service.IService;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2UserNoVersion;
 
 /**
  * <p>
- * 乐观锁处理器,底层接口
+ * Service层测试
  * </p>
  *
- * @author TaoYu 小锅盖
- * @since 2017-04-08
+ * @author hubin
+ * @date 2017-01-30
  */
-public interface VersionHandler<T> {
+public interface IH2UserNoVersionService extends IService<H2UserNoVersion> {
 
-    /**
-     * 根据类型,使得对象对应的字段+1
-     */
-    void plusVersion(Object paramObj, Field field, T versionValue) throws Exception;
-}
+}

+ 36 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/service/impl/H2UserNoVersionServiceImpl.java

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2011-2014, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.test.h2.entity.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.service.impl.ServiceImpl;
+import com.baomidou.mybatisplus.test.h2.entity.mapper.H2UserNoVersionMapper;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2UserNoVersion;
+import com.baomidou.mybatisplus.test.h2.entity.service.IH2UserNoVersionService;
+
+/**
+ * <p>
+ * Service层测试
+ * </p>
+ *
+ * @author hubin
+ * @date 2017-01-30
+ */
+@Service
+public class H2UserNoVersionServiceImpl extends ServiceImpl<H2UserNoVersionMapper, H2UserNoVersion> implements IH2UserNoVersionService {
+
+}

+ 14 - 22
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugins/optimisticLocker/OptimisticLockerInterceptorTest.java

@@ -4,7 +4,6 @@ import java.io.Reader;
 import java.sql.Connection;
 import java.sql.Timestamp;
 import java.util.Date;
-import java.util.Objects;
 import java.util.Random;
 
 import org.apache.ibatis.io.Resources;
@@ -23,7 +22,6 @@ import com.baomidou.mybatisplus.mapper.EntityWrapper;
 import com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity.DateVersionUser;
 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.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;
@@ -56,7 +54,7 @@ public class OptimisticLockerInterceptorTest {
 		SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession();
 		Connection conn = session.getConnection();
 		Reader reader = Resources
-				.getResourceAsReader("com/baomidou/mybatisplus/test/plugins/optimisticLocker/CreateDB.sql");
+				.getResourceAsReader("h2/optlock/CreateDB.sql");
 		ScriptRunner runner = new ScriptRunner(conn);
 		runner.setLogWriter(null);
 		runner.runScript(reader);
@@ -136,18 +134,6 @@ public class OptimisticLockerInterceptorTest {
 		Assert.assertTrue(timestampVersionUserMapper.selectById(15L).getVersion().after(originVersion));
 	}
 
-	@Test
-	public void stringVersionTest() {
-		// 查询数据
-		StringVersionUser versionUser = stringersionUserMapper.selectById(1);
-		String originVersion = versionUser.getTt();
-		// 更新数据
-		versionUser.setName("苗神");
-		stringersionUserMapper.updateById(versionUser);
-		Assert.assertEquals(stringersionUserMapper.selectById(1).getTt(),
-				String.valueOf(Long.parseLong(originVersion) + 1));
-	}
-
 	@Test
 	public void multiThreadVersionTest() {
 		final Random random = new Random();
@@ -178,25 +164,31 @@ public class OptimisticLockerInterceptorTest {
 	@Test
 	public void multiParamVersionTest() {
 		// 查询数据
-		IntVersionUser versionUser = intVersionUserMapper.selectById(2);
+		Long id = 2L;
+		IntVersionUser versionUser = intVersionUserMapper.selectById(id);
 		Integer originVersion = versionUser.getVersion();
 		// null条件
+		versionUser.setVersion(null);
 		intVersionUserMapper.update(versionUser, null);
-		Assert.assertTrue(Objects.equals(intVersionUserMapper.selectById(2).getVersion(), originVersion));
+		versionUser = intVersionUserMapper.selectById(id);
+		Assert.assertEquals(originVersion.intValue(), versionUser.getVersion().intValue());
 		// 空条件
+		versionUser.setVersion(null);
 		intVersionUserMapper.update(versionUser, new EntityWrapper<IntVersionUser>());
-		Assert.assertTrue(Objects.equals(intVersionUserMapper.selectById(2).getVersion(), originVersion));
+		Assert.assertEquals(intVersionUserMapper.selectById(id).getVersion(), originVersion);
 		// 正常查询不带version
 		IntVersionUser wrapper = new IntVersionUser();
 		wrapper.setName("lisi");
 		intVersionUserMapper.update(versionUser, new EntityWrapper<>(wrapper));
-		Assert.assertTrue(intVersionUserMapper.selectById(2).getVersion() == originVersion + 1);
+		versionUser = intVersionUserMapper.selectById(id);
+		Assert.assertEquals(versionUser.getVersion(), originVersion);
 		// 原始条件带version按原始逻辑走
 		IntVersionUser wrapper2 = new IntVersionUser();
 		wrapper2.setName("lisi");
-		wrapper2.setVersion(originVersion + 1);
-		intVersionUserMapper.update(versionUser, new EntityWrapper<>(wrapper2));
-		Assert.assertTrue(intVersionUserMapper.selectById(1).getVersion() == originVersion + 1);
+		wrapper2.setId(id);
+		wrapper2.setVersion(versionUser.getVersion() + 1);
+		int effRow = intVersionUserMapper.update(versionUser, new EntityWrapper<>(wrapper2));
+		Assert.assertEquals(0, effRow);
 	}
 
 	@Test // FIXME这个测试应该归属逻辑删除里

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

@@ -1,13 +0,0 @@
-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));
-    }
-
-}

+ 6 - 0
mybatis-plus/src/test/resources/h2/addr.ddl.sql

@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS  h2address (
+	addr_id BIGINT(20) NOT NULL ,
+	addr_name VARCHAR(30) NULL DEFAULT NULL ,
+	test_id BIGINT(20) NOT NULL ,
+	PRIMARY KEY (addr_id)
+)

+ 5 - 0
mybatis-plus/src/test/resources/h2/addr.insert.sql

@@ -0,0 +1,5 @@
+insert into h2address(addr_id,addr_name,test_id) values(1,'Suzhou', 101);
+insert into h2address(addr_id,addr_name,test_id) values(2,'Beijing', 101);
+insert into h2address(addr_id,addr_name,test_id) values(3,'Shanghai', 101);
+insert into h2address(addr_id,addr_name,test_id) values(4,'Guangzhou', 101);
+insert into h2address(addr_id,addr_name,test_id) values(5,'Hangzhou', 101);

+ 28 - 0
mybatis-plus/src/test/resources/h2/optlock/CreateDB.sql

@@ -0,0 +1,28 @@
+DROP TABLE
+IF EXISTS version_user;
+
+CREATE TABLE version_user (
+	id bigint (11) NOT NULL ,
+	NAME VARCHAR (20),
+	age INT (11),
+	version INT (11),
+	isDelete INT (11) DEFAULT 0,
+	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',109);
+insert into version_user (id,name,version) values(3,'wangwu',null);
+
+DROP TABLE
+IF EXISTS time_version_user;
+
+CREATE TABLE time_version_user (
+	id bigint (11) NOT NULL ,
+	NAME VARCHAR (20),
+	version datetime,
+	PRIMARY KEY (`id`)
+) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
+
+DROP TABLE
+IF EXISTS string_version_user;

+ 35 - 0
mybatis-plus/src/test/resources/h2/optlock/mybatis-config.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration
+    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-config.dtd">
+
+<configuration>
+	<plugins>
+		<plugin interceptor="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"></plugin>
+		<plugin interceptor="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor"></plugin>
+	</plugins>
+
+	<environments default="development">
+		<environment id="development">
+			<transactionManager type="JDBC">
+				<property name="" value="" />
+			</transactionManager>
+			<dataSource type="UNPOOLED">
+				<property name="driver" value="com.mysql.jdbc.Driver" />
+				<property name="url" value="jdbc:mysql://localhost:3306/mybatis-plus" />
+				<property name="username" value="root" />
+				<property name="password" value="!Ty5546380" />
+			</dataSource>
+
+		</environment>
+
+	</environments>
+
+	<mappers>
+		<mapper class="com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.IntVersionUserMapper" />
+		<mapper class="com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.LongVersionUserMapper" />
+		<mapper class="com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.DateVersionUserMapper" />
+		<mapper class="com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.TimestampVersionUserMapper" />
+	</mappers>
+
+</configuration>

+ 67 - 68
mybatis-plus/src/test/resources/h2/spring-jdbc.xml

@@ -1,75 +1,74 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
-	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+<beans xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns="http://www.springframework.org/schema/beans"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
-	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
-	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
+	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
 
-	<!-- 加载配置文件 -->
-	<context:property-placeholder location="classpath:properties/jdbc-h2.properties" />
+    <!-- 加载配置文件 -->
+    <context:property-placeholder location="classpath:properties/jdbc-h2.properties"/>
 
-	<!-- 数据库连接池 -->
-	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
-		destroy-method="close">
-		<property name="url" value="${jdbc.url}" />
-		<property name="username" value="${jdbc.username}" />
-		<property name="password" value="${jdbc.password}" />
-		<property name="driverClassName" value="${jdbc.driver}" />
-		<!-- 初始化连接大小 -->
-		<property name="initialSize" value="0" />
-		<!-- 连接池最大使用连接数量 -->
-		<property name="maxActive" value="20" />
-		<!-- 连接池最大空闲 -->
-		<property name="maxIdle" value="20" />
-		<!-- 连接池最小空闲 -->
-		<property name="minIdle" value="0" />
-		<!-- 获取连接最大等待时间 -->
-		<property name="maxWait" value="60000" />
-		<property name="testOnBorrow" value="false" />
-		<property name="testOnReturn" value="false" />
-		<property name="testWhileIdle" value="true" />
-		<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
-		<property name="timeBetweenEvictionRunsMillis" value="60000" />
-		<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
-		<property name="minEvictableIdleTimeMillis" value="25200000" />
-		<!-- 打开removeAbandoned功能 -->
-		<property name="removeAbandoned" value="true" />
-		<!-- 1800秒,也就是30分钟 -->
-		<property name="removeAbandonedTimeout" value="1800" />
-		<!-- 关闭abanded连接时输出错误日志 -->
-		<property name="logAbandoned" value="true" />
-		<!-- 监控数据库 -->
-		<property name="filters" value="stat" />
-	</bean>
+    <!-- 数据库连接池 -->
+    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
+          destroy-method="close">
+        <property name="url" value="${jdbc.url}"/>
+        <property name="username" value="${jdbc.username}"/>
+        <property name="password" value="${jdbc.password}"/>
+        <property name="driverClassName" value="${jdbc.driver}"/>
+        <!-- 初始化连接大小 -->
+        <property name="initialSize" value="0"/>
+        <!-- 连接池最大使用连接数量 -->
+        <property name="maxActive" value="20"/>
+        <!-- 连接池最大空闲 -->
+        <property name="maxIdle" value="20"/>
+        <!-- 连接池最小空闲 -->
+        <property name="minIdle" value="0"/>
+        <!-- 获取连接最大等待时间 -->
+        <property name="maxWait" value="60000"/>
+        <property name="testOnBorrow" value="false"/>
+        <property name="testOnReturn" value="false"/>
+        <property name="testWhileIdle" value="true"/>
+        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
+        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
+        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
+        <property name="minEvictableIdleTimeMillis" value="25200000"/>
+        <!-- 打开removeAbandoned功能 -->
+        <property name="removeAbandoned" value="true"/>
+        <!-- 1800秒,也就是30分钟 -->
+        <property name="removeAbandonedTimeout" value="1800"/>
+        <!-- 关闭abanded连接时输出错误日志 -->
+        <property name="logAbandoned" value="true"/>
+        <!-- 监控数据库 -->
+        <property name="filters" value="stat"/>
+    </bean>
 
-	<!-- 事务管理器 -->
-	<bean id="transactionManager"
-		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
-		<!-- 数据源 -->
-		<property name="dataSource" ref="dataSource" />
-	</bean>
+    <!-- 事务管理器 -->
+    <bean id="transactionManager"
+          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
+        <!-- 数据源 -->
+        <property name="dataSource" ref="dataSource"/>
+    </bean>
 
-	<!-- 通知 -->
-	<tx:advice id="txAdvice" transaction-manager="transactionManager">
-		<tx:attributes>
-			<!-- 传播行为 -->
-			<tx:method name="save*" propagation="REQUIRED" />
-			<tx:method name="insert*" propagation="REQUIRED" />
-			<tx:method name="add*" propagation="REQUIRED" />
-			<tx:method name="create*" propagation="REQUIRED" />
-			<tx:method name="delete*" propagation="REQUIRED" />
-			<tx:method name="update*" propagation="REQUIRED" />
-			<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
-			<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
-			<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
-		</tx:attributes>
-	</tx:advice>
-	<!-- 切面 -->
-	<aop:config>
-		<aop:advisor advice-ref="txAdvice"
-			pointcut="execution(* com.baomidou.*.service.*.*.*(..))" />
-	</aop:config>
+    <!-- 通知 -->
+    <tx:advice id="txAdvice" transaction-manager="transactionManager">
+        <tx:attributes>
+            <!-- 传播行为 -->
+            <tx:method name="save*" propagation="REQUIRED"/>
+            <tx:method name="insert*" propagation="REQUIRED"/>
+            <tx:method name="add*" propagation="REQUIRED"/>
+            <tx:method name="create*" propagation="REQUIRED"/>
+            <tx:method name="delete*" propagation="REQUIRED"/>
+            <tx:method name="update*" propagation="REQUIRED"/>
+            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
+            <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
+            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
+        </tx:attributes>
+    </tx:advice>
+    <!-- 切面 -->
+    <aop:config>
+        <aop:advisor advice-ref="txAdvice"
+                     pointcut="execution(* com.baomidou.*.service.*.*.*(..))"/>
+    </aop:config>
 </beans>

+ 9 - 8
mybatis-plus/src/test/resources/h2/spring-test-h2.xml

@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-	xmlns:context="http://www.springframework.org/schema/context"
-	xmlns:mvc="http://www.springframework.org/schema/mvc"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
-           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
-           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns="http://www.springframework.org/schema/beans"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
-	<context:component-scan base-package="com.baomidou.mybatisplus.test.h2" />
+    <context:component-scan base-package="com.baomidou.mybatisplus.test.h2"/>
+
+    <bean name="/DBConfig" class="com.baomidou.mybatisplus.test.h2.config.DBConfig" />
+    <bean name="/MybatisPlusConfig" class="com.baomidou.mybatisplus.test.h2.config.MybatisPlusConfig" />
 
 </beans>

+ 4 - 4
mybatis-plus/src/test/resources/h2/user.insert.sql

@@ -1,5 +1,5 @@
 insert into h2user(test_id, name, test_date, age, price, test_type, version) values (101,'Tomcat', '2017-1-1 1:1:1', 3, 9.99, 1, 1),
-insert into h2user(test_id, name, test_date, age, price, test_type, version) values (102,'Jerry', '2017-3-1 1:1:1', 2, 19.99, 1, 1);
-insert into h2user(test_id, name, test_date, age, price, test_type, version) values (103,'Bob', '2017-4-1 1:1:1', 13, 99.99, 1, 1);
-insert into h2user(test_id, name, test_date, age, price, test_type, version) values (104,'Joe', '2017-2-1 1:1:1', 18, 1.99, 1, 1);
-insert into h2user(test_id, name, test_date, age, price, test_type, version) values (105,'Tony', '2017-2-1 1:1:1', 18, 1.99, 1, 1);
+insert into h2user(test_id, name, test_date, age, price, test_type, version) values (102,'Jerry', '2017-3-1 1:1:1', 2, 19.99, 1, 2);
+insert into h2user(test_id, name, test_date, age, price, test_type, version) values (103,'Bob', '2017-4-1 1:1:1', 13, 99.99, 1, 3);
+insert into h2user(test_id, name, test_date, age, price, test_type, version) values (104,'Joe', '2017-2-1 1:1:1', 18, 1.99, 1, 4);
+insert into h2user(test_id, name, test_date, age, price, test_type, version) values (105,'Tony', '2017-2-1 1:1:1', 18, 1.99, 1, 5);

+ 1 - 7
mybatis-plus/src/test/resources/plugins/optimisticLockerInterceptor.xml

@@ -20,13 +20,7 @@
 			value="com.baomidou.mybatisplus.test.plugins.optimisticLocker.entity" />
 		<property name="plugins">
 			<array>
-				<bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">
-					<property name="properties">
-						<value>
-							versionHandlers=com.baomidou.mybatisplus.test.plugins.optimisticLocker.StringTypeHandler
-						</value>
-					</property>
-				</bean>
+				<bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor"/>
 				<bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor" />
 			</array>
 		</property>

+ 2 - 1
mybatis-plus/src/test/resources/properties/jdbc-h2.properties

@@ -1,4 +1,5 @@
 jdbc.driver=org.h2.Driver
-jdbc.url=jdbc:h2:~/test
+jdbc.url=jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+#jdbc.url=jdbc:h2:~/test
 jdbc.username=sa
 jdbc.password=