先说结论:

在项目启动后,使用修改配置文件+restart()连接/连接池对象的方法解决。

 

-----------------------------------------------------

思路2:

使用读写锁,给restart()、setUrl()等代码块加写锁;

数据库操作方法加读锁。

这样就可以在修改数据库连接信息时,先获取写锁,保证数据库操作方法不能执行(如果修改到一半,有数据库操作方法执行的话,后续修改链接方法会报错,所以不能让数据库操作方法执行);

而在不修改连接时,数据库操作方法之间获取的是读锁,不会影响线程彼此之间的操作。

 

具体方法:

1.读写锁用法:

//公平读写锁
//ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
//非公平读写锁,默认false
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
//读锁
lock.readLock().lock();

//...
//省略数据库增删改查方法

//执行完后,释放读锁
lock.readLock().unlock();


/*-----------------------------------*/

//Druid数据库连接池,注意对象类型是 DruidDataSource
//@Autowired
//DruidDataSource dataSource;

//写锁
lock.writeLock().lock();

//修改数据库连接信息
dataSource.restart();
dataSource.setUrl("数据库url");
dataSource.setUserName("数据库用户名");
dataSource.setPassword("数据库密码");
dataSource.setDriverClassName("数据库驱动名称");

//执行完后,释放写锁
lock.writeLock().unlock();

2.使用AOP,切入调用数据库的mapper方法,调用前加读锁,调用后释放读锁;

3.对修改数据库连接的操作加写锁,修改完成后释放写锁。

 

-----------------------------------------------------

 

之前听同事说,有一个需求,要求在spring/springboot项目启动后,留一个接口,修改数据库连接。

意思就是项目启动后,本来我查的某一个数据库;然后突然要改为查询另一个数据库,怎么办。

可能主备库会用到?当主库挂了后,要在不影响项目运行的情况下改为查备库?

然后本人稍微研究了一下,把代码与思路放在下面。

 

-----------------------------------------------------

 

1.首先,spring/springboot项目可以使用druid链接池。

springboot下需要使用的jar包:
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

spring下需要使用的jar包:
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.1</version>
</dependency>

 

2.然后,配置好druid连接池,把它交给spring容器管理。

 

3.在代码中,使用注解把druid连接池对象注入,例如:

@Autowired 
DruidDataSource dataSource;

 

4.然后,就可以使用这个连接池对象修改链接信息了。需要先执行restart(),然后用set方法修改,然后修改后的链接信息就生效了,例如:

//注入Druid数据库连接池对象
@Autowired
DruidDataSource dataSource;

//注入一个测试用的mapper
@Autowired
UserMapper userMapper;


public void test(){
  //先查回一个用户名来,证明现在链接没有修改。(用的是启动spring时配置的链接信息)
  String username = userMapper.getUserNameById(1);
  System.out.println(username);
  
  //先执行restart(),然后才能执行set,否则会报错UnsupportedOperationException()
  dataSource.restart();
  
  //然后就能修改链接信息了
  //填写一个不存在的数据库,测试用
  dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/nobase?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true");
  //dataSource.setDriver("这里填驱动对象");
  //dataSource.setUsername("这里填用户名");
  //dataSource.setPassword("这里填密码");

  //然后再调用下mapper,此时会报错,因为本人将链接信息修改了,对应一个不存在的数据库,当然会报错
  String username = userMapper.getUserNameById(1);
  System.out.println(username);
}

注意事项:

1.需要先执行restart(),然后才能执行set,否则会报错UnsupportedOperationException()

2.在高并发场景下,需要做些限制才能使用,例如以下情况,执行set时,也会报错UnsupportedOperationException()

public void test(){
  String username = userMapper.getUserNameById(1);
  System.out.println(username);
  
  dataSource.restart();

  //如果执行restart后,有程序执行了mapper,则下方会报错
  String username = userMapper.getUserNameById(1);
  System.out.println(username);
 
  //由于上方执行了mapper,导致dataSource被init,所以这里会报错;详情见源码
  //执行set时必须在dataSource刚restart后才行(此时还没有被init)
  dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/nobase?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true");
 
  String username = userMapper.getUserNameById(1);
  System.out.println(username);
}

原理就是,dataSource执行setUrl等方法时,源码会判断DataSource是否已经被初始化(init);如果已经被初始化,则报错UnsupportedOperationException();

dataSource执行restart()方法后,会处于未被初始化的状态,所以才能执行setUrl等操作;

而当dataSource执行restart()方法后,执行mapper等数据库操作时,会被初始化(init);

接着想执行setUrl等方法,会发现dataSource被初始化了,然后报错,导致修改链接失败。

 

=======================================================

controller中注入DruidDataSource报错的,可以试下下面这个:

https://blog.csdn.net/zhu0836/article/details/84330666

=======================================================

正确解决方法

1.由于数据库连接初始化时一般读取的是配置文件(假如数据库连接url与用户名密码在config.properties中保存),所以启动项目时,让项目读取外部的配置文件;

2.项目启动后,可以先把配置文件中的数据库连接信息修改掉(config.properties),手动修改或写个Controller进行修改都可;

3.然后再写个Controller,其中调用一下dataSource.restart()就可以了;之后有线程执行数据库操作时,dataSource自动重新初始化,读取修改后的配置文件,就实现项目启动后修改数据库连接的效果了。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