SpringBoot+Mybatis-Plus实现mysql读写分离方案的示例代码
1.引入mybatis-plus相关包,pom.xml文件
2.配置文件application.property增加多库配置
mysql数据源配置
spring.datasource.primary.jdbc-url=jdbc:mysql://xx.xx.xx.xx:3306/portal?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=utf8&serverTimezone=GMT%2B8 spring.datasource.primary.username=root spring.datasource.primary.password=root spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver #mysqlslave数据源配置 spring.datasource.slave.jdbc-url=jdbc:mysql://xx.xx.xx.xx:3306/portal?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=utf8&serverTimezone=GMT%2B8 spring.datasource.slave.username=root spring.datasource.slave.password=root spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
3.配置数据源及注解
数据源配置MultiDataSourceConfig.Java
/** *配置多数据源 */ @Profile("dev")//开发模式配置文件 @Configuration @MapperScan(basePackages="com.csc.portal.mapper")//扫描包 publicclassMultiDataSourceConfig{ /** *主数据源 *@return */ @Bean @ConfigurationProperties(prefix="spring.datasource.primary") publicDataSourcemasterDataSource(){ returnDataSourceBuilder.create().build(); } /** *从数据源 *@return */ @Bean @ConfigurationProperties(prefix="spring.datasource.slave") publicDataSourceslaveDataSource(){ returnDataSourceBuilder.create().build(); } /** *路由数据源,前面两个数据源是为了创建此数据源 *@parammasterDataSource主数据源 *@paramslaveDataSource从数据源 *@return */ @Bean publicDataSourcemyRoutingDataSource(@Qualifier("masterDataSource")DataSourcemasterDataSource, @Qualifier("slaveDataSource")DataSourceslaveDataSource){ Map
数据库枚举类
publicenumDBTypeEnum{ MASTER,SLAVE; }
注解
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public@interfaceMaster{ }
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public@interfaceSlave{ }
4.Mybatis-plus配置
@EnableTransactionManagement @Configuration @MapperScan(basePackages="com.csc.portal.mapper") publicclassMybatisPlusConfig{ /** *分页插件 */ @Bean publicPaginationInterceptorpaginationInterceptor(){ returnnewPaginationInterceptor(); } @Resource(name="myRoutingDataSource") privateDataSourcemyRoutingDataSource; /** *使用MyBatisPlus的sqlSessionFactory代替, *此处注意mybatis与mybatisPlus的配置不同,不然扫描不到对数据操作的方法。会报未绑定错误 *@returnsqlSessionFactory *@throwsException */ @Bean publicSqlSessionFactorysqlSessionFactory()throwsException{ MybatisSqlSessionFactoryBeansqlSessionFactoryBean=newMybatisSqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(myRoutingDataSource); sqlSessionFactoryBean.setMapperLocations(newPathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); MybatisConfigurationmybatisConfiguration=newMybatisConfiguration(); sqlSessionFactoryBean.setConfiguration(mybatisConfiguration); returnsqlSessionFactoryBean.getObject(); } /** *此处为使用mybatis时的sqlsessionFactory配置 *@return *@throwsException */ /* @Bean publicSqlSessionFactorysqlSessionFactory()throwsException{ SqlSessionFactoryBeansqlSessionFactoryBean=newSqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(myRoutingDataSource); sqlSessionFactoryBean.setMapperLocations(newPathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); returnsqlSessionFactoryBean.getObject(); } */ /** *事务配置 *@return事务管理器 */ @Bean publicDataSourceTransactionManagertransactionManager(){ DataSourceTransactionManagertx=newDataSourceTransactionManager(); tx.setDataSource(myRoutingDataSource); returntx; }
5.增加数据源管理类
DBContextHolder.java
publicclassDBContextHolder{ /** *外部一个请求将会产生一个线程与之对应,每个线程的变量可用ThreadLocal进行存储 */ privatestaticfinalThreadLocalcontextHolder=newThreadLocal<>(); publicstaticvoidset(DBTypeEnumdbType){ contextHolder.set(dbType); } publicstaticDBTypeEnumget(){ returncontextHolder.get(); } publicstaticvoidmaster(){ set(DBTypeEnum.MASTER); System.out.println("切换到master"); } publicstaticvoidslave(){ set(DBTypeEnum.SLAVE); System.out.println("切换到slave"); } }
指定选择数据源
MyRoutingDataSource.java方法determineCurrentLookupKey决定最终使用哪个数据源进行操作,若为空则使用默认数据源。
publicclassMyRoutingDataSourceextendsAbstractRoutingDataSource{ @Nullable @Override protectedObjectdetermineCurrentLookupKey(){ System.out.println("线程名:"+Thread.currentThread().getName()+":"+DBContextHolder.get()); returnDBContextHolder.get(); /*if(DBContextHolder.get()!=null){ System.out.println("线程名:"+Thread.currentThread().getName()+":"+DBContextHolder.get()); returnDBContextHolder.get(); }else{ System.out.println("未匹配到指定数据库,默认切换到Master"); returnDBTypeEnum.MASTER; }*/ //returnDBContextHolder.get(); } }
6.增加aop切面
@Aspect @Component @Order(0)//配置注解优先级,优于事物注解@Transactional先进行数据源切换, //不然在事物中进行数据源切换无效 publicclassDataSourceAop{ @Pointcut(/*"!@annotation(com.csc.portal.annotation.Master)"+ "&&(execution(*com.csc.portal.service..*.select*(..))"+ "||execution(*com.csc.portal.service..*.get*(..))"+*/ "@annotation(com.csc.portal.annotation.Slave)") publicvoidreadPointcut(){ } @Pointcut("@annotation(com.csc.portal.annotation.Master)"//+ /*"||execution(*com.csc.portal.service..*.insert*(..))"+ "||execution(*com.csc.portal.service..*.add*(..))"+ "||execution(*com.csc.portal.service..*.update*(..))"+ "||execution(*com.csc.portal.service..*.edit*(..))"+ "||execution(*com.csc.portal.service..*.delete*(..))"+ "||execution(*com.csc.portal.service..*.remove*(..))"*/) publicvoidwritePointcut(){ } @Before("readPointcut()") publicvoidread(){ //获取拦截类 DBContextHolder.slave(); System.out.println(Thread.currentThread().getName()+DBContextHolder.get()); } @Before("writePointcut()") publicvoidwrite(){ //获取拦截类 /*StringclassName=pjp.getTarget().getClass().getName(); System.out.println("当前线程"+Thread.currentThread().getName()+"拦截类为:"+className); //获取拦截的方法名 MethodSignaturemsig=(MethodSignature)pjp.getSignature(); MethodcurrentMethod=null; try{ currentMethod=pjp.getTarget().getClass().getMethod(msig.getName(),msig.getParameterTypes()); }catch(NoSuchMethodExceptione){ e.printStackTrace(); } StringmethodName=currentMethod.getName(); System.out.println("拦截方法名为:"+methodName);*/ DBContextHolder.master(); System.out.println(Thread.currentThread().getName()+DBContextHolder.get()); } }
6.实际应用
- 在service层方法前增加注解@Master表示使用主库,进行增删改的操作使用主库。
- 在service层方法前增加注解@Slave表示使用从库,进行查的操作使用从库,默认使用从库,可不配置。
- @Transactional注解加到service层,增加了@Transactional注解后,启用事务后,一个事务内部的connection是复用的,所以就算AOP切了数据源字符串,但是数据源并不会被真正修改。所以@Transactional注解不要写在controller层,不然在service层也切换不了数据源。
- @Transactional与@Master可同时使用,已经配置@Master注解的优先级较高,先切换数据源后执行事务。
到此这篇关于SpringBoot+Mybatis-Plus实现mysql读写分离方案的示例代码的文章就介绍到这了,更多相关SpringBootMybatis-Plusmysql读写分离内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。