<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql连接-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis依赖-->
<dependency><groupId&batis</groupId><artifactId>mybatis</artifactId><version>3.4.6</version>
</dependency>
<dependency><groupId&batis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version>
</dependency>
<!--druid连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version>
</dependency>
<dependency><groupId>org.apachemons</groupId><artifactId>commons-lang3</artifactId>
</dependency>
spring:datasource:driver-class-name: sql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test?useSSL=falseusername: rootpassword: rootdruid:# 初始化大小,最小,最大initialSize: 10minIdle: 5maxActive: 100# 获取数据库连接等待的超时时间maxWait: 60000maxOpenPreparedStatements: -1# 系统启动时通过该sql语句验证数据库是否可用,如果不配置validationQuery,则下面三项无效validationQuery: SELECT 1# 启用空闲连接检测,以便回收testWhileIdle: true# 从连接池获取连接时,是否检测连接可用性,开启性能会有些许影响testOnBorrow: false# 释放连接到连接池时,是否检测连接可用性,开启性能会有些许影响testOnReturn: false# 配置多久进行一次检测,检测需要关闭的空闲连接 单位毫秒timeBetweenEvictionRunsMillis: 60000# 配置连接在池中的最小生存时间minEvictableIdleTimeMillis: 300000# 配置连接在池中的最大生存时间maxEvictableIdleTimeMillis: 400000# 打开PSCache,并且指定每个连接上PSCache的大小poolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙filters: statuseGlobalDataSourceStat: true# 通过connectProperties属性来打开mergeSql功能;慢SQL记录connectProperties: Sql=true;druid.stat.slowSqlMillis=5000# 设置链接超时回收removeAbandoned: true# 从获取到链接开始,超过这么长时间,链接将被连接池强制回收removeAbandonedTimeout: 180# 强制回收链接是,将堆栈追踪信息打印到日志中logAbandoned: truebreakAfterAcquireFailure: trueconnectionErrorRetryAttempts: 0mybatis:type-aliases-package: itymapper-locations: classpath:mapper/*.xmlconfiguration:auto-mapping-behavior: fulluse-generated-keys: truejdbc-type-for-null: null
batis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;/*** @Auther: ljq* @Date: 2023/8/22 13:31*/
@MapperScan(basePackages = "com.mapper")
// 必须依赖阿里的数据源,排除默认的数据源
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
@EnableScheduling// 启用定时任务
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}
管理数据源dataSource
import com.alibaba.druid.pool.DruidDataSource;/*** 动态数据源定时器管理。存放连接源** @Auther: ljq* @Date: 2023/8/22 13:37*/
public class DDSSource {/*** 动态数据源*/private final DruidDataSource dds;public DDSSource(DruidDataSource dds) {this.dds = dds;}/*** 关闭连接。*/public void close() {dds.close();}/*** 获取连接源*/public DruidDataSource getDds() {return dds;}
}
使用单例的方式存放已经连接好的数据源
import com.alibaba.druid.pool.DruidDataSource;
import org.apachemons.lang3.ObjectUtils;
import java.util.HashMap;
import java.util.Map;/*** 动态数据源管理器** @Auther: ljq* @Date: 2023/8/22 13:37*/
public class DDSHolder {private static DDSHolder ddsHolder;/*** 管理动态数据源列表。<工程编码,数据源>*/private final Map<String, DDSSource> ddsMap = new HashMap<>();private DDSHolder() {}/*** 获取单例对象*/public static DDSHolder instance() {if (ObjectUtils.isEmpty(ddsHolder)) {ddsHolder = new DDSHolder();}return ddsHolder;}/*** 添加动态数据源。*/public synchronized void addDDS(String databaseName, DruidDataSource dds) {DDSSource ddst = new DDSSource(dds);ddsMap.put(databaseName, ddst);}/*** 查询动态数据源** @param databaseName 数据库名* @return dds*/public synchronized DruidDataSource getDDS(String databaseName) {if (ainsKey(databaseName)) {DDSSource ddst = (databaseName);Dds();}return null;}/*** 关闭数据源** @param databaseName 数据库名*/public synchronized void closeDDSByName(String databaseName) {if (ainsKey(databaseName)) {DDSSource ddsSource = (databaseName);ddsSource.close();ve(databaseName);}}
}
用于区分当前连接的数据源,专门用于切换数据源,只需要在要切换的数据源的地方调用set
/*** @Auther: ljq* @Date: 2023/8/22 13:36*/
public class DBIdentifier {/*** 用不同的工程编码来区分数据库*/private static final ThreadLocal<String> databaseName = new ThreadLocal<>();/*** 获取当前使用的数据源*/public static String getDatabaseName() {();}/*** 设置数据源** @param code 数据库名*/public static void setDatabaseName(String code) {databaseName.set(code);}
}
通过重写了DruidDataSource来管理动态动态连接
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
slf4j.Slf4j;
import org.apachemons.lang3.ObjectUtils;
import org.apachemons.lang3.StringUtils;import java.sql.SQLException;/*** 定义动态数据源派生类。从基础的DataSource派生,动态性自己实现** @Auther: ljq* @Date: 2023/8/22 13:36*/
@Slf4j
public class DynamicDataSource extends DruidDataSource {/*** 改写本方法是为了在请求不同工程的数据时去连接不同的数据库*/@Overridepublic DruidPooledConnection getConnection() {String databaseName = DatabaseName();if (StringUtils.isBlank(databaseName)) {databaseName = "test";// 如果默认的database是null,这调用默认的库}//1、获取数据源DruidDataSource dds = DDSHolder.instance().getDDS(databaseName);// 从单例中获取连接源//2、如果数据源不存在则创建if (ObjectUtils.isEmpty(dds)) {try {DruidDataSource newDDS = initDDS(databaseName);// 初始化连接源DDSHolder.instance().addDDS(databaseName, newDDS);// 将连接后的数据源添加至管理中} catch (IllegalArgumentException e) {("初始化失败。 databaseName:" + databaseName);return null;}}dds = DDSHolder.instance().getDDS(databaseName);// 获取数据源try {Connection();} catch (SQLException e) {(e.getMessage());return null;}}// 调用父类的getConnection@Overridepublic DruidPooledConnection getConnection(String username, String password) throws SQLException {Connection();}/*** 以当前数据对象作为模板复制一份*/private DruidDataSource initDDS(String databaseName) throws IllegalArgumentException {DruidDataSource dds = new DruidDataSource();// 2、复制PoolConfiguration的属性dds.setInitialSize(10);dds.setMaxActive(50);dds.setMinIdle(5);dds.setMaxWait(6000);dds.setValidationQuery("SELECT 1");// 设置链接超时回收dds.setRemoveAbandoned(true);// 从获取到链接开始,超过这么长时间,链接将被连接池强制回收dds.setRemoveAbandonedTimeout(180);// 强制回收链接是,将堆栈追踪信息打印到日志中dds.setLogAbandoned(true);// 一定要在yml中配置一个默认的数据源,否则将无法进行连接if ("test".equals(databaseName)) {// 如果是默认数据源,调用yml中的信息进行连接// 添加默认的连接源String urlFormat = Url();dds.setUrl(urlFormat);dds.DriverClassName());dds.Username());dds.Password());return dds;}dds.setUrl(DBMgr.instance().getDBUrl(databaseName));dds.DriverClassName());// 公用一个driver-class-namedds.setUsername(DBMgr.instance().getDBUserName(databaseName));dds.setPassword(DBMgr.instance().getDBPassword(databaseName));return dds;}
}
ity.DDSource;
import com.mapper.DDSourceMapper;
import org.apachemons.lang3.BooleanUtils;
import org.apachemons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 项目数据库管理。提供连接唯一编码查询数据库连接配置信息。** @Auther: ljq* @Date: 2023/8/22 13:39*/
@Component
public class DBMgr {private static DBMgr dBMgr;/*** 保存数据库URL的映射关系。*/private final Map<String, String> dbUrlMap = new HashMap<>();/*** 保存数据名称的映射关系*/private final Map<String, String> dbUserNameMap = new HashMap<>();/*** 保存数据名称的映射关系*/private final Map<String, String> dbPasswordMap = new HashMap<>();/*** 存放最后更新时间的,用于判断当前数据库连接信息是否被修改,如果被修改,需要重新连接数据库*/private final Map<String, Date> dbUpdateTime = new HashMap<>();@Autowiredprivate DDSourceMapper dDSourceMapper;private DBMgr() {}// 每五分钟运行一次,这里使用定时任务是为了类似实时更新的,根据数据库中的update_time字段判断是否需要重新连接@Scheduled(fixedRate = 1000 * 60 * 5)private void process() {if (ObjectUtils.isEmpty(dBMgr)) {dBMgr = new DBMgr();}List<DDSource> dDSources = dDSourceMapper.selectAll();for (DDSource dDSource : dDSources) {boolean set = this.DbName(), DbUrl(), DbUsername(), DbPassword(), UpdateTime());// 不会查询出主库的信息,所以不存在aantdata会断开连接if (BooleanUtils.isTrue(set)) {// 需要关闭,在重新连接DDSHolder.instance().DbName());}}DBIdentifier.setDatabaseName("test");// 建议每次调用setDatabaseName之后重置为初始库}// 单例实例化public static DBMgr instance() {if (ObjectUtils.isEmpty(dBMgr)) {dBMgr = new DBMgr();}return dBMgr;}private boolean set(String dbName, String dbUrl, String userName, String dbPassword, Date updateTime) {boolean ck = false;dBMgr.dbUrlMap.put(dbName, dbUrl);dBMgr.dbUserNameMap.put(dbName, userName);dBMgr.dbPasswordMap.put(dbName, dbPassword);Date date = (dbName);// 通过判断修改时间判断是否需要重新连接if (ObjectUtils.isNotEmpty(date) && !updateTime.equals(date)) {ck = true;}dBMgr.dbUpdateTime.put(dbName, updateTime);return ck;}public String getDBUrl(String projectCode) {if (ainsKey(projectCode)) {(projectCode);}return null;}public String getDBUserName(String projectCode) {if (ainsKey(projectCode)) {(projectCode);}return null;}public String getDBPassword(String projectCode) {if (ainsKey(projectCode)) {(projectCode);}return null;}
}
配置默认数据源
slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
batis.spring.SqlSessionFactoryBean;
batis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.t.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import t.annotation.Bean;
import t.annotation.Configuration;
import io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;/*** @Auther: ljq* @Date: 2023/8/22 13:36*/
@Slf4j
@Configuration
@MapperScan(basePackages = "com.mapper", sqlSessionFactoryRef = "dynamicSqlSession")
public class DataSourceConfig {/*** 根据配置参数创建数据源。使用派生的子类。** @return 数据源*/@Bean(name = "dynamicDataSource")@ConfigurationProperties(prefix = "spring.datasource")public DataSource getDataSource() {DataSourceBuilder<?> builder = ate();pe(DynamicDataSource.class);return builder.build();}/*** 创建会话工厂。** @param dataSource 数据源* @return 会话工厂*/@Bean(name = "dynamicSqlSession")public SqlSessionFactory getSqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);try {bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));Object();} catch (Exception e) {("DynamicSqlSession 创建会话工厂失败");return null;}}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis//DTD Mapper 3.0//EN"".dtd">
<mapper namespace="com.mapper.DDSourceMapper"><resultMap id="BaseResultMap" type=ity.DDSource"><result property="dbName" column="db_name" jdbcType="VARCHAR"/><result property="dbUrl" column="db_url" jdbcType="VARCHAR"/><result property="dbUsername" column="db_username" jdbcType="VARCHAR"/><result property="dbPassword" column="db_password" jdbcType="VARCHAR"/><result property="createTime" column="create_time" jdbcType="TIMESTAMP"/><result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/></resultMap><sql id="Base_Column_List">db_name,db_title,db_url,db_username,db_password,create_time,update_time</sql><select id="selectAll" resultMap="BaseResultMap">select <include refid="Base_Column_List"/> from dd_sourcewhere db_name != 'test' -- 去除了自己需要连接的数据库,因为在启动程序的时候会自动使用yml的进行连接</select>
</mapper>
ity.DDSource;
import org.springframework.stereotype.Repository;import java.util.List;/*** @Auther: ljq* @Date: 2023/5/25 17:08*/
@Repository
public interface DDSourceMapper {List<DDSource> selectAll();
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;/*** 数据库字典表*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DDSource implements Serializable {private String dbName;private String dbUrl;private String dbUsername;private String dbPassword;private Date createTime;private Date updateTime;
}
使用上面的方式就可以完成数据库的动态连接了,DBMgr的中的定时任务也可以换成其他方式实现,我这样的虽然不是实时更新,但是还是省去了重启项目的时间,只要数据库中的update_time一变,定时任务就能监听到需要重连的数据库,这样也挺方便的
通过上面的这些方法,我们这就来使用一下吧,其实上面的配置中也是使用到了的,我们通过一个简单的service来测试吧
fig.DBIdentifier;
import com.mapper.OtherMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;/*** @Auther: ljq* @Date: 2023/5/28 17:18*/
@Service
public class TestService {@Autowiredprivate OtherMapper otherMapper;public void select(String indexCode) {try {// 设置需要连接的数据库名DBIdentifier.setDatabaseName("otherDatabase");// test是默认数据库,可以不显示调用List<OtherEntity> otherList = otherMapper.selectAll();// 查询数据库// ...System.out.println(otherList);} finally {DBIdentifier.setDatabaseName("test");// 设置回默认数据库}}
}
博客园:.html
部分在原来作者的基础上修改了
通过上面的这些方法我们可以了解到,需要切换数据库变得非常容易了,如果不需要使用我这种通过从主库获取连接信息的话,也可以使用ini配置文件实现,都是可以的
下面我来说一下这个流程
首先我们需要调用DBIdentifier.setDatabaseName这个方法来设置需要调用的数据库,在本地变量中存放数据库名,在通过查询方法,它会进入DynamicDataSource的getConnection方法,通过DatabaseName获取需要操作的数据库名,在通过DDSHolder获取连接源,如果连接源不存在,则需要初始化一个数据库,初始化完之后会返回给调用的查询,来获取查询数据
而这个DBMgr中就是存放需要连接的数据库的链接密码等信息,还一个定时任务负责更新连接源
为什么不用原来博主的自动销毁呢,我认为如果我一个链接时不时的就要调用,如果在某个特定时间不调用导致链接销毁,但是过了段时间又重新访问,这个时候又要做链接的操作,我觉得是有一点慢的,因为建立连接是需要时间的,所以我这边是通过修改时间来判断是否需要重新建立连接。
我的也不一定比原博主的好,如果这个连接数量过多或不怎么访问的结果都连接过来,这样也是一种浪费,各有各的好处
转载请注明原文链接,点个关注,谢谢大家支持
本文发布于:2024-02-02 18:23:25,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170686983445611.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |