转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9190226.html
May 12, 2018 星期六,那是个晴天,天湛蓝湛蓝的非常干净,仿佛飘过一粒尘埃也能看得清清楚楚,然后就发生了些事情。。。很伤心很难过,至今也没能抹去,欸,我是怎样一步步把自己变成这个样子呢。
难过的事情总会在萦绕很久,罢了,这里还是不回忆了,就这样吧。
首先我说说这次配置多数据源的原因吧:原因大致有二:
一是我们的线上的有两大业务系统云像系统和线上交易系统,这两个系统的分别使用各自的mysql实例,交合业务的情况下目前通过定时脚本做数据更新和同步,遂在开发新的业务模块的时候就想到了将springboot配置两个数据源(mysql和mysql)
二是最近在改造数据库日志表的时候发现mysql的优化捉襟见肘,遂就想到了换数据库,换个我个人研究了许久的PostgreSQL,老大一开始不怎么乐意这么干,但是看我又研究了这么久,性能也确实较mysql高许多,再加上公司技术团队并不是很大的情况下(主要是业务量上去了数据库性能跟不上,也没有独立的DBA来维护和调优Mysql),就给了我一周的时间研究数据库(下次具体聊),故就涉及到两个数据源(mysql和PostgreSQL)的问题。
嗯,对于以上两个问题,我尝试了差异化的解决方式,对于mysl和mysql数据源我选择的是 阿里Druid+TK.Mybatis的解决方式,对于mysql和PG数据源我选择的是Hikari+TK.Mybatis的解决方式.这两种方式在实际配置中是有些许差异的,这里我略去不讲,只讲第二种,首先罗列下这其中碰到的问题:
A>DataSource和SessionFactory引用指定注入问题。
B>Hikari数据源配置参数名称差异问题。
C>Springboot init时配置类先后顺序的问题
D>多数据源下Mybatis Mapper类重复问题
我先讲讲我大致的配置过程吧,首先新建立两个配置类,以隔离开(刚在一个包 中不隔离开也可以):
然后在两个包中分别新建两个配置类,一个是MyBatis配置类和数据源、session工厂配置类,我这里是这样子:
这时候,我先展示下数据源配置类:
1 package **.task.config.mysql; 2 3 4 import com.github.pagehelper.PageHelper; 5 import com.zaxxer.hikari.HikariConfig; 6 import com.zaxxer.hikari.HikariDataSource; 7 import org.apache.commons.lang3.StringUtils; 8 import org.apache.ibatis.plugin.Interceptor; 9 import org.apache.ibatis.session.SqlSessionFactory; 10 import org.mybatis.spring.SqlSessionFactoryBean; 11 import org.mybatis.spring.SqlSessionTemplate; 12 import org.springframework.beans.factory.annotation.Qualifier; 13 import org.springframework.beans.factory.annotation.Value; 14 import org.springframework.boot.context.properties.ConfigurationProperties; 15 import org.springframework.context.annotation.Bean; 16 import org.springframework.context.annotation.Configuration; 17 import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 18 import org.springframework.core.io.support.ResourcePatternResolver; 19 import org.springframework.jdbc.datasource.DataSourceTransactionManager; 20 import org.springframework.transaction.annotation.EnableTransactionManagement; 21 22 import javax.sql.DataSource; 23 import java.sql.SQLException; 24 import java.util.Properties; 25 26 /** 27 * mybatis 配置数据源类 28 * 29 * @author 30 * @date 2016年12月15日 31 * @since 1.7 32 */ 33 @Configuration("mysqlCfg") 34 @EnableTransactionManagement 35 @ConfigurationProperties(prefix = "spring.db_mysql") //引用配置文件参数前缀 36 public class MybatisConfiguration extends HikariConfig { 37 @Value("${mybatis.mysql.xmlLocation}") 38 private String xmlLocation; 39 40 private String typeAliasesPackage; 41 //配置数据源 42 @Bean("mysqlDataSource") 43 public DataSource dataSource(){ 44 /* 45 HikariDataSource ds=HikariDataSource(); 46 ds.setJdbcUrl(); 47 ds.setConnectionTimeout(); 48 */ 49 return new HikariDataSource(this); 50 } 51 //配置Session工厂 52 @Bean(name = "mysqlSqlSessionFactory") 53 public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("mysqlDataSource")DataSource dataSource) { 54 SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); 55 bean.setDataSource(dataSource); 56 if(StringUtils.isNotBlank(typeAliasesPackage)){ 57 bean.setTypeAliasesPackage(typeAliasesPackage); 58 } 59 //分页插件 60 PageHelper pageHelper = new PageHelper(); 61 Properties properties = new Properties(); 62 properties.setProperty("reasonable", "true"); 63 properties.setProperty("supportMethodsArguments", "true"); 64 properties.setProperty("returnPageInfo", "check"); 65 properties.setProperty("params", "count=countSql"); 66 pageHelper.setProperties(properties); 67 //添加XML目录 68 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 69 Interceptor[] plugins = new Interceptor[]{pageHelper}; 70 bean.setPlugins(plugins); 71 try { 72 73 bean.setMapperLocations(resolver.getResources(xmlLocation)); 74 return bean.getObject(); 75 } catch (Exception e) { 76 e.printStackTrace(); 77 throw new RuntimeException(e); 78 } 79 } 80 //mybatis会用到的SqlSession模板 81 @Bean 82 public SqlSessionTemplate sqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { 83 return new SqlSessionTemplate(sqlSessionFactory); 84 } 85 //数据源事物配置 86 @Bean 87 public DataSourceTransactionManager transactionManager(@Qualifier("mysqlDataSource") DataSource dataSource) { 88 return new DataSourceTransactionManager(dataSource); 89 } 90 91 }
在这里需要说明的是我通过继承HikariConfig来配置数据源以及Session工厂,配置DataSource和Session工厂的时候需要使用指定注解名称,在这里是“mysqlDataSource“和”mysqlSqlSessionFactory“,如果项目只有一个数据源的话大可不必写这个的,另外需要特别注意的是在配置session工厂一定要在形式参数前使用@Qualifier注解引用指定的数据源,同时SqlSession模板和事物也需要通过@Qualifier注解指定session工厂和数据源,这里这样做的原因是为了解决多数据源配置引用不明的问题。
OK,数据源配置完成了,现在将配置第二个配置类Mybatis配置类,以下是具体配置内容:
1 package **.task.config.mysql; 2 3 import org.springframework.boot.autoconfigure.AutoConfigureAfter; 4 import org.springframework.boot.bind.RelaxedPropertyResolver; 5 import org.springframework.context.EnvironmentAware; 6 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Configuration; 8 import org.springframework.core.env.Environment; 9 import tk.mybatis.spring.mapper.MapperScannerConfigurer; 10 11 /** 12 * mybatis mapper 扫描配置类 13 * 14 * @author 15 * @date 2018年05月15日 16 */ 17 @Configuration("mysqlMapper") 18 @AutoConfigureAfter(MybatisConfiguration.class)//init时指定先后顺序,这里是数据源在mybatis之前配置 19 public class MapperConfiguration implements EnvironmentAware { 20 21 private RelaxedPropertyResolver propertyResolver; 22 23 private String basePackage; 24 //这里为mybatis配置指定session工厂和Dao类基础包路径 25 @Bean("mysqlMapperScannerConfigurer") 26 public MapperScannerConfigurer mapperScannerConfigurer(Environment environment){ 27 28 MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); 29 mapperScannerConfigurer.setSqlSessionFactoryBeanName("mysqlSqlSessionFactory"); 30 mapperScannerConfigurer.setBasePackage(basePackage); 31 return mapperScannerConfigurer; 32 } 33 34 //设置环境变量(引用配置文件中的) 35 @Override 36 public void setEnvironment(Environment environment) { 37 this.propertyResolver = new RelaxedPropertyResolver(environment, null); 38 this.basePackage = propertyResolver.getProperty("mybatis.mysql.basepackage"); 39 } 40 }
配置Mybaits的时候需要将数据源配置置于之后配置,这里通过注解@AutoConfigureAfter来指定数据源配置类,在配置Mybatis引用的Session工厂时也要指定为数据源配置类中的sqlSession工厂,同时也需要指定生成的Mapper的包名,这个包的路径这里我写在application.yml的配置文件中。
配置类已经写完,现在最后一步了,在配置文件中指定配置类所引用的配置参数,大致是这样子:
spring: application: name: **-task jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 default-property-inclusion: non_null db_mysql: name: DEV_MYSQL #LOCAL jdbc-url: jdbc:mysql://${MYSQL_HOST:192.168.10.141}:${MYSQL_PORT:3306}/p2p?useUnicode=true&characterEncoding=UTF8 driver-class-name: com.mysql.jdbc.Driver username: p2p password: p2p # Hikari connection pool type: com.zaxxer.hikari.HikariDataSource minimum-idle: 5 maximum-pool-size: 15 auto-commit: true idle-timeout: 30000 pool-name: DatebookHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 validation-timeout: 10000 db_pg: name: DEV_PG # JDBC config jdbc-url: jdbc:postgresql://192.168.10.141:5432/log driver-class-name: org.postgresql.Driver username: log password: log # Hikari connection pool type: com.zaxxer.hikari.HikariDataSource minimum-idle: 5 maximum-pool-size: 15 auto-commit: true idle-timeout: 30000 pool-name: DatebookHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 validation-timeout: 10000
以上配置文件中主要展示了数据源的一些配置(注意db_mysql和db_pg这两项),这里需要特别注意的是在Hikari数据源的配置参数中没有url和driverClass,只有jdbc-url 和 driver-class-name这两个,其它的配置配置参数名称与c3p0和Druid的无异,具体的连接池大小需要根据实际的项目和数据库服务器的硬件参数来配置,这里我只给出常见配置。
哦,对了,还需要在配置文件中追加Mybatis的配置参数,具体是这样:
1 mybatis: 2 mysql: 3 basepackage: **.task.mapper.mysql 4 xmlLocation: classpath:mapper/mysql/*.xml 5 pg: 6 basepackage: **.task.mapper.pg 7 xmlLocation: classpath:mapper/pg/*.xml
Mybatis的配置完成了,对于PostgreSQL的配置只需要注意响应的配置别名即可(比如数据源、session工厂、SqlSession工厂等等)
本节所讲的配置貌似已经完成,但是这里我顺带讲一下我在性能测试的时候所遇见的两个TK.mybatis这个插件的问题(原生mybatis也可能存在):
A>对于两个库中存在同名的Mapper名字,在@Autowired使用时会产生冲突
B>持久化需要返回主键时对于mysql和PG两中数据库的处理方式存在差异
对于以上第一个问题(多数据源Mapper冲突),我给出的解决方式是在生成的Mapper类中指定冲突的那个Mapper的Service别名,这样:
1 package **.task.mapper.pg; 2 3 import com.github.carvechris.security.task.entity.pg.TestEmp; 4 import org.springframework.stereotype.Service; 5 import tk.mybatis.mapper.common.Mapper; 6 7 @Service("pgTestEmpMapper")//这里指定别名 8 public interface TestEmpMapper extends Mapper<TestEmp> { 9 }
这个Dao在使用的时候需要使用@Resource注解来指定的Dao类:
1 @Autowired 2 @Resource(name = "pgTestEmpMapper") 3 private **.task.mapper.pg.TestEmpMapper pgEmpMapper;
对于以上第二个问题(持久化返回主键问题),mysql和pg的处理方式不同,具体为:
对于mysql:需要在实体类中指定主键的生成方式,即可在调用insert方法时返回生成的主键:
/** * 表ID */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY)//设置为主键自增以回写主键 private Integer id;
以上的主键策略可以是主键表,也可以是UUID的方式,根据项目实际需求而定。
对于PG:首先需要在实体类的主键中这样声明:
/** * 表ID */ @Id @Column(insertable=false)//指定主键为数据库生成(同时需要在DB中将ID声明为serial类型) private Long id;
再在Dao(生成的Mapper类中)声明一个独立的查入方法:
1 package **.task.mapper.pg; 2 3 import **.task.entity.pg.ZwPlBalancequery; 4 import org.apache.ibatis.annotations.InsertProvider; 5 import org.a2018-06-16pache.ibatis.annotations.Options; 6 import org.apache.ibatis.annotations.Select; 7 import tk.mybatis.mapper.common.Mapper; 8 import tk.mybatis.mapper.provider.base.BaseInsertProvider; 9 10 public interface ZwPlBalancequeryMapper extends Mapper<ZwPlBalancequery> { 11 //需要需要独立声明插入方法以返回插入记录的ID 12 @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") 13 @InsertProvider(type = BaseInsertProvider.class, method = "dynamicSQL") 14 int insertWBack(ZwPlBalancequery record); 15 }
以上插入方法(insertWBack)中的注解是将id的生成方式改为数据库生成,至此,完美解决持久化返回记录ID问题。