最近项目需要指出多数据源,同时支持事务回滚,这里记录一下

1、多数据源方式介绍

主要方式有以下两种:

通过配置多个SqlSessionFactory 来实现多数据源,这么做的话,未免过于笨重,而且无法实现动态添加数据源这个需求

通过 spring AbstractRoutingDataSource 为我们抽象了一个 DynamicDataSource 解决这一问题

2、多数据源实现

2.1、分包方式实现:

2.1.1、在application.properties中配置两个数据库:

## test1 databasespring.datasource.test1.url=jdbc:mysql://localhost:3307/multipledatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=falsespring.datasource.test1.username=rootspring.datasource.test1.password=rootspring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver## test2 databasespring.datasource.test2.url=jdbc:mysql://localhost:3307/multipledatasource2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=falsespring.datasource.test2.username=rootspring.datasource.test2.password=rootspring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver

2.1.2、建立连个数据源的配置文件:

springbooot中的参数可以参考上一篇博客(不定期更新中):https://blog.csdn.net/tuesdayma/article/details/81029539

第一个配置文件:

//表示这个类为一个配置类@Configuration// 配置mybatis的接口类放的地方@MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test01", sqlSessionFactoryRef = "test1SqlSessionFactory")public class DataSourceConfig1 {// 将这个对象放入Spring容器中@Bean(name = "test1DataSource")// 表示这个数据源是默认数据源@Primary// 读取application.properties中的配置参数映射成为一个对象// prefix表示参数的前缀@ConfigurationProperties(prefix = "spring.datasource.test1")public DataSource getDateSource1() {return DataSourceBuilder.create().build();}@Bean(name = "test1SqlSessionFactory")// 表示这个数据源是默认数据源@Primary// @Qualifier表示查找Spring容器中名字为test1DataSource的对象public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(// 设置mybatis的xml所在位置new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test01/*.xml"));return bean.getObject();}@Bean("test1SqlSessionTemplate")// 表示这个数据源是默认数据源@Primarypublic SqlSessionTemplate test1sqlsessiontemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);}}

第二个配置文件:

@Configuration@MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test02", sqlSessionFactoryRef = "test2SqlSessionFactory")public class DataSourceConfig2 {@Bean(name = "test2DataSource")@ConfigurationProperties(prefix = "spring.datasource.test2")public DataSource getDateSource2() {return DataSourceBuilder.create().build();}@Bean(name = "test2SqlSessionFactory")public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test02/*.xml"));return bean.getObject();}@Bean("test2SqlSessionTemplate")public SqlSessionTemplate test2sqlsessiontemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);}}

注意:

1、@Primary这个注解必须要加,因为不加的话spring将分不清楚那个为主数据源(默认数据源)

2、mapper的接口、xml形式以及dao层都需要两个分开,目录如图:

3、bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“XXXX”));mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致的,具体看情况吧,注意一下就行,问题不大的)

4、在service层中根据不同的业务注入不同的dao层。

5、如果是主从复制- -读写分离:比如test01中负责增删改,test02中负责查询。但是需要注意的是负责增删改的数据库必须是主库(master)

6、如果是分布式结构的话,不同模块操作各自的数据库就好,test01包下全是test01业务,test02全是test02业务,但是如果test01中掺杂着test02的编辑操作,这时候将会产生事务问题:即test01中的事务是没法控制test02的事务的,这个问题在之后的博客中会解决。

2.2 AOP实现:

简介: 用这种方式实现多数据源的前提必须要清楚两个知识点:AOP原理和AbstractRoutingDataSource抽象类。

1、AOP:这个东西。。。不切当的说就是相当于拦截器,只要满足要求的都会被拦截过来,然后进行一些列的操作。具体需要自己去体会。。。

2、AbstractRoutingDataSource:这个类是实现多数据源的关键,他的作用就是动态切换数据源,实质:有多少个数据源就存多少个数据源在targetDataSources(是AbstractRoutingDataSource的一个map类型的属性,其中value为每个数据源,key表示每个数据源的名字)这个属性中,然后根据determineCurrentLookupKey()这个方法获取当前数据源在map中的key值,然后determineTargetDataSource()方法中动态获取当前数据源,如果当前数据源不存并且默认数据源也不存在就抛出异常。

