Repository: alibaba/fescar (java)

Issue:
NotSerializableException
### Ⅰ. Issue Description
用spring Boot+dubbo微服务搭建的一个fescar测试。
有三个模块

- demo-api

- demo-provider

- demo-provider2

- demo-consumer

consumer调用provider，provider再次调用provider2。
在不加入@GlobalTransactional的时候，测试通过，数据库简单插入一行数据。
加入注解的时候，在provider2加入`int a=1/0;`
测试显示：provider2的数据回滚成功。
但是provider报错序列化问题。
详细异常如下。


### Ⅱ. Describe what happened


```
java.sql.SQLException: Invalid argument value: java.io.NotSerializableException
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:127) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:87) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:61) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:71) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:85) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.jdbc.ClientPreparedStatement.setObject(ClientPreparedStatement.java:1690) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_setObject(FilterChainImpl.java:3401) ~[druid-1.1.12.jar:1.1.12]
	at com.alibaba.druid.filter.FilterAdapter.preparedStatement_setObject(FilterAdapter.java:1320) ~[druid-1.1.12.jar:1.1.12]
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_setObject(FilterChainImpl.java:3398) ~[druid-1.1.12.jar:1.1.12]
	at com.alibaba.druid.filter.FilterAdapter.preparedStatement_setObject(FilterAdapter.java:1320) ~[druid-1.1.12.jar:1.1.12]
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_setObject(FilterChainImpl.java:3398) ~[druid-1.1.12.jar:1.1.12]
	at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.setObject(PreparedStatementProxyImpl.java:460) ~[druid-1.1.12.jar:1.1.12]
	at com.alibaba.druid.pool.DruidPooledPreparedStatement.setObject(DruidPooledPreparedStatement.java:481) ~[druid-1.1.12.jar:1.1.12]
	at com.alibaba.fescar.rm.datasource.exec.InsertExecutor.getTableRecords(InsertExecutor.java:147) ~[fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.exec.InsertExecutor.afterImage(InsertExecutor.java:88) ~[fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.exec.AbstractDMLBaseExecutor.executeAutoCommitFalse(AbstractDMLBaseExecutor.java:71) [fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.exec.AbstractDMLBaseExecutor.executeAutoCommitTrue(AbstractDMLBaseExecutor.java:91) [fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.exec.AbstractDMLBaseExecutor.doExecute(AbstractDMLBaseExecutor.java:55) [fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.exec.BaseTransactionalExecutor.execute(BaseTransactionalExecutor.java:72) [fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.exec.ExecuteTemplate.execute(ExecuteTemplate.java:100) [fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.exec.ExecuteTemplate.execute(ExecuteTemplate.java:46) [fescar-rm-datasource-0.1.3.jar:na]
	at com.alibaba.fescar.rm.datasource.PreparedStatementProxy.execute(PreparedStatementProxy.java:60) [fescar-rm-datasource-0.1.3.jar:na]
	at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:46) [mybatis-3.4.6.jar:3.4.6]
	at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74) [mybatis-3.4.6.jar:3.4.6]
	at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50) [mybatis-3.4.6.jar:3.4.6]
	at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) [mybatis-3.4.6.jar:3.4.6]
	at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) [mybatis-3.4.6.jar:3.4.6]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198) [mybatis-3.4.6.jar:3.4.6]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185) [mybatis-3.4.6.jar:3.4.6]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433) [mybatis-spring-1.3.2.jar:1.3.2]
	at com.sun.proxy.$Proxy50.insert(Unknown Source) [na:na]
	at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:278) [mybatis-spring-1.3.2.jar:1.3.2]
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:58) [mybatis-3.4.6.jar:3.4.6]
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59) [mybatis-3.4.6.jar:3.4.6]
	at com.sun.proxy.$Proxy52.insert(Unknown Source) [na:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) [spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at com.sun.proxy.$Proxy54.insert(Unknown Source) [na:na]
	at com.demo.provider.service.UserServiceImpl.addUser(UserServiceImpl.java:58) [classes/:na]
	at com.demo.provider.service.UserServiceImpl$$FastClassBySpringCGLIB$$36ea8b52.invoke(<generated>) [classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) [spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at com.alibaba.fescar.spring.annotation.GlobalTransactionalInterceptor$1.execute(GlobalTransactionalInterceptor.java:64) [fescar-spring-0.1.3.jar:na]
	at com.alibaba.fescar.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:52) [fescar-tm-0.1.3.jar:na]
	at com.alibaba.fescar.spring.annotation.GlobalTransactionalInterceptor.invoke(GlobalTransactionalInterceptor.java:61) [fescar-spring-0.1.3.jar:na]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) [spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
	at com.demo.provider.service.UserServiceImpl$$EnhancerBySpringCGLIB$$baae7637.addUser(<generated>) [classes/:na]
	at com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:76) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:52) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:62) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:73) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:138) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:104) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:96) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:173) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51) [dubbo-2.6.5.jar:2.6.5]
	at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57) [dubbo-2.6.5.jar:2.6.5]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]
Caused by: com.mysql.cj.exceptions.WrongArgumentException: Invalid argument value: java.io.NotSerializableException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_181]
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_181]
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_181]
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_181]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:59) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:103) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:149) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.AbstractQueryBindings.setSerializableObject(AbstractQueryBindings.java:585) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.AbstractQueryBindings.setObject(AbstractQueryBindings.java:252) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	at com.mysql.cj.jdbc.ClientPreparedStatement.setObject(ClientPreparedStatement.java:1688) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	... 84 common frames omitted
Caused by: java.io.NotSerializableException: com.alibaba.fescar.rm.datasource.sql.struct.Null
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) ~[na:1.8.0_181]
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) ~[na:1.8.0_181]
	at com.mysql.cj.AbstractQueryBindings.setSerializableObject(AbstractQueryBindings.java:574) ~[mysql-connector-java-8.0.11.jar:8.0.11]
	... 86 common frames omitted
```



