diff --git a/pictures/multi-console.png b/pictures/multi-console.png new file mode 100644 index 0000000..876c645 Binary files /dev/null and b/pictures/multi-console.png differ diff --git a/pictures/multi-db-show.png b/pictures/multi-db-show.png new file mode 100644 index 0000000..384d34c Binary files /dev/null and b/pictures/multi-db-show.png differ diff --git a/pictures/multi-db1-show.png b/pictures/multi-db1-show.png new file mode 100644 index 0000000..2c70db1 Binary files /dev/null and b/pictures/multi-db1-show.png differ diff --git a/pictures/multi-db1.png b/pictures/multi-db1.png new file mode 100644 index 0000000..7953ceb Binary files /dev/null and b/pictures/multi-db1.png differ diff --git a/pictures/multi-db2-show.png b/pictures/multi-db2-show.png new file mode 100644 index 0000000..3ec044d Binary files /dev/null and b/pictures/multi-db2-show.png differ diff --git a/pictures/multi-db2.png b/pictures/multi-db2.png new file mode 100644 index 0000000..312bbc4 Binary files /dev/null and b/pictures/multi-db2.png differ diff --git a/pictures/multi-tran-fail.png b/pictures/multi-tran-fail.png new file mode 100644 index 0000000..47dcb9f Binary files /dev/null and b/pictures/multi-tran-fail.png differ diff --git a/spring-boot/springboot-druid-mybatis-multi/pom.xml b/spring-boot/springboot-druid-mybatis-multi/pom.xml new file mode 100644 index 0000000..cc44259 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + com.heibaiying + springboot-druid-mybatis-multi + 0.0.1-SNAPSHOT + springBoot-druid-mybatis-multi + Demo project for Spring Boot + + + 1.8 + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + + + mysql + mysql-connector-java + 6.0.6 + + + + com.alibaba + druid-spring-boot-starter + 1.1.10 + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-jta-atomikos + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/DruidMybatisMultiApplication.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/DruidMybatisMultiApplication.java new file mode 100644 index 0000000..078e1d0 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/DruidMybatisMultiApplication.java @@ -0,0 +1,15 @@ +package com.heibaiying.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class DruidMybatisMultiApplication { + + public static void main(String[] args) { + SpringApplication.run(DruidMybatisMultiApplication.class, args); + } + +} + diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/aop/DynamicDataSourceAspect.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/aop/DynamicDataSourceAspect.java new file mode 100644 index 0000000..a7f9f14 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/aop/DynamicDataSourceAspect.java @@ -0,0 +1,40 @@ +package com.heibaiying.springboot.aop; + +import com.heibaiying.springboot.config.DataSourceContextHolder; +import com.heibaiying.springboot.constant.Data; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +/** + * @author : heibaiying + * @description : 动态方式去切换数据源 + */ +@Aspect +@Component +public class DynamicDataSourceAspect { + + @Pointcut(value = "execution(* com.heibaiying.springboot.dao.*.*(..))") + public void dataSourcePointCut() { + } + + @Before(value = "dataSourcePointCut()") + public void beforeSwitchDS(JoinPoint point) { + + Object[] args = point.getArgs(); + + if (args == null || args.length < 1 || !Data.DATASOURCE2.equals(args[0])) { + DataSourceContextHolder.setDataKey(Data.DATASOURCE1); + } else { + DataSourceContextHolder.setDataKey(Data.DATASOURCE2); + } + } + + @After(value = "dataSourcePointCut()") + public void afterSwitchDS(JoinPoint point) { + DataSourceContextHolder.clearDataKey(); + } +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/bean/Programmer.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/bean/Programmer.java new file mode 100644 index 0000000..f5fb33e --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/bean/Programmer.java @@ -0,0 +1,34 @@ +package com.heibaiying.springboot.bean; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @author : heibaiying + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Programmer { + + private int id; + + private String name; + + private int age; + + private float salary; + + private Date birthday; + + public Programmer(String name, int age, float salary, Date birthday) { + this.name = name; + this.age = age; + this.salary = salary; + this.birthday = birthday; + } + +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/AbstractDataSourceConfig.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/AbstractDataSourceConfig.java new file mode 100644 index 0000000..62f3cb7 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/AbstractDataSourceConfig.java @@ -0,0 +1,47 @@ +package com.heibaiying.springboot.config; + +import com.atomikos.jdbc.AtomikosDataSourceBean; +import org.springframework.core.env.Environment; + +import javax.sql.DataSource; +import java.util.Properties; + +/** + * @author : heibaiying + * @description : + */ +public class AbstractDataSourceConfig { + + public DataSource getDataSource(Environment env, String prefix, String dataSourceName){ + Properties prop = build(env,prefix); + AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); + ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); + ds.setUniqueResourceName(dataSourceName); + ds.setXaProperties(prop); + return ds; + } + + public Properties build(Environment env, String prefix) { + Properties prop = new Properties(); + prop.put("url", env.getProperty(prefix + "url")); + prop.put("username", env.getProperty(prefix + "username")); + prop.put("password", env.getProperty(prefix + "password")); + prop.put("driverClassName", env.getProperty(prefix + "driver-class-name", "")); + prop.put("initialSize", env.getProperty(prefix + "initialSize", Integer.class)); + prop.put("maxActive", env.getProperty(prefix + "maxActive", Integer.class)); + prop.put("minIdle", env.getProperty(prefix + "minIdle", Integer.class)); + prop.put("maxWait", env.getProperty(prefix + "maxWait", Integer.class)); + //prop.put("poolPreparedStatements", env.getProperty(prefix + "poolPreparedStatements", Boolean.class)); + //prop.put("maxPoolPreparedStatementPerConnectionSize",env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class)); + prop.put("validationQuery", env.getProperty(prefix + "validationQuery")); + //prop.put("validationQueryTimeout", env.getProperty(prefix + "validationQueryTimeout", Integer.class)); + prop.put("testOnBorrow", env.getProperty(prefix + "testOnBorrow", Boolean.class)); + prop.put("testOnReturn", env.getProperty(prefix + "testOnReturn", Boolean.class)); + prop.put("testWhileIdle", env.getProperty(prefix + "testWhileIdle", Boolean.class)); + prop.put("timeBetweenEvictionRunsMillis", env.getProperty(prefix + "timeBetweenEvictionRunsMillis", Integer.class)); + prop.put("minEvictableIdleTimeMillis", env.getProperty(prefix + "minEvictableIdleTimeMillis", Integer.class)); + //prop.put("useGlobalDataSourceStat",env.getProperty(prefix + "useGlobalDataSourceStat", Boolean.class)); + //prop.put("filters", env.getProperty(prefix + "filters")); + return prop; + } +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/CustomSqlSessionTemplate.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/CustomSqlSessionTemplate.java new file mode 100644 index 0000000..759c552 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/CustomSqlSessionTemplate.java @@ -0,0 +1,739 @@ +package com.heibaiying.springboot.config; + +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.exceptions.PersistenceException; +import org.apache.ibatis.executor.BatchResult; +import org.apache.ibatis.session.*; +import org.mybatis.spring.MyBatisExceptionTranslator; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.util.Assert; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.util.List; +import java.util.Map; + +import static java.lang.reflect.Proxy.newProxyInstance; +import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable; +import static org.mybatis.spring.SqlSessionUtils.*; + +/*** + * @description: 自定义 sqlSessionTemplate 实现 + * 这个方法继承自sqlSessionTemplate,主要是重写了getSqlSessionFactory方法,并在getSqlSession时候传入我们用此方法得到的SqlSessionFactory + */ + +@Slf4j +public class CustomSqlSessionTemplate extends SqlSessionTemplate { +/* + + + private final SqlSessionFactory sqlSessionFactory; + private final ExecutorType executorType; + private final SqlSession sqlSessionProxy; + private final PersistenceExceptionTranslator exceptionTranslator; + + private Map targetSqlSessionFactories; + private SqlSessionFactory defaultTargetSqlSessionFactory; + + + public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { + this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); + } + + public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { + this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration() + .getEnvironment().getDataSource(), true)); + } + + public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, + PersistenceExceptionTranslator exceptionTranslator) { + + super(sqlSessionFactory, executorType, exceptionTranslator); + + this.sqlSessionFactory = sqlSessionFactory; + this.executorType = executorType; + this.exceptionTranslator = exceptionTranslator; + + this.sqlSessionProxy = (SqlSession) newProxyInstance( + SqlSessionFactory.class.getClassLoader(), + new Class[]{SqlSession.class}, + new SqlSessionInterceptor()); + + this.defaultTargetSqlSessionFactory = sqlSessionFactory; + } + + public void setTargetSqlSessionFactories(Map targetSqlSessionFactories) { + this.targetSqlSessionFactories = targetSqlSessionFactories; + } + + public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { + this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; + } + + */ +/*** + * 获取当前使用数据源对应的会话工厂 + *//* + + @Override + public SqlSessionFactory getSqlSessionFactory() { + + String dataSourceKey = DataSourceContextHolder.getDataKey(); + log.info("SqlSessionFactory key : {}",dataSourceKey); + SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactories.get(dataSourceKey); + if (targetSqlSessionFactory != null) { + return targetSqlSessionFactory; + } else if (defaultTargetSqlSessionFactory != null) { + return defaultTargetSqlSessionFactory; + } else { + Assert.notNull(targetSqlSessionFactories, "Property 'targetSqlSessionFactories' or 'defaultTargetSqlSessionFactory' are required"); + Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactories' are required"); + } + return this.sqlSessionFactory; + } + + + */ +/** + * 这个方法的实现和父类的实现是基本一致的,唯一不同的就是在getSqlSession方法传参中获取会话工厂的方式 + *//* + + private class SqlSessionInterceptor implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + //在getSqlSession传参时候,用我们重写的getSqlSessionFactory获取当前数据源对应的会话工厂 + final SqlSession sqlSession = getSqlSession( + CustomSqlSessionTemplate.this.getSqlSessionFactory(), + CustomSqlSessionTemplate.this.executorType, + CustomSqlSessionTemplate.this.exceptionTranslator); + try { + Object result = method.invoke(sqlSession, args); + if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) { + sqlSession.commit(true); + } + return result; + } catch (Throwable t) { + Throwable unwrapped = unwrapThrowable(t); + if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { + Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator + .translateExceptionIfPossible((PersistenceException) unwrapped); + if (translated != null) { + unwrapped = translated; + } + } + throw unwrapped; + } finally { + closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory()); + } + } + } + + public ExecutorType getExecutorType() { + return this.executorType; + } + + public PersistenceExceptionTranslator getPersistenceExceptionTranslator() { + return this.exceptionTranslator; + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public T selectOne(String statement) { + return this.sqlSessionProxy. selectOne(statement); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public T selectOne(String statement, Object parameter) { + return this.sqlSessionProxy. selectOne(statement, parameter); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public Map selectMap(String statement, String mapKey) { + return this.sqlSessionProxy. selectMap(statement, mapKey); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public Map selectMap(String statement, Object parameter, String mapKey) { + return this.sqlSessionProxy. selectMap(statement, parameter, mapKey); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { + return this.sqlSessionProxy. selectMap(statement, parameter, mapKey, rowBounds); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public Cursor selectCursor(String statement) { + return this.sqlSessionProxy.selectCursor(statement); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public Cursor selectCursor(String statement, Object parameter) { + return this.sqlSessionProxy.selectCursor(statement, parameter); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds) { + return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public List selectList(String statement) { + return this.sqlSessionProxy. selectList(statement); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public List selectList(String statement, Object parameter) { + return this.sqlSessionProxy. selectList(statement, parameter); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public List selectList(String statement, Object parameter, RowBounds rowBounds) { + return this.sqlSessionProxy. selectList(statement, parameter, rowBounds); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void select(String statement, ResultHandler handler) { + this.sqlSessionProxy.select(statement, handler); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void select(String statement, Object parameter, ResultHandler handler) { + this.sqlSessionProxy.select(statement, parameter, handler); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { + this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public int insert(String statement) { + return this.sqlSessionProxy.insert(statement); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public int insert(String statement, Object parameter) { + return this.sqlSessionProxy.insert(statement, parameter); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public int update(String statement) { + return this.sqlSessionProxy.update(statement); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public int update(String statement, Object parameter) { + return this.sqlSessionProxy.update(statement, parameter); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public int delete(String statement) { + return this.sqlSessionProxy.delete(statement); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public int delete(String statement, Object parameter) { + return this.sqlSessionProxy.delete(statement, parameter); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public T getMapper(Class type) { + return getConfiguration().getMapper(type, this); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void commit() { + throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void commit(boolean force) { + throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void rollback() { + throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void rollback(boolean force) { + throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void close() { + throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public void clearCache() { + this.sqlSessionProxy.clearCache(); + } + + */ +/** + * {@inheritDoc} + * + *//* + + @Override + public Configuration getConfiguration() { + return this.sqlSessionFactory.getConfiguration(); + } + + */ +/** + * {@inheritDoc} + *//* + + @Override + public Connection getConnection() { + return this.sqlSessionProxy.getConnection(); + } + + */ +/** + * {@inheritDoc} + * + * @since 1.0.2 + * + *//* + + @Override + public List flushStatements() { + return this.sqlSessionProxy.flushStatements(); + } + + @Override + public void destroy() throws Exception { + //This method forces spring disposer to avoid call of SqlSessionTemplate.close() which gives UnsupportedOperationException + } +*/ +private final SqlSessionFactory sqlSessionFactory; + private final ExecutorType executorType; + private final SqlSession sqlSessionProxy; + private final PersistenceExceptionTranslator exceptionTranslator; + + private Map targetSqlSessionFactorys; + private SqlSessionFactory defaultTargetSqlSessionFactory; + + public void setTargetSqlSessionFactorys(Map targetSqlSessionFactorys) { + this.targetSqlSessionFactorys = targetSqlSessionFactorys; + } + + public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { + this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; + } + + public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { + this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); + } + + public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { + this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration() + .getEnvironment().getDataSource(), true)); + } + + public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, + PersistenceExceptionTranslator exceptionTranslator) { + + super(sqlSessionFactory, executorType, exceptionTranslator); + + this.sqlSessionFactory = sqlSessionFactory; + this.executorType = executorType; + this.exceptionTranslator = exceptionTranslator; + + this.sqlSessionProxy = (SqlSession) newProxyInstance( + SqlSessionFactory.class.getClassLoader(), + new Class[] { SqlSession.class }, + new SqlSessionInterceptor()); + + this.defaultTargetSqlSessionFactory = sqlSessionFactory; + } + + @Override + public SqlSessionFactory getSqlSessionFactory() { + + String dataSourceKey = DataSourceContextHolder.getDataKey(); + System.out.println("当前会话工厂 : "+dataSourceKey); + SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(dataSourceKey); + if (targetSqlSessionFactory != null) { + return targetSqlSessionFactory; + } else if (defaultTargetSqlSessionFactory != null) { + return defaultTargetSqlSessionFactory; + } else { + Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required"); + Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required"); + } + return this.sqlSessionFactory; + } + + @Override + public Configuration getConfiguration() { + return this.getSqlSessionFactory().getConfiguration(); + } + + public ExecutorType getExecutorType() { + return this.executorType; + } + + public PersistenceExceptionTranslator getPersistenceExceptionTranslator() { + return this.exceptionTranslator; + } + + /** + * {@inheritDoc} + */ + public T selectOne(String statement) { + return this.sqlSessionProxy. selectOne(statement); + } + + /** + * {@inheritDoc} + */ + public T selectOne(String statement, Object parameter) { + return this.sqlSessionProxy. selectOne(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public Map selectMap(String statement, String mapKey) { + return this.sqlSessionProxy. selectMap(statement, mapKey); + } + + /** + * {@inheritDoc} + */ + public Map selectMap(String statement, Object parameter, String mapKey) { + return this.sqlSessionProxy. selectMap(statement, parameter, mapKey); + } + + /** + * {@inheritDoc} + */ + public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { + return this.sqlSessionProxy. selectMap(statement, parameter, mapKey, rowBounds); + } + + /** + * {@inheritDoc} + */ + public List selectList(String statement) { + return this.sqlSessionProxy. selectList(statement); + } + + /** + * {@inheritDoc} + */ + public List selectList(String statement, Object parameter) { + return this.sqlSessionProxy. selectList(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public List selectList(String statement, Object parameter, RowBounds rowBounds) { + return this.sqlSessionProxy. selectList(statement, parameter, rowBounds); + } + + /** + * {@inheritDoc} + */ + public void select(String statement, ResultHandler handler) { + this.sqlSessionProxy.select(statement, handler); + } + + /** + * {@inheritDoc} + */ + public void select(String statement, Object parameter, ResultHandler handler) { + this.sqlSessionProxy.select(statement, parameter, handler); + } + + /** + * {@inheritDoc} + */ + public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { + this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); + } + + /** + * {@inheritDoc} + */ + public int insert(String statement) { + return this.sqlSessionProxy.insert(statement); + } + + /** + * {@inheritDoc} + */ + public int insert(String statement, Object parameter) { + return this.sqlSessionProxy.insert(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public int update(String statement) { + return this.sqlSessionProxy.update(statement); + } + + /** + * {@inheritDoc} + */ + public int update(String statement, Object parameter) { + return this.sqlSessionProxy.update(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public int delete(String statement) { + return this.sqlSessionProxy.delete(statement); + } + + /** + * {@inheritDoc} + */ + public int delete(String statement, Object parameter) { + return this.sqlSessionProxy.delete(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public T getMapper(Class type) { + return getConfiguration().getMapper(type, this); + } + + /** + * {@inheritDoc} + */ + public void commit() { + throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void commit(boolean force) { + throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void rollback() { + throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void rollback(boolean force) { + throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void close() { + throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void clearCache() { + this.sqlSessionProxy.clearCache(); + } + + /** + * {@inheritDoc} + */ + public Connection getConnection() { + return this.sqlSessionProxy.getConnection(); + } + + /** + * {@inheritDoc} + * @since 1.0.2 + */ + public List flushStatements() { + return this.sqlSessionProxy.flushStatements(); + } + + /** + * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also + * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to + * the {@code PersistenceExceptionTranslator}. + */ + private class SqlSessionInterceptor implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + final SqlSession sqlSession = getSqlSession( + CustomSqlSessionTemplate.this.getSqlSessionFactory(), + CustomSqlSessionTemplate.this.executorType, + CustomSqlSessionTemplate.this.exceptionTranslator); + try { + Object result = method.invoke(sqlSession, args); + if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) { + // force commit even on non-dirty sessions because some databases require + // a commit/rollback before calling close() + sqlSession.commit(true); + } + return result; + } catch (Throwable t) { + Throwable unwrapped = unwrapThrowable(t); + if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { + Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator + .translateExceptionIfPossible((PersistenceException) unwrapped); + if (translated != null) { + unwrapped = translated; + } + } + throw unwrapped; + } finally { + closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory()); + } + } + } + +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DataSourceConfig.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DataSourceConfig.java new file mode 100644 index 0000000..d33673e --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DataSourceConfig.java @@ -0,0 +1,145 @@ +package com.heibaiying.springboot.config; + +import com.alibaba.druid.pool.xa.DruidXADataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import com.heibaiying.springboot.constant.Data; +import org.apache.ibatis.logging.stdout.StdOutImpl; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * @author : heibaiying + * @description : 多数据源配置 + * 需要特别强调的是以下任意的DataSource的bean都不要用@Primary去修饰,包括动态数据源。原因是在事务下,spring在DataSourceUtils类中doGetConnection方法中判断获取当前数据源是否持有连接的时候, + * 总是会传入用@Primary修饰的数据源,如果一个事务下有多个数据源,这种情况下就会出现connection复用,无法切换数据源, 源码截图详见README.md + */ +@Configuration +@MapperScan(basePackages = DataSourceConfig.BASE_PACKAGES, sqlSessionTemplateRef = "sqlSessionTemplate") +public class DataSourceConfig extends AbstractDataSourceConfig { + + static final String BASE_PACKAGES = "com.heibaiying.springboot.dao"; + + private static final String MAPPER_LOCATION = "classpath:mappers/*.xml"; + + /** + * 创建数据源1,不要用@Primary去修饰 + */ + @Bean + public DataSource dataSourceOne(Environment env) { + String prefix = "spring.datasource.druid.db1."; + return getDataSource(env, prefix, "one"); + } + + /** + * 创建数据源2,不要用@Primary去修饰 + */ + @Bean + public DataSource dataSourceTwo(Environment env) { + String prefix = "spring.datasource.druid.db2."; + return getDataSource(env, prefix, "two"); + } + + + /* @Bean + @ConfigurationProperties("spring.datasource.druid.db1") + public DataSource dataSourceOne() { + return new DruidXADataSource(); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.db2") + public DataSource dataSourceTwo() { + return new DruidXADataSource(); + }*/ + + /** + * 设置多数据源,不要用@Primary去修饰 + */ + @Bean + public DataSource dynamicDataSource(DataSource dataSourceOne, DataSource dataSourceTwo) { + DynamicDataSource dynamicDataSource = new DynamicDataSource(); + // 设置默认数据源 + dynamicDataSource.setDefaultTargetDataSource(dataSourceOne); + // 设置多数据源 + Map dsMap = new HashMap<>(); + dsMap.put(Data.DATASOURCE1, dataSourceOne); + dsMap.put(Data.DATASOURCE2, dataSourceTwo); + + dynamicDataSource.setTargetDataSources(dsMap); + return dynamicDataSource; + } + + + /** + * @param dataSourceOne 数据源1 + * @return 数据源1的会话工厂 + */ + @Bean + public SqlSessionFactory sqlSessionFactoryOne(DataSource dataSourceOne) + throws Exception { + return createSqlSessionFactory(dataSourceOne); + } + + + /** + * @param dataSourceTwo 数据源2 + * @return 数据源2的会话工厂 + */ + @Bean + public SqlSessionFactory sqlSessionFactoryTwo(DataSource dataSourceTwo) + throws Exception { + return createSqlSessionFactory(dataSourceTwo); + } + + + /*** + * sqlSessionTemplate与Spring事务管理一起使用,以确保使用的实际SqlSession是与当前Spring事务关联的, + * 此外它还管理会话生命周期,包括根据Spring事务配置根据需要关闭,提交或回滚会话 + * @param sqlSessionFactoryOne 数据源1 + * @param sqlSessionFactoryTwo 数据源2 + */ + @Bean + public CustomSqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactoryOne, + SqlSessionFactory sqlSessionFactoryTwo) { + Map sqlSessionFactoryMap = new HashMap<>(); + sqlSessionFactoryMap.put(Data.DATASOURCE1, sqlSessionFactoryOne); + sqlSessionFactoryMap.put(Data.DATASOURCE2, sqlSessionFactoryTwo); + + CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(sqlSessionFactoryOne); + customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap); + return customSqlSessionTemplate; + } + + /*** + * 自定义会话工厂 + * @param dataSource 数据源 + * @return :自定义的会话工厂 + */ + private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception { + SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION)); + // 其他可配置项(包括是否打印sql,是否开启驼峰命名等) + org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); + configuration.setMapUnderscoreToCamelCase(true); + configuration.setLogImpl(StdOutImpl.class); + factoryBean.setConfiguration(configuration); + /* * + * 采用个如下方式配置属性的时候一定要保证已经进行数据源的配置(setDataSource)和数据源和MapperLocation配置(setMapperLocations) + * factoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true); + * factoryBean.getObject().getConfiguration().setLogImpl(StdOutImpl.class); + **/ + return factoryBean.getObject(); + } +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DataSourceContextHolder.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DataSourceContextHolder.java new file mode 100644 index 0000000..fcc4d01 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DataSourceContextHolder.java @@ -0,0 +1,25 @@ +package com.heibaiying.springboot.config; + +/** + * @author : heibaiying + * @description : 保证数据源切换的线程隔离 + */ +public class DataSourceContextHolder { + + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + // 设置数据源名 + public static void setDataKey(String dbName) { + contextHolder.set(dbName); + } + + // 获取数据源名 + public static String getDataKey() { + return (contextHolder.get()); + } + + // 清除数据源名 + public static void clearDataKey() { + contextHolder.remove(); + } +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DynamicDataSource.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DynamicDataSource.java new file mode 100644 index 0000000..185ca0a --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/DynamicDataSource.java @@ -0,0 +1,16 @@ +package com.heibaiying.springboot.config; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * @author : heibaiying + * @description : 动态数据源配置 + */ + +public class DynamicDataSource extends AbstractRoutingDataSource { + @Override + protected Object determineCurrentLookupKey() { + System.out.println("当前数据库:"+DataSourceContextHolder.getDataKey()); + return DataSourceContextHolder.getDataKey(); + } +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/XATransactionManagerConfig.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/XATransactionManagerConfig.java new file mode 100644 index 0000000..180cd4a --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/config/XATransactionManagerConfig.java @@ -0,0 +1,42 @@ +package com.heibaiying.springboot.config; + +import com.atomikos.icatch.jta.UserTransactionImp; +import com.atomikos.icatch.jta.UserTransactionManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.jta.JtaTransactionManager; + +import javax.transaction.TransactionManager; +import javax.transaction.UserTransaction; + +/** + * @author : heibaiying + * @description : 分布式事务配置 + */ +@Configuration +@EnableTransactionManagement +public class XATransactionManagerConfig { + + @Bean + public UserTransaction userTransaction() throws Throwable { + UserTransactionImp userTransactionImp = new UserTransactionImp(); + userTransactionImp.setTransactionTimeout(10000); + return userTransactionImp; + } + + @Bean(initMethod = "init", destroyMethod = "close") + public TransactionManager atomikosTransactionManager() { + UserTransactionManager userTransactionManager = new UserTransactionManager(); + userTransactionManager.setForceShutdown(false); + return userTransactionManager; + } + + @Bean + public PlatformTransactionManager transactionManager(UserTransaction userTransaction, + TransactionManager transactionManager) { + return new JtaTransactionManager(userTransaction, transactionManager); + } +} + diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/constant/Data.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/constant/Data.java new file mode 100644 index 0000000..5a714ec --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/constant/Data.java @@ -0,0 +1,11 @@ +package com.heibaiying.springboot.constant; + +/** + * @author : heibaiying + * @description : + */ +public class Data { + + public static final String DATASOURCE1="DATASOURCE1"; + public static final String DATASOURCE2="DATASOURCE2"; +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/TestController.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/TestController.java new file mode 100644 index 0000000..558995f --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/TestController.java @@ -0,0 +1,61 @@ +package com.heibaiying.springboot.controller; + +import com.heibaiying.springboot.bean.Programmer; +import com.heibaiying.springboot.constant.Data; +import com.heibaiying.springboot.dao.ProgrammerMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author : heibaiying + * @description : 测试controller + */ + +@RestController +public class TestController { + + + @Autowired + private ProgrammerMapper programmerDao; + + /** + * 查询全部数据源的数据 + */ + @GetMapping("/db/programmers") + public List getAllProgrammers() { + List programmers = programmerDao.selectAll(Data.DATASOURCE1); + programmers.addAll(programmerDao.selectAll(Data.DATASOURCE2)); + return programmers; + } + + @GetMapping("ts/db/programmers") + @Transactional + public List getAllProgrammersTs() { + List programmers = programmerDao.selectAll(Data.DATASOURCE1); + programmers.addAll(programmerDao.selectAll(Data.DATASOURCE2)); + return programmers; + } + + /** + * 不指定就使用默认的数据源 + */ + @GetMapping("/db1/programmers") + public List getDB1Programmers() { + + return programmerDao.selectAll(null); + } + + /** + * 从指定数据源查询 + */ + @GetMapping("/db2/programmers") + public List getDB2Programmers() { + return programmerDao.selectAll(Data.DATASOURCE2); + } + + +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/TransactionController.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/TransactionController.java new file mode 100644 index 0000000..9c09fab --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/TransactionController.java @@ -0,0 +1,40 @@ +package com.heibaiying.springboot.controller; + +import com.heibaiying.springboot.bean.Programmer; +import com.heibaiying.springboot.constant.Data; +import com.heibaiying.springboot.dao.ProgrammerMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + +/** + * @author : heibaiying + * @description : 测试单数据库事务 + */ +@RestController +public class TransactionController { + + @Autowired + private ProgrammerMapper programmerDao; + + + @RequestMapping("db1/change") + @Transactional + public void changeDb1() { + Programmer programmer = new Programmer(1, "xiaolandb1", 99, 6662.32f, new Date()); + programmerDao.modify(Data.DATASOURCE1, programmer); + } + + @RequestMapping("ts/db1/change") + @Transactional + public void changeTsDb1() { + Programmer programmer = new Programmer(1, "xiaolandb1", 88, 6662.32f, new Date()); + programmerDao.modify(Data.DATASOURCE1, programmer); + // 抛出异常 查看回滚 + int j = 1 / 0; + } + +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/XATransactionController.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/XATransactionController.java new file mode 100644 index 0000000..997a106 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/controller/XATransactionController.java @@ -0,0 +1,44 @@ +package com.heibaiying.springboot.controller; + +import com.heibaiying.springboot.bean.Programmer; +import com.heibaiying.springboot.constant.Data; +import com.heibaiying.springboot.dao.ProgrammerMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + +/** + * @author : heibaiying + * @description : 测试分布式事务 + */ +@RestController +public class XATransactionController { + + @Autowired + private ProgrammerMapper programmerDao; + + + @RequestMapping("/db/change") + @Transactional + public void changeDb() { + Programmer programmer01 = new Programmer(1, "xiaolandb1", 100, 6662.32f, new Date()); + Programmer programmer02 = new Programmer(1, "xiaohongdb2", 100, 6662.32f, new Date()); + programmerDao.modify(Data.DATASOURCE1, programmer01); + programmerDao.modify(Data.DATASOURCE2, programmer02); + } + + @RequestMapping("ts/db/change") + @Transactional + public void changeTsDb() { + Programmer programmer01 = new Programmer(1, "xiaolandb1", 99, 6662.32f, new Date()); + Programmer programmer02 = new Programmer(1, "xiaohongdb2", 99, 6662.32f, new Date()); + programmerDao.modify(Data.DATASOURCE1, programmer01); + programmerDao.modify(Data.DATASOURCE2, programmer02); + int i = 1 / 0; + } + + +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/dao/ProgrammerMapper.java b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/dao/ProgrammerMapper.java new file mode 100644 index 0000000..79a97ba --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/java/com/heibaiying/springboot/dao/ProgrammerMapper.java @@ -0,0 +1,24 @@ +package com.heibaiying.springboot.dao; + +import com.heibaiying.springboot.bean.Programmer; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author : heibaiying + */ +@Mapper +public interface ProgrammerMapper { + + List selectAll(String dataSource); + + void save(Programmer programmer); + + Programmer selectById(int id); + + int modify(String dataSource,@Param("pro") Programmer programmer); + + void delete(int id); +} diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/resources/application.yml b/spring-boot/springboot-druid-mybatis-multi/src/main/resources/application.yml new file mode 100644 index 0000000..cd311b8 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/resources/application.yml @@ -0,0 +1,113 @@ +spring: + datasource: + druid: + db1: + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + # 使用 druid 作为连接池 + type: com.alibaba.druid.pool.DruidDataSource + + # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 + initialSize: 5 + # 最小连接池数量 + minIdle: 5 + # 最大连接池数量 + maxActive: 10 + # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 + maxWait: 60000 + # Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 + timeBetweenEvictionRunsMillis: 60000 + # 连接保持空闲而不被驱逐的最小时间 + minEvictableIdleTimeMillis: 300000 + # 用来检测连接是否有效的sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL + validationQuery: SELECT 9 + # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 + testWhileIdle: true + # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnBorrow: false + # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnReturn: false + # 是否自动回收超时连接 + removeAbandoned: true + # 超时时间(以秒数为单位) + remove-abandoned-timeout: 180 + # WebStatFilter用于采集web-jdbc关联监控的数据。 + web-stat-filter: + # 是否开启 WebStatFilter 默认是true + enabled: true + # 需要拦截的url + url-pattern: /* + # 排除静态资源的请求 + exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" + + # Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。 + stat-view-servlet: + #是否启用StatViewServlet 默认值true + enabled: true + # 需要拦截的url + url-pattern: /druid/* + # 允许清空统计数据 + reset-enable: true + login-username: druid + login-password: druid + db2: + url: jdbc:mysql://127.0.0.1:3306/mysql02?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + # 使用 druid 作为连接池 + type: com.alibaba.druid.pool.DruidDataSource + + # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 + initialSize: 5 + # 最小连接池数量 + minIdle: 5 + # 最大连接池数量 + maxActive: 10 + # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 + maxWait: 60000 + # Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 + timeBetweenEvictionRunsMillis: 60000 + # 连接保持空闲而不被驱逐的最小时间 + minEvictableIdleTimeMillis: 300000 + # 用来检测连接是否有效的sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL + validationQuery: SELECT 9 + # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 + testWhileIdle: true + # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnBorrow: false + # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnReturn: false + # 是否自动回收超时连接 + removeAbandoned: true + # 超时时间(以秒数为单位) + remove-abandoned-timeout: 180 + # WebStatFilter用于采集web-jdbc关联监控的数据。 + web-stat-filter: + # 是否开启 WebStatFilter 默认是true + enabled: true + # 需要拦截的url + url-pattern: /* + # 排除静态资源的请求 + exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" + + # Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。 + stat-view-servlet: + #是否启用StatViewServlet 默认值true + enabled: true + # 需要拦截的url + url-pattern: /druid/* + # 允许清空统计数据 + reset-enable: true + login-username: druid + login-password: druid +# mybatis 相关配置 +mybatis: + configuration: + # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 + # oracle数据库建议配置为JdbcType.NULL, 默认是Other + jdbc-type-for-null: 'null' + # 是否打印sql语句 调试的时候可以开启 + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file diff --git a/spring-boot/springboot-druid-mybatis-multi/src/main/resources/mappers/ProgrammerMapper.xml b/spring-boot/springboot-druid-mybatis-multi/src/main/resources/mappers/ProgrammerMapper.xml new file mode 100644 index 0000000..e14af21 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/main/resources/mappers/ProgrammerMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday}) + + + + + + update programmer set name=#{pro.name},age=#{pro.age},salary=#{pro.salary},birthday=#{pro.birthday} where id=#{pro.id} + + + + delete from programmer where id = #{id} + + + \ No newline at end of file diff --git a/spring-boot/springboot-druid-mybatis-multi/src/test/java/com/heibaiying/springboot/SpringbootDruidMybatisMultiApplicationTests.java b/spring-boot/springboot-druid-mybatis-multi/src/test/java/com/heibaiying/springboot/SpringbootDruidMybatisMultiApplicationTests.java new file mode 100644 index 0000000..6571538 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/src/test/java/com/heibaiying/springboot/SpringbootDruidMybatisMultiApplicationTests.java @@ -0,0 +1,17 @@ +package com.heibaiying.springboot; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringbootDruidMybatisMultiApplicationTests { + + @Test + public void contextLoads() { + } + +} + diff --git a/spring-boot/springboot-druid-mybatis-multi/tmlog.lck b/spring-boot/springboot-druid-mybatis-multi/tmlog.lck new file mode 100644 index 0000000..e69de29 diff --git a/spring-boot/springboot-druid-mybatis-multi/tmlog101.log b/spring-boot/springboot-druid-mybatis-multi/tmlog101.log new file mode 100644 index 0000000..f48e578 --- /dev/null +++ b/spring-boot/springboot-druid-mybatis-multi/tmlog101.log @@ -0,0 +1,9 @@ +{"id":"10.0.75.1.tm154840078074100001","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm1","state":"COMMITTING","expires":1548400791454,"resourceName":"one"},{"uri":"10.0.75.1.tm2","state":"COMMITTING","expires":1548400791454,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840078074100001","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm1","state":"TERMINATED","expires":1548400791469,"resourceName":"one"},{"uri":"10.0.75.1.tm2","state":"TERMINATED","expires":1548400791469,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840078467900002","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm3","state":"COMMITTING","expires":1548400794622,"resourceName":"one"},{"uri":"10.0.75.1.tm4","state":"COMMITTING","expires":1548400794622,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840078467900002","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm3","state":"TERMINATED","expires":1548400794642,"resourceName":"one"},{"uri":"10.0.75.1.tm4","state":"TERMINATED","expires":1548400794642,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840078978400003","wasCommitted":false,"participants":[{"uri":"10.0.75.1.tm5","state":"TERMINATED","expires":1548400799772,"resourceName":"one"},{"uri":"10.0.75.1.tm6","state":"TERMINATED","expires":1548400799772,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840079364000004","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm7","state":"COMMITTING","expires":1548400803592,"resourceName":"one"},{"uri":"10.0.75.1.tm8","state":"COMMITTING","expires":1548400803592,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840079364000004","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm7","state":"TERMINATED","expires":1548400803609,"resourceName":"one"},{"uri":"10.0.75.1.tm8","state":"TERMINATED","expires":1548400803609,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840079686400005","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm9","state":"COMMITTING","expires":1548400806814,"resourceName":"one"},{"uri":"10.0.75.1.tm10","state":"COMMITTING","expires":1548400806814,"resourceName":"two"}]} +{"id":"10.0.75.1.tm154840079686400005","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm9","state":"TERMINATED","expires":1548400806831,"resourceName":"one"},{"uri":"10.0.75.1.tm10","state":"TERMINATED","expires":1548400806831,"resourceName":"two"}]}