1、创建枚举类DataSourceKey列出你所有的数据源名称,当然了,类名你可以按照自己的取名习惯,下面所有的类也是如此。

public enum DataSourceKey {DB_MASTER,DB_SLAVE1,DB_SLAVE2,DB_OTHER}

2、创建DynamicDataSourceContextHolder类,这个类是为了解决多线程访问全局变量的问题。

import org.apache.commons.lang3.RandomUtils;import org.apache.log4j.Logger;/*** @author RocLiu [apedad@qq.com]* @version 1.0*/public class DynamicDataSourceContextHolder {private static final Logger LOG = Logger.getLogger(DynamicDataSourceContextHolder.class);private static final ThreadLocal<DataSourceKey> currentDatesource = new ThreadLocal<>();/*** 清除当前数据源*/public static void clear() {currentDatesource.remove();}/*** 获取当前使用的数据源** @return 当前使用数据源的ID*/public static DataSourceKey get() {return currentDatesource.get();}/*** 设置当前使用的数据源** @param value 需要设置的数据源ID*/public static void set(DataSourceKey value) {currentDatesource.set(value);}/*** 设置从从库读取数据* 采用简单生成随机数的方式切换不同的从库*/public static void setSlave() {if (RandomUtils.nextInt(0, 2) > 0) {DynamicDataSourceContextHolder.set(DataSourceKey.DB_SLAVE2);} else {DynamicDataSourceContextHolder.set(DataSourceKey.DB_SLAVE1);}}}

3、创建类DynamicRoutingDataSource继承AbstractRoutingDataSource类并且实现determineCurrentLookupKey()方法,设置数据源。

import org.apache.log4j.Logger;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicRoutingDataSource extends AbstractRoutingDataSource {private static final Logger LOG = Logger.getLogger(DynamicRoutingDataSource.class);@Overrideprotected Object determineCurrentLookupKey() {LOG.info("当前数据源:{}"+ DynamicDataSourceContextHolder.get());return DynamicDataSourceContextHolder.get();}}