### Ⅳ. How to reproduce it 

源码地址:[https://github.com/yuhong151885/fescar-demo](https://github.com/yuhong151885/fescar-demo)

### Ⅵ. Environment:

- JDK version : 1.8.0_181
- OS : win10
- Others: intelliJ IDEA 

Pull request:
fix #357 pk auto generated but value is null
<!-- Please make sure you have read and understood the contributing guidelines -->

### Ⅰ. Describe what this PR did
when pk auto generated and  value is null, fescar can not get the pk value .

### Ⅱ. Does this pull request fix one issue?
<!-- If that, add "fixes #xxx" below in the next line, for example, fixes #97. -->

fix #357 
### Ⅲ. Why don't you add test cases (unit test/integration test)? 


### Ⅳ. Describe how to verify it


### Ⅴ. Special notes for reviews



Patch:
diff --git a/README.md b/README.md
index 121f15ef7..c48546ee9 100644
--- a/README.md
+++ b/README.md
@@ -95,9 +95,10 @@ Contributors are welcomed to join the FEATS project. Please check [CONTRIBUTING]
 ## Contact
 
 * [Twitter](): TBD. Follow along for latest Fescar news on Twitter.
-* Email Group:
-     * dev.fescar@gmail.com: Fescar developer discussion (APIs, feature design, etc).
 
+* Mailing list: 
+  * dev-fescar@googlegroups.com , for dev/user discussion. [subscribe](mailto:dev-fescar+subscribe@googlegroups.com), [unsubscribe](mailto:dev-fescar+unsubscribe@googlegroups.com), [archive](https://groups.google.com/forum/#!forum/dev-fescar)
+  
 **Dingtalk**
 
 <img src="https://upload-images.jianshu.io/upload_images/4420767-4e95b186a1a1bfba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"  height="300" width="300">
diff --git a/common/src/main/java/com/alibaba/fescar/common/thread/RejectedPolicys.java b/common/src/main/java/com/alibaba/fescar/common/thread/RejectedPolicys.java
new file mode 100644
index 000000000..bfaaf6722
--- /dev/null
+++ b/common/src/main/java/com/alibaba/fescar/common/thread/RejectedPolicys.java
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  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
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  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.alibaba.fescar.common.thread;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * policys for RejectedExecutionHandler
+ *
+ * Created by guoyao on 2019/2/26.
+ */
+public final class RejectedPolicys {
+
+    /**
+     * when rejected happened ,add the new task and run the oldest task
+     *
+     * @return rejected execution handler
+     */
+    public static RejectedExecutionHandler runsOldestTaskPolicy() {
+        return new RejectedExecutionHandler() {
+            @Override
+            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
+                if (executor.isShutdown()) {
+                    return;
+                }
+                BlockingQueue<Runnable> workQueue = executor.getQueue();
+                Runnable firstWork = workQueue.poll();
+                boolean newTaskAdd = workQueue.offer(r);
+                if (firstWork != null) {
+                    firstWork.run();
+                }
+                if (!newTaskAdd) {
+                    executor.execute(r);
+                }
+            }
+        };
+    }
+}
diff --git a/discovery/src/main/java/com/alibaba/fescar/discovery/registry/EurekaRegistryServiceImpl.java b/discovery/src/main/java/com/alibaba/fescar/discovery/registry/EurekaRegistryServiceImpl.java
index b4fb24c6b..ed72bd31b 100644
--- a/discovery/src/main/java/com/alibaba/fescar/discovery/registry/EurekaRegistryServiceImpl.java
+++ b/discovery/src/main/java/com/alibaba/fescar/discovery/registry/EurekaRegistryServiceImpl.java
@@ -22,12 +22,10 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-
 import com.alibaba.fescar.common.exception.EurekaRegistryException;
 import com.alibaba.fescar.common.util.NetUtil;
 import com.alibaba.fescar.config.Configuration;
 import com.alibaba.fescar.config.ConfigurationFactory;
-
 import com.google.common.collect.Lists;
 import com.netflix.appinfo.ApplicationInfoManager;
 import com.netflix.appinfo.InstanceInfo;
@@ -41,6 +39,8 @@ import com.netflix.discovery.EurekaEventListener;
 import com.netflix.discovery.shared.Application;
 import com.netflix.discovery.shared.Applications;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * The type Eureka registry service.
@@ -49,6 +49,7 @@ import org.apache.commons.lang3.StringUtils;
  * @date: 2018/3/6
  */
 public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventListener> {
+    private static final Logger LOGGER = LoggerFactory.getLogger(EurekaRegistryServiceImpl.class);
 
     private static final String DEFAULT_APPLICATION = "default";
     private static final String PRO_SERVICE_URL_KEY = "serviceUrl";
@@ -59,6 +60,7 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
     private static final String REGISTRY_WEIGHT = "weight";
     private static final String EUREKA_CONFIG_SERVER_URL_KEY = "eureka.serviceUrl.default";
     private static final String EUREKA_CONFIG_REFRESH_KEY = "eureka.client.refresh.interval";
+    private static final String EUREKA_CONFIG_SHOULD_REGISTER = "eureka.registration.enabled";
     private static final String EUREKA_CONFIG_METADATA_WEIGHT = "eureka.metadata.weight";
     private static final int EUREKA_REFRESH_INTERVAL = 5;
     private static final String DEFAULT_WEIGHT = "1";
@@ -95,7 +97,7 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
             instanceConfig.setApplicationName(getApplicationName());
             instanceConfig.setInstanceId(getInstanceId());
         }
-        getEurekaClient();
+        getEurekaClient(true);
         applicationInfoManager.setInstanceStatus(InstanceInfo.InstanceStatus.UP);
     }
 
