浏览代码

Merge branch 'dev' of https://git.oschina.net/baomidou/mybatis-plus.git
into dev

Conflicts:
mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/OptimisticLockerInterceptor.java

小锅盖 8 年之前
父节点
当前提交
111673f492

+ 7 - 16
mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/OptimisticLockerInterceptor.java

@@ -66,7 +66,7 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 	/**
 	 * 根据对象类型缓存version基本信息
 	 */
-	private static final Map<String, VersionCache> versionCache = new ConcurrentHashMap<>();
+	private static final Map<Class<?>, VersionCache> versionCache = new ConcurrentHashMap<Class<?>, VersionCache>();
 
 	/**
 	 * 根据version字段类型缓存的处理器
@@ -95,15 +95,13 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 		if (parameterObject instanceof ParamMap) {
 			// FIXME 这里还没处理
 			ParamMap<?> tt = (ParamMap<?>) parameterObject;
-			parameterClass = tt.get("param1").getClass();
-		}
-		if (ProxyFactory.isProxyClass(parameterClass)) {
+			realClass = tt.get("param1").getClass();
+		} else if (ProxyFactory.isProxyClass(parameterClass)) {
 			realClass = parameterClass.getSuperclass();
 		} else {
 			realClass = parameterClass;
 		}
-		String cacheKey = realClass.getName();
-		VersionCache versionPo = versionCache.get(cacheKey);
+		VersionCache versionPo = versionCache.get(realClass);
 		if (versionPo != null) {
 			if (versionPo.isVersionControl) {
 				processChangeSql(ms, parameterObject, versionPo);
@@ -129,10 +127,10 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 			if (versionField != null) {
 				versionField.setAccessible(true);
 				VersionCache cachePo = new VersionCache(true, versionColumn, versionField);
-				versionCache.put(cacheKey, cachePo);
+				versionCache.put(parameterClass, cachePo);
 				processChangeSql(ms, parameterObject, cachePo);
 			} else {
-				versionCache.put(cacheKey, new VersionCache(false));
+				versionCache.put(parameterClass, new VersionCache(false));
 			}
 		}
 		return invocation.proceed();
@@ -142,14 +140,7 @@ public final class OptimisticLockerInterceptor implements Interceptor {
 	private void processChangeSql(MappedStatement ms, Object parameterObject, VersionCache versionPo) throws Exception {
 		Field versionField = versionPo.versionField;
 		String versionColumn = versionPo.versionColumn;
-		Object realObject = null;
-		if (parameterObject instanceof ParamMap) {
-			ParamMap<?> tt = (ParamMap<?>) parameterObject;
-			realObject = tt.get("param1");
-		} else {
-			realObject = parameterObject;
-		}
-		final Object versionValue = versionField.get(realObject);
+		final Object versionValue = versionField.get(parameterObject);
 		if (versionValue != null) {// 先判断传参是否携带version,没带跳过插件
 			Configuration configuration = ms.getConfiguration();
 			BoundSql originBoundSql = ms.getBoundSql(parameterObject);

+ 1 - 1
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugin/OptimisticLocker/OptimisticLockerInterceptorJUnitTest.java → mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugin/OptimisticLocker/OptimisticLockerInterceptorest.java

@@ -25,7 +25,7 @@ import com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.LongVersionU
 import com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.ShortVersionUserMapper;
 import com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.TimestampVersionUserMapper;
 
-public class OptimisticLockerInterceptorJUnitTest {
+ class OptimisticLockerInterceptorest {
 
 	private static SqlSessionFactory sqlSessionFactory;
 

+ 66 - 8
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugin/OptimisticLockerInterceptorJUnitTest.java

@@ -1,6 +1,9 @@
 package com.baomidou.mybatisplus.test.plugin;
 
 import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Date;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -10,6 +13,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
 import com.baomidou.mybatisplus.mapper.EntityWrapper;
 import com.baomidou.mybatisplus.test.mysql.entity.User;
+import com.baomidou.mybatisplus.test.plugin.OptimisticLocker.entity.TimestampVersionUser;
 
 
 @RunWith(SpringJUnit4ClassRunner.class)
@@ -26,29 +30,83 @@ public class OptimisticLockerInterceptorJUnitTest extends UserTestBase{
 	
 	@Test
 	public void testUpdateWithoutVersionControl(){
-		User user = userService.selectById(11);
+		User user = userService.selectById(11);//if cacheEnabled=true, this object will be cached
 		Assert.assertEquals(1, user.getAge().intValue());
-		user.setVersion(null);
-		user.setAge(2);
-		userService.updateById(user);
+		User updateUser = new User();
+		updateUser.setId(user.getId());
+		updateUser.setAge(2);
+		userService.updateById(updateUser);
+		
 		user = userService.selectById(11);
 		Assert.assertEquals(2, user.getAge().intValue());
 		Assert.assertEquals(1, user.getVersion().intValue());
+		
+		User user2 = userService.selectById(12);
+		updateUser = new User();
+		updateUser.setAge(99);
+		updateUser.setPrice(new BigDecimal("9.99"));
+		userService.updateById(updateUser);
+		user2 = userService.selectById(12);
+		Assert.assertEquals(1, user2.getVersion().intValue());//test if version is cached in plugin [OK]
 	}
 	
 	@Test
 	public void testUpdateWithVersionControl(){
 		long userId = 12;
 		User user = userService.selectById(userId);
-		Assert.assertEquals(2, user.getAge().intValue());
-		user.setAge(3);
-		userService.updateById(user);
+		Assert.assertEquals(1, user.getVersion().intValue());
+		User updateUser = new User();
+		updateUser.setId(userId);
+		updateUser.setAge(3);
+		updateUser.setVersion(user.getVersion());
+		userService.updateById(updateUser);
+		user = userService.selectById(userId);
+		
+		Assert.assertEquals(3, user.getAge().intValue());
+		Assert.assertEquals(2, user.getVersion().intValue());
+		
 		User where = new User();
 		where.setId(userId);
+//		updateUser = new User();
+//		updateUser.setId(userId);
+//		updateUser.setAge(3);
+//		updateUser.setVersion(user.getVersion());
 		userService.update(user, new EntityWrapper<User>(where));
 		user = userService.selectById(userId);
 		Assert.assertEquals(3, user.getAge().intValue());
-		Assert.assertEquals(2, user.getVersion().intValue());
+		Assert.assertEquals(3, user.getVersion().intValue());
+	}
+	
+	@Test
+	public void testTimestampVersionControl(){
+		timeVersionMapper.deleteById(1L);
+		TimestampVersionUser user = new TimestampVersionUser();
+		user.setId(1L);
+		user.setName("name1");
+		Calendar cal = Calendar.getInstance();
+		cal.add(Calendar.DAY_OF_MONTH, -1);
+		user.setVersion(new Timestamp(new Date().getTime()));
+		timeVersionMapper.insert(user);
+		
+		cal = Calendar.getInstance();
+		cal.add(Calendar.SECOND, -1);
+		Date date = cal.getTime();
+		System.err.println("date="+date);
+		int compare = user.getVersion().compareTo(date);
+		Assert.assertTrue(compare<0);
+		
+		TimestampVersionUser updateUser = new TimestampVersionUser();
+		updateUser.setId(1L);
+		updateUser.setName("updname");
+		updateUser.setVersion(user.getVersion());
+		
+		timeVersionMapper.updateById(updateUser);
+		user = timeVersionMapper.selectById(1L);
+		Date version = user.getVersion();
+		System.err.println("after update: version="+version);
+		compare = version.compareTo(date);
+		System.err.println("compare="+compare);
+		Assert.assertTrue(compare>0);
 	}
 	
 }

+ 77 - 73
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/plugin/UserTestBase.java

@@ -1,73 +1,77 @@
-package com.baomidou.mybatisplus.test.plugin;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.baomidou.mybatisplus.mapper.EntityWrapper;
-import com.baomidou.mybatisplus.test.mysql.entity.User;
-import com.baomidou.mybatisplus.test.mysql.service.IUserService;
-import com.baomidou.mybatisplus.toolkit.IdWorker;
-
-public abstract class UserTestBase {
-
-	@Autowired
-	protected IUserService userService;
-	
-	@Before
-	public void init(){
-//		cleanData();
-		User userA = new User();
-		userA.setId(IdWorker.getId());
-		userA.setName("junyu_shi");
-		userA.setAge(1);
-		userA.setDesc("测试一把");
-		userA.setTestType(1);
-		userA.setVersion(1);
-		userService.insert(userA);
-		long id = IdWorker.getId();
-		userA = new User(id, "abc", 18, 1);
-		userA.setVersion(1);
-		userService.insert(userA);
-		userA = new User(18);
-		userA.setVersion(1);
-		userService.insert(userA);
-		
-		List<User> ul = new ArrayList<User>();
-		/* 手动输入 ID */
-		ul.add(new User(11L, "1", 1, 0));
-		ul.add(new User(12L, "2", 2, 1));
-		ul.add(new User(13L, "3", 3, 1));
-		ul.add(new User(14L, "delname", 4, 0));
-		ul.add(new User(15L, "5", 5, 1));
-		ul.add(new User(16L, "6", 6, 0));
-		/* 测试 name test_type 填充 */
-		ul.add(new User(17L, 7));
-		ul.add(new User(18L, 8));
-		ul.add(new User(19L, 9));
-		ul.add(new User(7));
-		ul.add(new User(20L, "deleteByMap", 7, 0));
-		/* 使用 ID_WORKER 自动生成 ID */
-		ul.add(new User("8", 8, 1));
-		ul.add(new User("9", 9, 1));
-		for (User u : ul) {
-			u.setVersion(1);
-			u.setPrice(new BigDecimal("99.99"));
-			userService.insert(u);
-		}
-	}
-	
-	@After
-	public void cleanData(){
-		User user = new User();
-		user.setDesc(null);
-		EntityWrapper<User> ew = new EntityWrapper<>(user);
-		ew.and("1=1");
-		userService.delete(ew);
-	}
-	
-}
+package com.baomidou.mybatisplus.test.plugin;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.baomidou.mybatisplus.test.mysql.entity.User;
+import com.baomidou.mybatisplus.test.mysql.service.IUserService;
+import com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper.TimestampVersionUserMapper;
+import com.baomidou.mybatisplus.toolkit.IdWorker;
+
+public abstract class UserTestBase {
+
+	@Autowired
+	protected IUserService userService;
+	
+	@Autowired
+	protected TimestampVersionUserMapper timeVersionMapper;
+	
+	@Before
+	public void init(){
+//		cleanData();
+		User userA = new User();
+		userA.setId(IdWorker.getId());
+		userA.setName("junyu_shi");
+		userA.setAge(1);
+		userA.setDesc("测试一把");
+		userA.setTestType(1);
+		userA.setVersion(1);
+		userService.insert(userA);
+		long id = IdWorker.getId();
+		userA = new User(id, "abc", 18, 1);
+		userA.setVersion(1);
+		userService.insert(userA);
+		userA = new User(18);
+		userA.setVersion(1);
+		userService.insert(userA);
+		
+		List<User> ul = new ArrayList<User>();
+		/* 手动输入 ID */
+		ul.add(new User(11L, "1", 1, 0));
+		ul.add(new User(12L, "2", 2, 1));
+		ul.add(new User(13L, "3", 3, 1));
+		ul.add(new User(14L, "delname", 4, 0));
+		ul.add(new User(15L, "5", 5, 1));
+		ul.add(new User(16L, "6", 6, 0));
+		/* 测试 name test_type 填充 */
+		ul.add(new User(17L, 7));
+		ul.add(new User(18L, 8));
+		ul.add(new User(19L, 9));
+		ul.add(new User(7));
+		ul.add(new User(20L, "deleteByMap", 7, 0));
+		/* 使用 ID_WORKER 自动生成 ID */
+		ul.add(new User("8", 8, 1));
+		ul.add(new User("9", 9, 1));
+		for (User u : ul) {
+			u.setVersion(1);
+			u.setPrice(new BigDecimal("99.99"));
+			userService.insert(u);
+		}
+	}
+	
+	@After
+	public void cleanData(){
+		User user = new User();
+		user.setDesc(null);
+		EntityWrapper<User> ew = new EntityWrapper<>(user);
+		ew.and("1=1");
+		userService.delete(ew);
+	}
+	
+}