4、配置数据源,这一步比较重要,创建配置类DynamicDataSourceConfiguration

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;import com.apedad.example.commons.DataSourceKey;import com.apedad.example.commons.DynamicRoutingDataSource;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;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.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;import java.util.HashMap;import java.util.Map;@MapperScan(basePackages = "com.apedad.example.dao")@Configurationpublic class DynamicDataSourceConfiguration {@Bean@ConfigurationProperties(prefix = "multiple.datasource.master")//此处的"multiple.datasource.master"需要你在application.properties中配置,详细信息看下面贴出的application.properties文件。public DataSource dbMaster() {return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "multiple.datasource.slave1")public DataSource dbSlave1() {return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "multiple.datasource.slave2")public DataSource dbSlave2() {return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "multiple.datasource.other")public DataSource dbOther() {return DruidDataSourceBuilder.create().build();}/*** 核心动态数据源** @return 数据源实例*/@Beanpublic DataSource dynamicDataSource() {DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();dataSource.setDefaultTargetDataSource(dbMaster());Map<Object, Object> dataSourceMap = new HashMap<>(4);dataSourceMap.put(DataSourceKey.DB_MASTER, dbMaster());dataSourceMap.put(DataSourceKey.DB_SLAVE1, dbSlave1());dataSourceMap.put(DataSourceKey.DB_SLAVE2, dbSlave2());dataSourceMap.put(DataSourceKey.DB_OTHER, dbOther());dataSource.setTargetDataSources(dataSourceMap);return dataSource;}@Beanpublic SqlSessionFactory sqlSessionFactory() throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dynamicDataSource());//此处设置为了解决找不到mapper文件的问题sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));return sqlSessionFactoryBean.getObject();}@Beanpublic SqlSessionTemplate sqlSessionTemplate() throws Exception {return new SqlSessionTemplate(sqlSessionFactory());}/*** 事务管理** @return 事务管理实例*/@Beanpublic PlatformTransactionManager platformTransactionManager() {return new DataSourceTransactionManager(dynamicDataSource());}}

5、为了不影响业务代码而实现数据源切换,我决定使用AOP切换数据源,为了准确的知道哪个地方需要切换哪个数据源,我这里使用自定义注解的方式,如果你又更好的方式也推荐你使用自己的方式。创建自定义注解类:TargetDataSource:

import com.apedad.example.commons.DataSourceKey;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface TargetDataSource {DataSourceKey dataSourceKey() default DataSourceKey.DB_MASTER;}

6、编写数据源切换切面类:DynamicDataSourceAspect

import com.apedad.example.annotation.TargetDataSource;import org.apache.log4j.Logger;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.aspectj.lang.reflect.MethodSignature;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect@Order(-1)@Componentpublic class DynamicDataSourceAspect {private static final Logger LOG = Logger.getLogger(DynamicDataSourceAspect.class);@Pointcut("execution(* com.apedad.example.service.*.list*(..))")public void pointCut() {}/*** 执行方法前更换数据源** @param joinPoint        切点* @param targetDataSource 动态数据源*/@Before("@annotation(targetDataSource)")public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();if (dataSourceKey == DataSourceKey.DB_OTHER) {LOG.info(String.format("设置数据源为  %s", DataSourceKey.DB_OTHER));DynamicDataSourceContextHolder.set(DataSourceKey.DB_OTHER);} else {LOG.info(String.format("使用默认数据源  %s", DataSourceKey.DB_MASTER));DynamicDataSourceContextHolder.set(DataSourceKey.DB_MASTER);}}/*** 执行方法后清除数据源设置** @param joinPoint        切点* @param targetDataSource 动态数据源*/@After("@annotation(targetDataSource)")public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {LOG.info(String.format("当前数据源  %s  执行清理方法", targetDataSource.dataSourceKey()));DynamicDataSourceContextHolder.clear();}@Before(value = "pointCut()")public void doBeforeWithSlave(JoinPoint joinPoint) {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();//获取当前切点方法对象Method method = methodSignature.getMethod();if (method.getDeclaringClass().isInterface()) {//判断是否为借口方法try {//获取实际类型的方法对象method = joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes());} catch (NoSuchMethodException e) {LOG.error("方法不存在!", e);}}if (null == method.getAnnotation(TargetDataSource.class)) {DynamicDataSourceContextHolder.setSlave();}}}

7、在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 SpringBootDynamicDatasourceStartedApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDynamicDatasourceStartedApplication.class, args);}}

8、使用,在你需要切换数据源的service方法上加上注解就OK,注意:如果你使用了接口对service层进行分离,那么注解需要添加到你的实现类的相关方法上。示例如下

@Service("userInfoService")public class UserInfoServiceImpl implements UserInfoService {private static final Logger LOG = Logger.getLogger(UserInfoServiceImpl.class);@Resourceprivate UserInfoMapper userInfoMapper;@TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)@Overridepublic List<UserInfo> listAll() {return userInfoMapper.listAll();}//使用此注解来切换到想切换的数据源@TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)@Overridepublic int insert(UserInfo userInfo) {return userInfoMapper.insert(userInfo);}}

2.3 配置数据源(不用DruidDataSourceBuilder)

当然,上面也可以不用DruidDataSourceBuilder,想下面一样:

自定义 DataSource 类型的 @Bean 可以覆盖默认设置,

@Bean@ConfigurationProperties(prefix="app.datasource")public DataSource dataSource() {return new FancyDataSource();}
app.datasource.url=jdbc:h2:mem:mydbapp.datasource.username=saapp.datasource.pool-size=30

Spring Boot也提供了一个工具类 DataSourceBuilder 用来创建标准的数据源。如果需要重用 DataSourceProperties 的配置,可以用它初始化一个 DataSourceBuilder

@Bean@ConfigurationProperties("app.datasource")public DataSource dataSource() {return DataSourceBuilder.create().build();}

在此场景中,保留了通过Spring Boot暴露的标准属性,通过添加 @ConfigurationProperties ,可以暴露在相应的命命名空间暴露其他特定实现的配置,具体详情可以参考DataSourceAutoConfiguration

配置两个数据源

创建多个数据源和创建一个工作都是一样的,如果使用JDBC或JPA的默认自动配置,需要将其中一个设置为 @Primary (然后它就能被任何 @Autowired 注入获取)。

