MyBaits と Spring を統合するには、xml 設定ファイルとアノテーションの 2 つの方法がありますが、実際の開発では、アノテーションの方法が比較的シンプルでよく使用されます。
春の統合 mybaits を紹介する前に、mybaits プログラムの独自開発がどのように行われるかを見てみましょう。
① mybaits-config 設定ファイル
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载同目录下的jdbc.properties配置文件-->
<properties resource="jdbc.properties"></properties>
<!--配置类型别名-->
<typeAliases>
<package name="org.mybatis.builder"/>
</typeAliases>
<!--数据源配置-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--SQL语句的映射-->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
</configuration>
② jdbc.properties 設定パラメータ
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=UTF-8
jdbc.uid=root
jdbc.password=123456
③ MyBatisプログラムコア(SqlSessionFactoryオブジェクト)
mport java.io.IOException;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws IOException {
// 1. 加载 mybaits-config.xml 配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 创建 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 获取 SqlSession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 执行 sqlSession 对象执行查询, 获取指定mapper对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
// 5. 执行查询
User user = userDao.findUserById(1);
// 6. 释放资源
sqlSession.close();
}
}
本題に入ります: Spring はアノテーションを通じて MyBatis を統合します (同時にデュアル データ ソースを使用します)。
① pom.xml の依存関係に関連する座標を導入する
<dependencies>
<!-- mysql连接驱动 -->
<dependencie>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependencie>
<!-- druid数据库连接池 -->
<dependencie>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependencie>
<!-- mybatis坐标 -->
<dependencie>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependencie>
<!-- mybatis整合spring坐标 -->
<dependencie>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependencie>
<!-- mybatis依赖jdbc坐标 -->
<dependencie>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependencie>
</<dependencies>>
② application.propertiesパラメータを設定する
server.port=8090
server.servlet-path=/
#主数据源
spring.datasource.primary.driverClassName = com.mysql.jdbc.Driver
spring.datasource.primary.url = jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.primary.username = root
spring.datasource.primary.password = root
#副数据源
spring.datasource.secondary.driverClassName = com.mysql.jdbc.Driver
spring.datasource.secondary.url = jdbc:mysql://localhost:3306/db2?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.secondary.username = root
spring.datasource.secondary.password = root
#通用属性
spring.datasource.maxActive=20
spring.datasource.minIdle=8
spring.datasource.initialSize=10
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery=SELECT 1
spring.datasource.timeBetweenEvictionRunsMillis = 3600000
③Springアノテーション統合MyBatisエントリ
@Configuration // 该注解类似于spring配置文件
@EnableTransactionManagement // 开启注解式事务驱动
public class MyBatisConfig{
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
/**
* 主数据源配置
* @ConfigurationProperties:支持属性文件和javaBean的映射
* @throws Exception
*/
@Bean
@ConfigurationProperties(prefix="spring.datasource.primary" )
public DataSource primaryDataSource() throws Exception {
System.out.println("创建主数据源");
return createDataSource();
}
/**
* 子数据源配置
* @return
* @throws Exception
*/
@Bean
@ConfigurationProperties(prefix="spring.datasource.secondary" )
public DataSource secondaryDataSource() throws Exception {
System.out.println("创建子数据源");
return createDataSource();
}
/**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@Autowire注解报错(@Autowired,默认是根据类型Type来自动注入的)
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean
@Primary // 这个@Primary与下面的DatabaseType.primary无关
public DynamicDataSource dataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
// DatabaseType 是自定义的枚举类型的类,下面步骤中会给出
targetDataSources.put(DatabaseType.primary, primaryDataSource);
targetDataSources.put(DatabaseType.secondary, secondaryDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
// 将所有的数据源设置进DynamicDataSource(该类继承了AbstractRoutingDataSource类)
dataSource.setTargetDataSources(targetDataSources);
// 设置默认的数据源为主数据源
dataSource.setDefaultTargetDataSource(primaryDataSource);
return dataSource;
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
//fb.setTypeAliasesPackage("com.huan.model");// 为基包下的类设置别名
fb.setMapperLocations(resolver.getResources("classpath:com/spring/*/mapper/impl/*.xml"));// 指定映射文件位置
return fb.getObject();
}
//加载属性并赋值
private DataSource createDataSource() {
org.apache.tomcat.jdbc.pool.DataSource dataSource = (org.apache.tomcat.jdbc.pool.DataSource)DataSourceBuilder.create().build();
dataSource.setMaxActive(maxActive);
dataSource.setMinIdle(minIdle);
dataSource.setInitialSize(initialSize);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setValidationQuery(validationQuery);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
return dataSource;
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) throws Exception {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
これまでのところ、Spring による MyBatis の統合作業は正常に完了しており、上記の DynamicDataSource クラスはメイン データ ソースとセカンダリ データ ソース間の切り替えを実現しています。
④ カスタム @DS アノテーション。サービス層でデータ ソースの切り替えをマークするために使用されます。
/**
* Class Description 注解类,用于标注在Service层切换数据源
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DS {
String value() default "primary";
}
⑤列挙型クラスを設定し、データソースを記録する
/**
* 列出所有的数据源key(常用数据库名称来命名)
* 注意:这里数据源与数据库是一对一的,DatabaseType中的变量名称就是数据库的名称
* "primary" 代指主数据源
* "second" 代指副数据源
*/
public enum DatabaseType {
primary,
secondary
}
⑥ スレッドセーフな DatabaseType コンテナを保存する
/**
* 保存当前线程所对应的 DatabaseType
*/
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
public static void setDatabaseType(DatabaseType type) {
contextHolder.set(type);
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
⑦ インターセプターを設定する: サービス パッケージ内のすべてのメソッドをインターセプトし、アノテーションに従ってデータ ソースを切り替えます。
/**
* 拦截 service 包下的所有方法, 根据注解进行数据源的切换
* DatabaseContextHolder.setDatabaseType() 方法: 将数据源的key设置到当前线程中
*/
@Aspect
@Component
public class DynamicDataSourceAspect {
/**
* 使用空方法定义切点表达式
*/
@Pointcut("execution(* com.spring.patent.service.**.*(..))")
public void declareJointPointExpression() {
}
@Before("declareJointPointExpression()")
public void setDataSourceKey(JoinPoint point) throws NoSuchMethodException, SecurityException{
/*logger.info("拦截到了" + point.getSignature().getName() +"方法...");*/
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method targetMethod = methodSignature.getMethod();
DS ds = targetMethod.getAnnotation(DS.class);
if(ds != null){
// 1. service方法上有@DS注解, 注解的值是谁就使用谁的数据源(setDatabaseType将数据源设置到当前线程中)
String dsname = ds.value();
if("primary".equals(dsname)){
DatabaseContextHolder.setDatabaseType(DatabaseType.primary);
}else if("secondary".equals(dsname)){
DatabaseContextHolder.setDatabaseType(DatabaseType.secondary);
}
}else{
// 2. service方法上没有@DS注解时, 默认使用主数据源(setDatabaseType将数据源设置到当前线程中)
DatabaseContextHolder.setDatabaseType(DatabaseType.primary);
}
}
@After("declareJointPointExpression()")
public void restoreDataSource(JoinPoint point) throws NoSuchMethodException, SecurityException{
//方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
DatabaseContextHolder.clearDataSourceType();
}
}
⑧ 現在のスレッドが保持しているデータソースのキーを取得し、DynamicDataSource に設定します。
/**
* 动态数据源(需要继承AbstractRoutingDataSource)
* 该类通过DatabaseContextHolder对象在一个线程内获取对应的数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {// 决定当前数据源的key
// 获取当前线程对应的数据源的key, 并设置进DynamicDataSource
return DatabaseContextHolder.getDatabaseType();
}
}