用了一段時間SpringBoot,之前配置MYBATIS ,在打包WAR 放到tomcat下正常,但是WAR已經過時了,現在流行直接打包JAR 丟到DOCKER 里,無奈JAR 啟動的時候MAPPER 掃描有問題,只能說之前整合MYBATIS 的方式不對.
這次的整合應該是不存在加載順序引起的問題,使用了一段時間,妥妥的,記錄下來
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency>
application.yml,支持多個數據源,有多少就寫多少個,但這是靜態的多數據源方式,也可以動態往Spring的數據源路由里面丟
spring: datasource: # readSize: 1 # name: writeDataSource type: com.alibaba.druid.pool.DruidDataSource write: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&useSSL=true username: root password: 123 initialSize: 10 maxActive: 100 maxWait: 60000 minIdle: 5 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 'x' testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20
DataSourceConfig.java
/** * Created by ZhenWeiLai on 2016/11/20. */ @Configuration @EnableTransactionManagement @MapperScan("com.lzw.**.dao")//mybatis掃描 public class DataSourceConfig implements TransactionManagementConfigurer { @Value("${spring.datasource.type}") private Class<? extends DataSource> dataSourceType; @Primary @Bean(name = "writeDataSource") @ConfigurationProperties(prefix = "spring.datasource.write") public DataSource writeDataSource() { return DataSourceBuilder.create().type(dataSourceType).build(); } /** * 有多少個從庫就要配置多少個 * @return */ // @Bean(name = "readDataSource") // @ConfigurationProperties(prefix = "spring.datasource.read") // public DataSource readDataSourceOne() { // return DataSourceBuilder.create().type(dataSourceType).build(); // } /** * AbstractRoutingDataSource 這破玩意 繼承了AbstractDataSource ,AbstractDataSource又實現了DataSource * 所以可以直接丟去構建 SqlSessionFactory * @return */ @Bean("routingDataSource") // @Bean(name="routingDataSource",initMethod = "init",destroyMethod = "close") public MyRoutingDataSource dataSourceProxy(){ // int size = Integer.parseInt(dataSourceSize); MyRoutingDataSource proxy = new MyRoutingDataSource(); Map<Object,Object> dataSourceMap = new HashMap<>(); DataSource writeSource = writeDataSource(); // DataSource readSource = getReadDataSource(); dataSourceMap.put(TargetDataSource.WRITE.getCode(),writeSource); // dataSourceMap.put(TargetDataSource.READ.getCode(),readSource); proxy.setDefaultTargetDataSource(dataSourceMap.get(TargetDataSource.WRITE.getCode())); proxy.setTargetDataSources(dataSourceMap); return proxy; } @Bean public SqlSessionFactory sqlSessionFactory() { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSourceProxy()); bean.setTypeAliasesPackage("com.lzw"); bean.setVfs(SpringBootVFS.class); //添加XML目錄 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { bean.setMapperLocations(resolver.getResources("classpath:**/mapper/**/*.xml")); return bean.getObject(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 事務配置,考慮多數據源情況下 * @return */ @Bean public PlatformTransactionManager txManager() { return new DataSourceTransactionManager(dataSourceProxy()); } @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return txManager(); } }
MyRoutingDataSource.java
/** * Created by ZhenWeiLai on 2016/11/22. */ public class MyRoutingDataSource extends AbstractRoutingDataSource { /** * 這里可以做簡單負載均衡,暫時用不上 */ // private int dataSourceOrder; // private AtomicInteger count = new AtomicInteger(0); public MyRoutingDataSource() { } // public MyRoutingDataSource(int _dataSourceOrder){ // this.dataSourceOrder = _dataSourceOrder; // } /** * 這個方法會根據返回的key去配置文件查找數據源 * * @return */ @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getTargetDataSource(); } }
TargetDataSource.java
/** * Created by ZhenWeiLai on 2016/11/22. * 配置讀寫分離或者多數據源 */ public enum TargetDataSource { WRITE("write","主庫"), READ("read","從庫"), CREATE("create","建庫"); final private String code; final private String name; TargetDataSource(String _code,String _name) { this.code = _code; this.name = _name; } public String getCode() { return code; } public String getName(){ return name; } public static String getNameByCode(String _code){ for(TargetDataSource item : TargetDataSource.values()){ if(item.getCode().equals(_code)){ return item.getName(); } } return ""; } }
喜歡的話,可以使用AOP 注解切換數據源,但是要注意濫用AOP,加上全局統一AOP異常攔截,以及Spring AOP事務,順序問題,處理不好可能會引起事務失效,或者異常攔截失效,也可能是數據源切換失效.
AOP事務如果先開啟了,那么AOP切換數據源將無效
/** * Created by ZhenWeiLai on 2016/11/22. * 切換數據源的注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DataSourceSwitch { String targetDataSource() default ""; }
DataSourceContextHolder.java
/** * Created by ZhenWeiLai on 2016/11/22. * 多數據源/讀寫分離 */ public class DataSourceContextHolder { private static final ThreadLocal<String> dataSourceLocal = new ThreadLocal<>(); public static ThreadLocal<String> getDataSourceLocal() { return dataSourceLocal; } /** * 從庫 可以有多個 */ public static void read() { dataSourceLocal.set(TargetDataSource.READ.getCode()); } /** * 主庫 只有一個 */ public static void write() { dataSourceLocal.set(TargetDataSource.WRITE.getCode()); } public static String getTargetDataSource() { return dataSourceLocal.get(); } }