@Bean@Primary@ConfigurationProperties("app.datasource.foo")public DataSourceProperties fooDataSourceProperties() {return new DataSourceProperties();}@Bean@Primary@ConfigurationProperties("app.datasource.foo")public DataSource fooDataSource() {return fooDataSourceProperties().initializeDataSourceBuilder().build();}@Bean@ConfigurationProperties("app.datasource.bar")public DataSourceProperties barDataSourceProperties() {return new DataSourceProperties();}@Bean@ConfigurationProperties("app.datasource.bar")public DataSource barDataSource() {return barDataSourceProperties().initializeDataSourceBuilder().build();}
app.datasource.foo.type=com.zaxxer.hikari.HikariDataSourceapp.datasource.foo.maximum-pool-size=30app.datasource.bar.url=jdbc:mysql://localhost/testapp.datasource.bar.username=dbuserapp.datasource.bar.password=dbpassapp.datasource.bar.max-total=30

3. 开启事务后数据源切换失败

问题:
进行数据源切换配置运行成功后,我们数据源切换的Service层加入事务控制,发现此时数据源切换失败,dao只会访问默认的数据源。出现这个问题的原因,我在网上找了一下:

\1. AOP可以触发数据源字符串的切换,这个没问题
\2. 数据源真正切换的关键是 AbstractRoutingDataSource 的 determineCurrentLookupKey() 被调用,此方法是在open connection时触发
\3. 事务是在connection层面管理的,启用事务后,一个事务内部的connection是复用的,所以就算AOP切了数据源字符串,但是数据源并不会被真正修改

也就是说****将数据源切换和事务处理都放在Service层,则数据源切换时失效的****。

3.1 方法:控制事务和切换顺序

https://www.jianshu.com/p/216e17c3a9ba

下面提供两个解决方案:

1,在dao实现事务控制:此种方式不太合理,在于再dao层加入事务控制,无法保证一个方法内的事务的一致性

2.将数据源切换和事务开启按顺序进行,先切换,再开启事务。如下所示:

@DataSource(“erp”)
List findRelationList(String redisKey);
List findRelationList1(String redisKey);

@Override
public List findRelationList(String redisKey){
return ((SyncErpDataService)AopContext.currentProxy()).findRelationList1(redisKey);
}

*@Transactional(readOnly = true)*
@Override
public List findRelationList1(String redisKey){
return erpUserRoleDao.findRelationList(redisKey);
}

先进行数据源切换,再通过代理的方式调用另一个方法,该方法上开启事务,访问dao层。 本人测试不通过代理的方式进行方法调用的话,仍然没法数据源切换成功!!!

*切换数据源和开启事务分离:*

*控制器层->方法1(切换数据源,使用代理方式调用方法2)->方法2(开启事务,执行多个dao操作)*

3.2 方法:重写MultiDataSourceTransaction事务

可以查看:

https://blog.csdn.net/gaoshili001/article/details/79378902

https://github.com/baomidou/dynamic-datasource-spring-boot-starter/issues/83

3.3 错误 jdbcUrl is required with driverClassName.

在Spring Boot 1.5.x升级到Spring Boot 2.0后,一些配置及用法有了变化,如果不小心就会碰到“jdbcUrl is required with driverClassName.”的错误,详细错误信息可以查看这里。

经过一番努力,查找资料,终于找到了解决办法,在这里分享出来,省得后来者再去查找资料。

第一种方法:
在配置文件中使用spring.datasource.jdbc-url,而不是通常使用的spring.datasource.url。
在这里也请大佬们帮忙解释下spring.datasource.jdbc-url和spring.datasource.url的区别。

第二种方法:
在数据源配置时使用DataSourceProperties方法。

最后,我的微信号是chinesedragon,二维码在最后,欢迎朋友们共同学习。

GITEE源码https://gitee.com/shupengluo/SpringBoot2.0-MultiDataSource
GITHUB源码https://github.com/luoshupeng/SpringBoot2.0-MultiDataSource

参考:https://my.oschina.net/chinesedragon/blog/1647846

3.4错误org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope

使用spring事务注解的时候遇到过这个问题吗?

下面我们来看两种写法,第一种