+ 109 - 2
mybatis-plus/src/test/resources/spring/spring-jdbc.xml

@@ -79,7 +79,7 @@
 		class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
 		<property name="dataSource" ref="dataSource" />
 		<property name="configLocation" value="classpath:mybatis-config.xml" />
-		<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.test.mysql.entity" />
+		<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.test.mysql.entity;com.baomidou.mybatisplus.test.plugin.OptimisticLocker.entity" />
 		<property name="mapperLocations" value="classpath:mysql/*Mapper.xml" />
 		<property name="globalConfig" ref="globalConfig" />
 		<property name="plugins">
@@ -103,6 +103,113 @@
 	<bean id="sqlInjector" class="com.baomidou.mybatisplus.test.mysql.MySqlInjector" />
 
 	<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
-		<property name="basePackage" value="com.baomidou.mybatisplus.test.mysql.mapper" />
+		<property name="basePackage" value="com.baomidou.mybatisplus.test.mysql.mapper;com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper" />
+	</bean>
+<?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
+	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">
+
+	<!-- 加载配置文件 -->
+	<context:property-placeholder location="classpath:properties/jdbc.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="validationQuery" value="${validationQuery}" />
+		<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>
+
+	<!-- 通知 -->
+	<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>
+
+	<!-- MyBatis SqlSessionFactoryBean 配置 -->
+	<bean id="sqlSessionFactory"
+		class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
+		<property name="dataSource" ref="dataSource" />
+		<property name="configLocation" value="classpath:mybatis-config.xml" />
+		<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.test.mysql.entity;com.baomidou.mybatisplus.test.plugin.OptimisticLocker.entity" />
+		<property name="mapperLocations" value="classpath:mysql/*Mapper.xml" />
+		<property name="globalConfig" ref="globalConfig" />
+		<property name="plugins">
+			<array>
+				<!-- 分页插件配置 -->
+				<bean id="paginationInterceptor"
+					class="com.baomidou.mybatisplus.plugins.PaginationInterceptor">
+					<property name="dialectType" value="mysql" />
+				</bean>
+				<bean id="optimisticLockerInterceptor"
+					class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">
+				</bean>
+			</array>
+		</property>
+	</bean>
+
+	<!-- 注入配置 -->
+	<bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
+		<property name="sqlInjector" ref="sqlInjector" />
+	</bean>
+	<bean id="sqlInjector" class="com.baomidou.mybatisplus.test.mysql.MySqlInjector" />
+
+	<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
+		<property name="basePackage" value="com.baomidou.mybatisplus.test.mysql.mapper;com.baomidou.mybatisplus.test.plugin.OptimisticLocker.mapper" />
 	</bean>
 </beans>