@@ -111,16 +113,14 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
 
     @Override
     public void subscribe(String cluster, EurekaEventListener listener) throws Exception {
-        assertEureka();
         subscribeListener = true;
-        eurekaClient.registerEventListener(listener);
+        getEurekaClient(false).registerEventListener(listener);
     }
 
     @Override
     public void unsubscribe(String cluster, EurekaEventListener listener) throws Exception {
-        assertEureka();
         subscribeListener = false;
-        eurekaClient.unregisterEventListener(listener);
+        getEurekaClient(false).unregisterEventListener(listener);
     }
 
     @Override
@@ -131,14 +131,16 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
             return null;
         }
 
-        assertEureka();
-
         if (!subscribeListener) {
             refreshCluster();
             subscribe(null, new EurekaEventListener() {
                 @Override
                 public void onEvent(EurekaEvent event) {
-                    refreshCluster();
+                    try {
+                        refreshCluster();
+                    } catch (Exception e) {
+                        LOGGER.error("Eureka event listener refreshCluster error!");
+                    }
                 }
             });
         }
@@ -146,8 +148,8 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
         return Lists.newArrayList(CLUSTER_ADDRESS_MAP.get(clusterName.toUpperCase()));
     }
 
-    private static void refreshCluster() {
-        Applications applications = eurekaClient.getApplications();
+    private static void refreshCluster() throws EurekaRegistryException{
+        Applications applications = getEurekaClient(false).getApplications();
         List<Application> list = applications.getRegisteredApplications();
         if (list == null || list.isEmpty()) {
             CLUSTER_ADDRESS_MAP.clear();
@@ -172,7 +174,7 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
         eurekaClient = null;
     }
 
-    private static Properties getEurekaProperties() {
+    private static Properties getEurekaProperties(boolean needRegister) {
         Properties eurekaProperties = new Properties();
         eurekaProperties.setProperty(EUREKA_CONFIG_REFRESH_KEY, String.valueOf(EUREKA_REFRESH_INTERVAL));
 
@@ -189,6 +191,10 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
             eurekaProperties.setProperty(EUREKA_CONFIG_METADATA_WEIGHT, DEFAULT_WEIGHT);
         }
 
+        if (!needRegister) {
+            eurekaProperties.setProperty(EUREKA_CONFIG_SHOULD_REGISTER, "false");
+        }
+
         return eurekaProperties;
     }
 
@@ -200,16 +206,20 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
         return application;
     }
 
-    private static EurekaClient getEurekaClient() throws Exception {
+    private static EurekaClient getEurekaClient(boolean needRegister) throws EurekaRegistryException{
         if (eurekaClient == null) {
             synchronized (EurekaRegistryServiceImpl.class) {
                 try {
-                    if (eurekaClient == null) {
-                        ConfigurationManager.loadProperties(getEurekaProperties());
-                        InstanceInfo instanceInfo = new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();
-                        applicationInfoManager = new ApplicationInfoManager(instanceConfig, instanceInfo);
-                        eurekaClient = new DiscoveryClient(applicationInfoManager, new DefaultEurekaClientConfig());
+                    if (eurekaClient != null) {
+                        return eurekaClient;
                     }
+                    if (!needRegister){
+                        instanceConfig = new CustomEurekaInstanceConfig();
+                    }
+                    ConfigurationManager.loadProperties(getEurekaProperties(needRegister));
+                    InstanceInfo instanceInfo = new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();
+                    applicationInfoManager = new ApplicationInfoManager(instanceConfig, instanceInfo);
+                    eurekaClient = new DiscoveryClient(applicationInfoManager, new DefaultEurekaClientConfig());
                 } catch (Exception e) {
                     close();
                     throw new EurekaRegistryException("register eureka is error!", e);
@@ -219,12 +229,6 @@ public class EurekaRegistryServiceImpl implements RegistryService<EurekaEventLis
         return eurekaClient;
     }
 
-    private static void assertEureka() {
-        if (eurekaClient == null) {
-            throw new EurekaRegistryException("eureka client is not register,do this operation had to be register!");
-        }
-    }
-
     private static String getInstanceId() {
         return String.format("%s:%s:%d", instanceConfig.getAppname(), instanceConfig.getIpAddress(),
             instanceConfig.getNonSecurePort());
diff --git a/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/exec/InsertExecutor.java b/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/exec/InsertExecutor.java
index c059160af..8282cb61d 100644
--- a/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/exec/InsertExecutor.java
+++ b/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/exec/InsertExecutor.java
@@ -16,14 +16,6 @@
 
 package com.alibaba.fescar.rm.datasource.exec;
 
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
 import com.alibaba.fescar.common.exception.NotSupportYetException;
 import com.alibaba.fescar.common.exception.ShouldNeverHappenException;
 import com.alibaba.fescar.rm.datasource.PreparedStatementProxy;
@@ -31,17 +23,30 @@ import com.alibaba.fescar.rm.datasource.StatementProxy;
 import com.alibaba.fescar.rm.datasource.sql.SQLInsertRecognizer;
 import com.alibaba.fescar.rm.datasource.sql.SQLRecognizer;
 import com.alibaba.fescar.rm.datasource.sql.struct.ColumnMeta;
+import com.alibaba.fescar.rm.datasource.sql.struct.Null;
 import com.alibaba.fescar.rm.datasource.sql.struct.TableMeta;
 import com.alibaba.fescar.rm.datasource.sql.struct.TableRecords;
 
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 /**
  * The type Insert executor.
  *
  * @param <T> the type parameter
  * @param <S> the type parameter
+ * @author yuanguoyao
+ * @date 2019-03-21 21:30:02
  */
 public class InsertExecutor<T, S extends Statement> extends AbstractDMLBaseExecutor<T, S> {
 
+    protected static final String ERR_SQL_STATE = "S1009";
+
     /**
      * Instantiates a new Insert executor.
      *
@@ -61,75 +66,89 @@ public class InsertExecutor<T, S extends Statement> extends AbstractDMLBaseExecu
 
     @Override
     protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
+        //Pk column exists or PK is just auto generated
+        List<Object> pkValues = containsPK() ? getPkValuesByColumn() : getPkValuesByAuto();
+
+        TableRecords afterImage = getTableRecords(pkValues);
+
+        if (afterImage == null) {
+            throw new SQLException("Failed to build after-image for insert");
+        }
+
+        return afterImage;
+    }
+
+    protected boolean containsPK() {
         SQLInsertRecognizer recogizier = (SQLInsertRecognizer)sqlRecognizer;
         List<String> insertColumns = recogizier.getInsertColumns();
         TableMeta tmeta = getTableMeta();
-        TableRecords afterImage = null;
-        if (tmeta.containsPK(insertColumns)) {
-            // insert values including PK
-            List<Object> pkValues = null;
-            String pk = tmeta.getPkName();
-            for (int paramIdx = 0; paramIdx < insertColumns.size(); paramIdx++) {
-                if (insertColumns.get(paramIdx).equalsIgnoreCase(pk)) {
-                    if (statementProxy instanceof PreparedStatementProxy) {
-                        pkValues = ((PreparedStatementProxy)statementProxy).getParamsByIndex(paramIdx);
-                    } else {
-                        List<List<Object>> insertRows = recogizier.getInsertRows();
-                        pkValues = new ArrayList<>(insertRows.size());
-                        for (List<Object> row : insertRows) {
-                            pkValues.add(row.get(paramIdx));
-                        }
-                    }
-                    break;
-                }
-            }
-            if (pkValues == null) {
-                throw new ShouldNeverHappenException();
-            }
-            afterImage = getTableRecords(pkValues);
-
-        } else {
-            // PK is just auto generated
-            Map<String, ColumnMeta> pkMetaMap = getTableMeta().getPrimaryKeyMap();
-            if (pkMetaMap.size() != 1) {
-                throw new NotSupportYetException();
-            }
-            ColumnMeta pkMeta = pkMetaMap.values().iterator().next();
-            if (!pkMeta.isAutoincrement()) {
-                throw new ShouldNeverHappenException();
-            }
+        return tmeta.containsPK(insertColumns);
+    }
 
-            ResultSet genKeys = null;
-            try {
-                genKeys = statementProxy.getTargetStatement().getGeneratedKeys();
-            } catch (SQLException e) {
-                // java.sql.SQLException: Generated keys not requested. You need to
-                // specify Statement.RETURN_GENERATED_KEYS to
-                // Statement.executeUpdate() or Connection.prepareStatement().
-                if ("S1009".equalsIgnoreCase(e.getSQLState())) {
-                    genKeys = statementProxy.getTargetStatement().executeQuery("SELECT LAST_INSERT_ID()");
+    protected List<Object> getPkValuesByColumn() throws SQLException {
+        // insert values including PK
+        SQLInsertRecognizer recogizier = (SQLInsertRecognizer)sqlRecognizer;
+        List<String> insertColumns = recogizier.getInsertColumns();
+        String pk = getTableMeta().getPkName();
+        List<Object> pkValues = null;
+        for (int paramIdx = 0; paramIdx < insertColumns.size(); paramIdx++) {
+            if (insertColumns.get(paramIdx).equalsIgnoreCase(pk)) {
+                if (statementProxy instanceof PreparedStatementProxy) {
+                    pkValues = ((PreparedStatementProxy)statementProxy).getParamsByIndex(paramIdx);
                 } else {
-                    throw e;
+                    List<List<Object>> insertRows = recogizier.getInsertRows();
+                    pkValues = new ArrayList<>(insertRows.size());
+                    for (List<Object> row : insertRows) {
+                        pkValues.add(row.get(paramIdx));
+                    }
                 }
+                break;
             }
-            List<Object> pkValues = new ArrayList<>();
-            while (genKeys.next()) {
-                Object v = genKeys.getObject(1);
-                pkValues.add(v);
-            }
+        }
+        if (pkValues == null) {
+            throw new ShouldNeverHappenException();
+        }
+        //pk auto generated while column exists and value is null
+        if (pkValues.size() == 1 && pkValues.get(0) instanceof Null) {
+            pkValues = getPkValuesByAuto();
+        }
+        return pkValues;
+    }
 
-            afterImage = getTableRecords(pkValues);
 
+    protected List<Object> getPkValuesByAuto() throws SQLException {
+        // PK is just auto generated
+        Map<String, ColumnMeta> pkMetaMap = getTableMeta().getPrimaryKeyMap();
+        if (pkMetaMap.size() != 1) {
+            throw new NotSupportYetException();
         }
-
-        if (afterImage == null) {
-            throw new SQLException("Failed to build after-image for insert");
+        ColumnMeta pkMeta = pkMetaMap.values().iterator().next();
+        if (!pkMeta.isAutoincrement()) {
+            throw new ShouldNeverHappenException();
         }
 
-        return afterImage;
+        ResultSet genKeys = null;
+        try {
+            genKeys = statementProxy.getTargetStatement().getGeneratedKeys();
+        } catch (SQLException e) {
+            // java.sql.SQLException: Generated keys not requested. You need to
+            // specify Statement.RETURN_GENERATED_KEYS to
+            // Statement.executeUpdate() or Connection.prepareStatement().
+            if (ERR_SQL_STATE.equalsIgnoreCase(e.getSQLState())) {
+                genKeys = statementProxy.getTargetStatement().executeQuery("SELECT LAST_INSERT_ID()");
+            } else {
+                throw e;
+            }
+        }
+        List<Object> pkValues = new ArrayList<>();
+        while (genKeys.next()) {
+            Object v = genKeys.getObject(1);
+            pkValues.add(v);
+        }
+        return pkValues;
     }
 
-    private TableRecords getTableRecords(List<Object> pkValues) throws SQLException {
+    protected TableRecords getTableRecords(List<Object> pkValues) throws SQLException {
         TableRecords afterImage;
         String pk = getTableMeta().getPkName();
         StringBuffer selectSQLAppender = new StringBuffer("SELECT * FROM " + getTableMeta().getTableName() + " WHERE ");
diff --git a/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionalInterceptor.java b/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionalInterceptor.java
index 5e3b856b0..17aff9dc5 100644
--- a/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionalInterceptor.java
+++ b/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionalInterceptor.java
@@ -33,6 +33,9 @@ import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.core.BridgeMethodResolver;
+import org.springframework.util.ClassUtils;
 
 /**
  * The type Global transactional interceptor.
@@ -60,12 +63,15 @@ public class GlobalTransactionalInterceptor implements MethodInterceptor {
 
     @Override
     public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
-        final GlobalTransactional globalTrxAnno = getAnnotation(methodInvocation.getMethod(),
-            GlobalTransactional.class);
-        final GlobalLock globalLockAnno = getAnnotation(methodInvocation.getMethod(), GlobalLock.class);
-        if (globalTrxAnno != null) {
-            return handleGlobalTransaction(methodInvocation, globalTrxAnno);
-        } else if (globalLockAnno != null) {
+        Class<?> targetClass = (methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null);
+        Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
+        final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
+
+        final GlobalTransactional globalTransactionalAnnotation = getAnnotation(method, GlobalTransactional.class);
+        final GlobalLock globalLockAnnotation = getAnnotation(method, GlobalLock.class);
+        if (globalTransactionalAnnotation != null) {
+            return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
+        } else if (globalLockAnnotation != null) {
             return handleGlobalLock(methodInvocation);
         } else {
             return methodInvocation.proceed();


Test patch:
diff --git a/common/src/test/java/com/alibaba/fescar/common/thread/RejectedPolicysTest.java b/common/src/test/java/com/alibaba/fescar/common/thread/RejectedPolicysTest.java
new file mode 100644
index 000000000..64e90e715
--- /dev/null
+++ b/common/src/test/java/com/alibaba/fescar/common/thread/RejectedPolicysTest.java
@@ -0,0 +1,97 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  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
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  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.alibaba.fescar.common.thread;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Created by guoyao on 2019/2/26.
+ */
+public class RejectedPolicysTest {
+
+    private final int DEFAULT_CORE_POOL_SIZE = 1;
+    private final int DEFAULT_KEEP_ALIVE_TIME = 10;
+    private final int MAX_QUEUE_SIZE = 1;
+
+    /**
+     * Test runs oldest task policy.
+     *
+     * @throws Exception the exception
+     */
+    @Test
+    public void testRunsOldestTaskPolicy() throws Exception {
+        AtomicInteger atomicInteger = new AtomicInteger();
+        ThreadPoolExecutor poolExecutor =
+            new ThreadPoolExecutor(DEFAULT_CORE_POOL_SIZE, DEFAULT_CORE_POOL_SIZE, DEFAULT_KEEP_ALIVE_TIME,
+                TimeUnit.MILLISECONDS,
+                new LinkedBlockingQueue(MAX_QUEUE_SIZE),
+                new NamedThreadFactory("OldestRunsPolicy", DEFAULT_CORE_POOL_SIZE),
+                RejectedPolicys.runsOldestTaskPolicy());
+        CountDownLatch downLatch1 = new CountDownLatch(1);
+        CountDownLatch downLatch2 = new CountDownLatch(1);
+        CountDownLatch downLatch3 = new CountDownLatch(1);
+        //task1
+        poolExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    //wait the oldest task of queue count down
+                    downLatch1.await();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                atomicInteger.getAndAdd(1);
+            }
+        });
+        assertThat(atomicInteger.get()).isEqualTo(0);
+        //task2
+        poolExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                // run second
+                atomicInteger.getAndAdd(2);
+            }
+        });
+        //task3
+        poolExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                downLatch2.countDown();
+                //task3 run
+                atomicInteger.getAndAdd(3);
+                downLatch3.countDown();
+            }
+        });
+        //only the task2 run which is the oldest task of queue
+        assertThat(atomicInteger.get()).isEqualTo(2);
+        downLatch1.countDown();
+        downLatch2.await();
+        //wait task3 run +3
+        downLatch3.await();
+        //run task3
+        assertThat(atomicInteger.get()).isEqualTo(6);
+
+    }
+}
diff --git a/core/src/test/java/com/alibaba/fescar/core/protocol/transaction/GlobalBeginResponseTest.java b/core/src/test/java/com/alibaba/fescar/core/protocol/transaction/GlobalBeginResponseTest.java
new file mode 100644
index 000000000..84bbbafdd
--- /dev/null
+++ b/core/src/test/java/com/alibaba/fescar/core/protocol/transaction/GlobalBeginResponseTest.java
@@ -0,0 +1,70 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  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
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  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.alibaba.fescar.core.protocol.transaction;
+
+import com.alibaba.fescar.core.protocol.ResultCode;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A unit test for {@link GlobalBeginResponse}
+ *
+ * @author liujc
+ * @date 2019/3/22 11:09
+ **/
+public class GlobalBeginResponseTest {
+    private final String XID = "test_xid";
+    private final String EXTRA_DATA = "test_extra_data";
+    private final ResultCode RESULT_CODE = ResultCode.Success;
+
+    @Test
+    public void testGetSetXid() {
+        GlobalBeginResponse globalBeginResponse = new GlobalBeginResponse();
+        globalBeginResponse.setXid(XID);
+        Assert.assertEquals(XID, globalBeginResponse.getXid());
+    }
+
+    @Test
+    public void testGetSetExtraData() {
+        GlobalBeginResponse globalBeginResponse = new GlobalBeginResponse();
+        globalBeginResponse.setExtraData(EXTRA_DATA);
+        Assert.assertEquals(EXTRA_DATA, globalBeginResponse.getExtraData());
+    }
+
+    @Test
+    public void testGetTypeCode() {
+        GlobalBeginResponse globalBeginResponse = new GlobalBeginResponse();
+        Assert.assertEquals(GlobalBeginResponse.TYPE_GLOBAL_BEGIN_RESULT, globalBeginResponse.getTypeCode());
+    }
+
+    @Test
+    public void testDoEncodeAndDecode() {
+        GlobalBeginResponse globalBeginResponseOne = new GlobalBeginResponse();
+        globalBeginResponseOne.setXid(XID);
+        globalBeginResponseOne.setExtraData(EXTRA_DATA);
+        globalBeginResponseOne.setResultCode(RESULT_CODE);
+        byte[] encode = globalBeginResponseOne.encode();
+        GlobalBeginResponse globalBeginResponseTwo = new GlobalBeginResponse();
+        globalBeginResponseTwo.decode(ByteBuffer.wrap(encode));
+        assertThat(globalBeginResponseOne.getXid()).isEqualTo(globalBeginResponseTwo.getXid());
+        assertThat(globalBeginResponseOne.getExtraData()).isEqualTo(globalBeginResponseTwo.getExtraData());
+        assertThat(globalBeginResponseOne.getResultCode()).isEqualTo(globalBeginResponseTwo.getResultCode());
+    }
+}
diff --git a/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/exec/InsertExecutorTest.java b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/exec/InsertExecutorTest.java
new file mode 100644
index 000000000..633eea09c
--- /dev/null
+++ b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/exec/InsertExecutorTest.java
@@ -0,0 +1,275 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  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
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  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.alibaba.fescar.rm.datasource.exec;
+
+import com.alibaba.fescar.common.exception.NotSupportYetException;
+import com.alibaba.fescar.common.exception.ShouldNeverHappenException;
+import com.alibaba.fescar.rm.datasource.PreparedStatementProxy;
+import com.alibaba.fescar.rm.datasource.sql.SQLInsertRecognizer;
+import com.alibaba.fescar.rm.datasource.sql.struct.*;
+import org.assertj.core.api.Assertions;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.anyString;
+
+/**
+ * @author guoyao
+ * @date 2019/3/21
+ */
+public class InsertExecutorTest {
+
+    private static final String ID_COLUMN = "id";
+    private static final String USER_ID_COLUMN = "user_id";
+    private static final String USER_NAME_COLUMN = "user_name";
+    private static final Integer PK_VALUE = 100;
+
+    private PreparedStatementProxy statementProxy;
+
+    private SQLInsertRecognizer sqlInsertRecognizer;
+
+    private StatementCallback statementCallback;
+
+    private TableMeta tableMeta;
+
+    private InsertExecutor insertExecutor;
+
+    @Before
+    public void init() {
+        statementProxy = mock(PreparedStatementProxy.class);
+        statementCallback = mock(StatementCallback.class);
+        sqlInsertRecognizer = mock(SQLInsertRecognizer.class);
+        tableMeta =  mock(TableMeta.class);
+        insertExecutor = spy(new InsertExecutor(statementProxy, statementCallback, sqlInsertRecognizer));
+    }
+
+    @Test
+    public void testBeforeImage() throws SQLException {
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        TableRecords tableRecords = insertExecutor.beforeImage();
+        Assertions.assertThat(tableRecords.getRows()).isEmpty();
+        Assertions.assertThat(tableRecords.size()).isEqualTo(0);
+        try {
+            tableRecords.add(new Row());
+        } catch (Exception e) {
+            Assertions.assertThat(e instanceof UnsupportedOperationException).isTrue();
+        }
+        try {
+            tableRecords.getTableMeta();
+        } catch (Exception e) {
+            Assertions.assertThat(e instanceof UnsupportedOperationException).isTrue();
+        }
+    }
+
+    @Test
+    public void testAfterImage_ByColumn() throws SQLException {
+        doReturn(true).when(insertExecutor).containsPK();
+        List<Object> pkValues = new ArrayList<>();
+        pkValues.add(PK_VALUE);
+        doReturn(pkValues).when(insertExecutor).getPkValuesByColumn();
+        TableRecords tableRecords = new TableRecords();
+        doReturn(tableRecords).when(insertExecutor).getTableRecords(pkValues);
+        TableRecords resultTableRecords = insertExecutor.afterImage(new TableRecords());
+        Assertions.assertThat(resultTableRecords).isEqualTo(tableRecords);
+    }
+
+    @Test
+    public void testAfterImage_ByAuto() throws SQLException {
+        doReturn(false).when(insertExecutor).containsPK();
+        List<Object> pkValues = new ArrayList<>();
+        pkValues.add(PK_VALUE);
+        doReturn(pkValues).when(insertExecutor).getPkValuesByAuto();
+        TableRecords tableRecords = new TableRecords();
+        doReturn(tableRecords).when(insertExecutor).getTableRecords(pkValues);
+        TableRecords resultTableRecords = insertExecutor.afterImage(new TableRecords());
+        Assertions.assertThat(resultTableRecords).isEqualTo(tableRecords);
+    }
+
+    @Test(expected = SQLException.class)
+    public void testAfterImage_Exception() throws SQLException {
+        doReturn(false).when(insertExecutor).containsPK();
+        List<Object> pkValues = new ArrayList<>();
+        pkValues.add(PK_VALUE);
+        doReturn(pkValues).when(insertExecutor).getPkValuesByAuto();
+        doReturn(null).when(insertExecutor).getTableRecords(pkValues);
+        insertExecutor.afterImage(new TableRecords());
+    }
+
+    @Test
+    public void testContainsPK() {
+        List<String> insertColumns = mockInsertColumns();
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        when(tableMeta.containsPK(insertColumns)).thenReturn(true);
+        Assertions.assertThat(insertExecutor.containsPK()).isTrue();
+        when(tableMeta.containsPK(insertColumns)).thenReturn(false);
+        Assertions.assertThat(insertExecutor.containsPK()).isFalse();
+    }
+
+    @Test
+    public void testGetPkValuesByColumn() throws SQLException {
+        mockInsertColumns();
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        when(tableMeta.getPkName()).thenReturn(ID_COLUMN);
+        List<Object> pkValues = new ArrayList<>();
+        pkValues.add(PK_VALUE);
+        when(statementProxy.getParamsByIndex(0)).thenReturn(pkValues);
+        List pkValuesByColumn=insertExecutor.getPkValuesByColumn();
+        Assertions.assertThat(pkValuesByColumn).isEqualTo(pkValues);
+    }
+
+    @Test(expected = ShouldNeverHappenException.class)
+    public void testGetPkValuesByColumn_Exception() throws SQLException {
+        mockInsertColumns();
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        when(tableMeta.getPkName()).thenReturn(ID_COLUMN);
+        when(statementProxy.getParamsByIndex(0)).thenReturn(null);
+        insertExecutor.getPkValuesByColumn();
+    }
+
+    @Test
+    public void testGetPkValuesByColumn_PkValue_Null() throws SQLException {
+        mockInsertColumns();
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        when(tableMeta.getPkName()).thenReturn(ID_COLUMN);
+        List<Object> pkValuesNull = new ArrayList<>();
+        pkValuesNull.add(Null.get());
+        when(statementProxy.getParamsByIndex(0)).thenReturn(pkValuesNull);
+        List<Object> pkValuesAuto = new ArrayList<>();
+        pkValuesAuto.add(PK_VALUE);
+        //mock getPkValuesByAuto
+        doReturn(pkValuesAuto).when(insertExecutor).getPkValuesByAuto();
+        List pkValuesByColumn = insertExecutor.getPkValuesByColumn();
+        //pk value = Null so getPkValuesByAuto
+        verify(insertExecutor).getPkValuesByAuto();
+        Assertions.assertThat(pkValuesByColumn).isEqualTo(pkValuesAuto);
+    }
+
+    @Test(expected = NotSupportYetException.class)
+    public void testGetPkValuesByAuto_NotSupportYetException() throws SQLException {
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        Map<String, ColumnMeta> columnMetaMap = new HashMap<>();
+        columnMetaMap.put(ID_COLUMN, new ColumnMeta());
+        columnMetaMap.put(USER_ID_COLUMN, new ColumnMeta());
+        when(tableMeta.getPrimaryKeyMap()).thenReturn(columnMetaMap);
+        insertExecutor.getPkValuesByAuto();
+    }
+
+    @Test(expected = ShouldNeverHappenException.class)
+    public void testGetPkValuesByAuto_ShouldNeverHappenException() throws SQLException {
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        Map<String, ColumnMeta> columnMetaMap = new HashMap<>();
+        ColumnMeta columnMeta = mock(ColumnMeta.class);
+        columnMetaMap.put(ID_COLUMN, columnMeta);
+        when(columnMeta.isAutoincrement()).thenReturn(false);
+        when(tableMeta.getPrimaryKeyMap()).thenReturn(columnMetaMap);
+        insertExecutor.getPkValuesByAuto();
+    }
+
+    @Test(expected = SQLException.class)
+    public void testGetPkValuesByAuto_SQLException() throws SQLException {
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        ColumnMeta columnMeta = mock(ColumnMeta.class);
+        Map<String, ColumnMeta> columnMetaMap = new HashMap<>();
+        columnMetaMap.put(ID_COLUMN, columnMeta);
+        when(columnMeta.isAutoincrement()).thenReturn(true);
+        when(tableMeta.getPrimaryKeyMap()).thenReturn(columnMetaMap);
+        PreparedStatement preparedStatement = mock(PreparedStatement.class);
+        when(statementProxy.getTargetStatement()).thenReturn(preparedStatement);
+        when(preparedStatement.getGeneratedKeys()).thenThrow(new SQLException());
+        insertExecutor.getPkValuesByAuto();
+    }
+
+    @Test
+    public void testGetPkValuesByAuto_GeneratedKeys_NoResult() throws SQLException {
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        ColumnMeta columnMeta = mock(ColumnMeta.class);
+        Map<String, ColumnMeta> columnMetaMap = new HashMap<>();
+        columnMetaMap.put(ID_COLUMN, columnMeta);
+        when(columnMeta.isAutoincrement()).thenReturn(true);
+        when(tableMeta.getPrimaryKeyMap()).thenReturn(columnMetaMap);
+        PreparedStatement preparedStatement = mock(PreparedStatement.class);
+        when(statementProxy.getTargetStatement()).thenReturn(preparedStatement);
+        ResultSet resultSet = mock(ResultSet.class);
+        when(preparedStatement.getGeneratedKeys()).thenReturn(resultSet);
+        when(resultSet.next()).thenReturn(false);
+        when(resultSet.getObject(1)).thenReturn(PK_VALUE);
+        List pkValuesByAuto = insertExecutor.getPkValuesByAuto();
+        Assertions.assertThat(pkValuesByAuto).isEmpty();
+    }
+
+    @Test
+    public void testGetPkValuesByAuto_GeneratedKeys_HasResult() throws SQLException {
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        ColumnMeta columnMeta = mock(ColumnMeta.class);
+        Map<String, ColumnMeta> columnMetaMap = new HashMap<>();
+        columnMetaMap.put(ID_COLUMN, columnMeta);
+        when(columnMeta.isAutoincrement()).thenReturn(true);
+        when(tableMeta.getPrimaryKeyMap()).thenReturn(columnMetaMap);
+        PreparedStatement preparedStatement = mock(PreparedStatement.class);
+        when(statementProxy.getTargetStatement()).thenReturn(preparedStatement);
+        ResultSet resultSet = mock(ResultSet.class);
+        when(preparedStatement.getGeneratedKeys()).thenReturn(resultSet);
+        when(resultSet.next()).thenReturn(true).thenReturn(false);
+        when(resultSet.getObject(1)).thenReturn(PK_VALUE);
+        List<Object> pkValues = new ArrayList<>();
+        pkValues.add(PK_VALUE);
+        List pkValuesByAuto = insertExecutor.getPkValuesByAuto();
+        Assertions.assertThat(pkValuesByAuto).isEqualTo(pkValues);
+    }
+
+    @Test
+    public void testGetPkValuesByAuto_ExecuteQuery_HasResult() throws SQLException {
+        doReturn(tableMeta).when(insertExecutor).getTableMeta();
+        ColumnMeta columnMeta = mock(ColumnMeta.class);
+        Map<String, ColumnMeta> columnMetaMap = new HashMap<>();
+        columnMetaMap.put(ID_COLUMN, columnMeta);
+        when(columnMeta.isAutoincrement()).thenReturn(true);
+        when(tableMeta.getPrimaryKeyMap()).thenReturn(columnMetaMap);
+        PreparedStatement preparedStatement = mock(PreparedStatement.class);
+        when(statementProxy.getTargetStatement()).thenReturn(preparedStatement);
+        when(preparedStatement.getGeneratedKeys()).thenThrow(new SQLException("", InsertExecutor.ERR_SQL_STATE));
+        ResultSet resultSet = mock(ResultSet.class);
+        when(preparedStatement.executeQuery(anyString())).thenReturn(resultSet);
+        when(resultSet.next()).thenReturn(true).thenReturn(false);
+        when(resultSet.getObject(1)).thenReturn(PK_VALUE);
+        List<Object> pkValues = new ArrayList<>();
+        pkValues.add(PK_VALUE);
+        List pkValuesByAuto = insertExecutor.getPkValuesByAuto();
+        Assertions.assertThat(pkValuesByAuto).isEqualTo(pkValues);
+    }
+
+    private List<String> mockInsertColumns() {
+        List<String> columns = new ArrayList<>();
+        columns.add(ID_COLUMN);
+        columns.add(USER_ID_COLUMN);
+        columns.add(USER_NAME_COLUMN);
+        when(sqlInsertRecognizer.getInsertColumns()).thenReturn(columns);
+        return columns;
+    }
+}