@Transactionalpublic UserEntity login1(UserEntity user) {userDao.update(6);if(userDao.update(6)){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return user;}

第二种,****调用login()****

    public UserEntity login(UserEntity user) {this.test();return user;}@Transactionalpublic void test(){userDao.update(6);if(userDao.update(6)){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}}

第一种写法的时候,回滚是起作用的,而第二种写法的时候就会报错,错误就是一开始提到的。

为什么会这样呢?

*spring里事务是用注解配置的,当一个方法没有接口,单单只是一个方法不是服务时,事务的注解是不起作用的,需要回滚时就会报错。*

​ 出现这个问题的根本原因在于AOP的实现原理。由于@Transactional 的实现原理是AOP,AOP的实现原理是动态代理,换句话说,自调用时不存在代理对象的调用,这时不会产生我们注解@Transactional 配置的参数,自然无效了。

​ 虽然可以直接从容器中获取代理对象,但这样有侵入之嫌,不推荐。

sping的事务是通过注解配置上去的,而下面的那个方法并没有接口,在实现类里面只是一个简单的方法而已,对于事务的注解来说没有任何作用,所以在这个方法里面调用回滚的方法自然就报错了。

所以在以后的项目中如果你要使用事务,那么请记住,一个服务一个事务,一次请求一个事务,千万不要想着用调用方法,然后再一个方法上面加事务。你只能调用另外一个服务,在另外一个服务上面加事务。

也是在此记录一笔:

事务必须用在服务上,且一个服务一个事务,不得嵌套。

****不能在Controller或server层同时开启事务和切换数据源,是无法在去切换数据源的
如果用切片: 切换数据源的order值要比事务切面的值小,这样优先级高!否则自动切换数据源将会失败!
目前我们create/update接口内部,调用多次数据库切换,所以不能开启事务

注意:(1)有@Transactional注解的方法,方法内部不可以做切换数据库操作
(2)在同一个service其他没有@Transactional注解的方法调用带@Transactional的方法,事务不起作用,****

4.参考:

https://github.com/TavenYin/spring-dynamic-datasource

https://github.com/helloworlde/SpringBoot-DynamicDataSource

https://www.jianshu.com/p/0a485c965b8b

https://blog.csdn.net/twomr/article/details/79137056

https://github.com/baomidou/dynamic-datasource-spring-boot-starter/issues/83

https://blog.csdn.net/m0_37837382/article/details/81171393

https://www.cnblogs.com/jpfss/p/8295692.html

http://blog.zollty.com/b/archive/solution-of-spring-multiple-datasource.html

https://segmentfault.com/a/1190000015786019

https://juejin.im/post/5a927d23f265da4e7e10d740

1.基于Mybatis多SqlSession实例分开扫描各自Mapper

https://blog.csdn.net/isea533/article/details/46815385

http://www.cnblogs.com/ityouknow/p/6102399.html

https://blog.csdn.net/maoyeqiu/article/details/74011626

https://blog.csdn.net/neosmith/article/details/61202084

https://www.cnblogs.com/Alandre/p/6611813.html

2.动态AOP数据源

https://github.com/helloworlde/SpringBoot-DynamicDataSource/blob/roundrobin/src/main/java/cn/com/hellowood/dynamicdatasource/configuration/DynamicDataSourceContextHolder.java

https://github.com/baomidou/dynamic-datasource-spring-boot-starter/tree/master/src/main/java/com/baomidou/dynamic/datasource

https://www.hifreud.com/2017/07/06/spring-boot-18-data-access/

https://juejin.im/post/5c9f52de51882567c94e7184

3.另外一种思路:

https://github.com/hs-web/hsweb-framework/blob/master/hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/dynamic/DynamicSqlSessionFactory.java

SpringBoot多数据源切换详解,以及开启事务后数据源切换失败处理相关推荐

  1. Springboot多数据源配置详解

    Springboot多数据源配置详解 概念 配置 多数据源使用 概念 一般来说,我们正常的业务只涉及一个数据源,在特定的业务场景中需要使用多个数据源的情况,就需要配置多个数据源来满足特定的业务需求.本 ...

  2. Springboot整合redis配置详解

    Springboot整合redis配置详解 1.导入依赖 <dependency><groupId>org.springframework.boot</groupId&g ...

  3. Springboot@Configuration和@Bean详解

    Springboot@Configuration和@Bean详解 一.@Configuration @Target({ElementType.TYPE}) @Retention(RetentionPo ...

  4. SpringBoot (6)---RestTemplate方法详解(2)

    SpringBoot (6)---RestTemplate方法详解(2) 说明 上一篇SpringBoot 2.1 | 第三篇:RestTemplate请求HTTP(1)简单运用了RestTempla ...

  5. phpstudy mysql恢复数据_MySQL_详解MySQL误操作后怎样进行数据恢复,一、开启binlog。 首先查看binlo - phpStudy...

    详解MySQL误操作后怎样进行数据恢复 一.开启binlog. 首先查看binlog是否开启 mysql> show variables like "log_bin"; +- ...

  6. 仿抖音短视频APP源码,顶部导航栏切换详解

    仿抖音短视频APP源码,顶部导航栏切换详解的相关代码 class DaoHangNan extends StatefulWidget //继承StatefulWidget{TabController ...

  7. SpringBoot定时任务@Scheduled注解详解

    SpringBoot定时任务@Scheduled注解详解 项目开发中,经常会遇到定时任务的场景,Spring提供了@Scheduled注解,方便进行定时任务的开发 概述 要使用@Scheduled注解 ...

  8. mysql 事物的持久性是指_详解MySQL中事务的持久性实现原理

    前言 说到数据库事务,大家脑子里一定很容易蹦出一堆事务的相关知识,如事务的ACID特性,隔离级别,解决的问题(脏读,不可重复读,幻读)等等,但是可能很少有人真正的清楚事务的这些特性又是怎么实现的,为什 ...

  9. mysql从挂了数据怎么恢复_详解MySQL误操作后怎样进行数据恢复

    一.开启binlog. 首先查看binlog是否开启 mysql> show variables like "log_bin"; +---------------+----- ...

  10. 《MySQL安装流程详解》及《MySQL安装一直失败,重新安装显示已安装》

    <MySQL安装流程详解>及<MySQL安装一直失败,重新安装显示已安装> 本文由博主经过查阅网上资料整理总结后编写,如存在错误或不恰当之处请留言以便更正,内容仅供大家参考学习 ...

最新文章

  1. 如何进行网站主题模型优化_如何进行网站关键词优化
  2. android自定义滑块解锁,android 滑动解锁
  3. PHP标记风格,编码规范
  4. 域控制器安装完成没有_tcp_udp_Windows 安装 Jupyter Lab
  5. EXTJS学习系列提高篇:第二十五篇(转载)作者殷良胜,ext2.2打造全新功能grid系列--右键菜单篇...
  6. json学习系列(7)JSONBuilder的用法
  7. TensorFlow学习笔记(一)安装、配置、基本用法
  8. 利用CRM中间件Middleware从ERP下载Customer Material的常见错误
  9. JavaWeb重要知识点总结
  10. Yii中使用的简单方法
  11. 企业使用开源软件的风险
  12. centos7 安装/卸载 任意版本的mariadb(mysql)
  13. Copilot 自动编程AI工具
  14. maya 替换名称_maya替换对象,MAYA
  15. HTML jquery笔试题,2019Web前端面试题及答案汇总-Jquery篇
  16. Android-深色模式篇
  17. python变量名包括_python中变量的命名以及使用
  18. C语言中,求三个数中最大数
  19. HTML学生个人网站作业设计:电影网站设计——叮当电影(5页) HTML+CSS+JavaScript 简单DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载
  20. 哈尔滨工业大学深圳计算机学院院长,哈工大计算机学院院长徐晓飞来访我院

热门文章

  1. ‘numeric_limits’ is not a member of ‘std‘解决方法
  2. thrift (转)
  3. [整理]让winCE支持中文界面(可切换至英文界面)
  4. JavaScript--百度地图那些坑
  5. 基础才是重中之重~LazyInitializer.EnsureInitialized对属性实现化的性能优化
  6. 通过kafka提供的命令来查看offset消费情况
  7. 使用存储过程备份SqlServer数据库
  8. 众包专访:告别接包黑历史,来到开源中国众包接包小记
  9. 为storyBoard面板中添加圆角属性(添加自定义的属性)
  10. 2013年总结(4)-人脉