java通过JdbcTemplate连接多个(2个以上)不同类型的数据库

本文介绍了一种实现数据库连接管理和表信息获取的方法,涵盖了Oracle、MySQL和SQL Server等数据库类型,通过JdbcTemplate对象缓存减少资源消耗,并提供定时清理机制。

1、业务场景

要求获取不同数据库的表信息和表结构信息,数据库类型包括oracle,MySQL,SqlServer。

2、实现思路

step1 新增数据库连接信息(主机,端口,数据库类型,实例名,用户名,密码,状态)

step2 测试连接,成功状态正常,失败不正常

step3 创建JdbcTemplate对象,保存到map中(key=唯一标识符,value=JdbcTemplate对象),避免重复创建销毁

step4 通过SQL语句获取表信息和表结构

step5定时检查map,销毁不常用的JdbcTemplate对象

3、代码

工厂类生成JdbcTemplate对象。

package com.aadata.etl.cache;

import com.aadata.etl.model.DbConnInfo;
import com.aadata.etl.util.DateUtil;
import com.aadata.etl.util.StringUtil;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.text.ParseException;
import java.util.*;

@Component
@EnableScheduling
public class DbConnectCache {

    //还是用线程安全的集合比较好哈
    public static Map
<think>我们在之前已经讨论过使用JdbcTemplate获取多个数据库数据的基本方法(方案三)。现在,我将详细展开如何使用JdbcTemplate实现多数据源配置和操作。 核心思路:配置多个数据源,然后为每个数据源创建一个JdbcTemplate实例,在Service中注入这些JdbcTemplate实例,分别使用。 步骤: 1. 配置多个数据源(在application.properties或application.yml中配置多个数据库连接信息) 2. 创建多个数据源的配置类(或在一个配置类中配置多个DataSource Bean) 3. 为每个数据源创建JdbcTemplate Bean 4. 在Service中注入这些JdbcTemplate实例,并分别使用 具体实现: 1. 配置数据源信息(application.properties): ```properties # 第一个数据库 spring.datasource.db1.jdbc-url=jdbc:mysql://localhost:3306/db1 spring.datasource.db1.username=root spring.datasource.db1.password=root spring.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver # 第二个数据库 spring.datasource.db2.jdbc-url=jdbc:mysql://localhost:3306/db2 spring.datasource.db2.username=root spring.datasource.db2.password=root spring.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver ``` 2. 配置多个数据源和对应的JdbcTemplate(在配置类中): ```java @Configuration public class DatabaseConfig { // 配置第一个数据源 @Bean(name = "db1DataSource") @ConfigurationProperties(prefix = "spring.datasource.db1") public DataSource db1DataSource() { return DataSourceBuilder.create().build(); } // 配置第二个数据源 @Bean(name = "db2DataSource") @ConfigurationProperties(prefix = "spring.datasource.db2") public DataSource db2DataSource() { return DataSourceBuilder.create().build(); } // 为第一个数据源创建JdbcTemplate @Bean(name = "db1JdbcTemplate") public JdbcTemplate db1JdbcTemplate(@Qualifier("db1DataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } // 为第二个数据源创建JdbcTemplate @Bean(name = "db2JdbcTemplate") public JdbcTemplate db2JdbcTemplate(@Qualifier("db2DataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } } ``` 3. 在Service中使用这两个JdbcTemplate: ```java @Service public class MultiDBService { private final JdbcTemplate db1JdbcTemplate; private final JdbcTemplate db2JdbcTemplate; // 通过构造器注入,使用@Qualifier指定具体的Bean public MultiDBService( @Qualifier("db1JdbcTemplate") JdbcTemplate db1JdbcTemplate, @Qualifier("db2JdbcTemplate") JdbcTemplate db2JdbcTemplate) { this.db1JdbcTemplate = db1JdbcTemplate; this.db2JdbcTemplate = db2JdbcTemplate; } public List<User> getUsersFromDb1() { String sql = "SELECT * FROM users"; return db1JdbcTemplate.query(sql, (rs, rowNum) -> { User user = new User(); user.setId(rs.getLong("id")); user.setName(rs.getString("name")); // 设置其他属性 return user; }); } public List<Order> getOrdersFromDb2() { String sql = "SELECT * FROM orders"; return db2JdbcTemplate.query(sql, (rs, rowNum) -> { Order order = new Order(); order.setId(rs.getLong("id")); order.setProduct(rs.getString("product")); // 设置其他属性 return order; }); } // 同时从两个数据库获取数据并合并 public CombinedData getCombinedData() { List<User> users = getUsersFromDb1(); List<Order> orders = getOrdersFromDb2(); return new CombinedData(users, orders); } } ``` 4. 实体类(示例): ```java public class User { private Long id; private String name; // getters and setters } public class Order { private Long id; private String product; // getters and setters } public class CombinedData { private List<User> users; private List<Order> orders; // 构造方法、getters and setters } ``` 注意事项: - 如果两个数据库的表结构相同,可以使用同一个实体类,但通常不同数据库不同的表,所以使用不同的实体类。 - 在多数据源配置中,必须使用@Qualifier注解来指定注入的是哪个数据源或JdbcTemplate。 - 如果数据源较多,可以考虑使用Map来管理多个JdbcTemplate,但需要更复杂的配置。 - 事务管理:默认情况下,每个JdbcTemplate操作是独立事务。如果需要跨多个数据源的事务(分布式事务),则需要使用JTA事务管理器(如Atomikos)。 如何添加分布式事务支持? 如果需要跨两个数据库的事务,比如在同一个事务中同时更新两个数据库,则不能使用普通的DataSourceTransactionManager,而应该使用JTA事务管理器。可以使用Atomikos或Bitronix等。 示例(使用Atomikos): 1. 添加依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency> ``` 2. 修改数据源配置,使用AtomikosDataSourceBean: ```java @Bean(name = "db1DataSource") @ConfigurationProperties(prefix = "spring.datasource.db1") public DataSource db1DataSource() { // 使用Atomikos提供的DataSource return new AtomikosDataSourceBean(); } @Bean(name = "db2DataSource") @ConfigurationProperties(prefix = "spring.datasource.db2") public DataSource db2DataSource() { return new AtomikosDataSourceBean(); } ``` 3. 配置JTA事务管理器(Spring Boot会自动配置,但需要确保Atomikos依赖存在) 4. 在需要事务的方法上使用@Transactional注解: ```java @Transactional public void updateMultipleDatabases() { // 更新数据库1 db1JdbcTemplate.update("UPDATE ..."); // 更新数据库2 db2JdbcTemplate.update("UPDATE ..."); // 如果发生异常,两个操作都会回滚 } ``` 这样,当updateMultipleDatabases方法执行时,两个更新操作将在同一个分布式事务中。 总结: 使用JdbcTemplate操作多个数据库,核心在于配置多个数据源和对应的JdbcTemplate,然后在Service中注入使用。对于分布式事务,则需引入JTA事务管理器。 §§相关问题§§: 1. 如何使用JdbcTemplate进行分页查询? 2. JdbcTemplate如何执行批量更新操作? 3. 在Spring Boot中如何配置多个MongoDB数据源? 4. 使用JdbcTemplate时如何防止SQL注入攻击? </think> # Java后端使用JdbcTemplate获取多个数据库数据 使用JdbcTemplateJava后端获取多个数据库的数据是一种常见需求,下面我将详细解释实现方案并提供完整代码示例。 ## 解决方案:多JdbcTemplate实例配置 核心思路是为每个数据库配置独立的`DataSource`和`JdbcTemplate`实例,然后在服务层注入并使用这些实例。 ### 1. 添加依赖 (pom.xml) ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- JDBC 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 数据库驱动 (以MySQL为例) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies> ``` ### 2. 配置多个数据源 (application.yml) ```yaml spring: datasource: # 主数据库配置 primary: url: jdbc:mysql://localhost:3306/db1 username: user1 password: pass1 driver-class-name: com.mysql.cj.jdbc.Driver # 第二个数据库配置 secondary: url: jdbc:mysql://10.0.0.2:3306/db2 username: user2 password: pass2 driver-class-name: com.mysql.cj.jdbc.Driver ``` ### 3. 配置多个JdbcTemplate实例 ```java @Configuration public class DatabaseConfig { // 配置数据库数据源 @Bean @Primary @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } // 配置第二个数据库数据源 @Bean @ConfigurationProperties(prefix = "spring.datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } // 主数据库JdbcTemplate @Bean(name = "primaryJdbcTemplate") public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } // 第二个数据库JdbcTemplate @Bean(name = "secondaryJdbcTemplate") public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } } ``` ### 4. 服务层使用多个JdbcTemplate ```java @Service public class MultiDatabaseService { private final JdbcTemplate primaryJdbcTemplate; private final JdbcTemplate secondaryJdbcTemplate; @Autowired public MultiDatabaseService( @Qualifier("primaryJdbcTemplate") JdbcTemplate primaryJdbcTemplate, @Qualifier("secondaryJdbcTemplate") JdbcTemplate secondaryJdbcTemplate) { this.primaryJdbcTemplate = primaryJdbcTemplate; this.secondaryJdbcTemplate = secondaryJdbcTemplate; } /** * 从多个数据库获取数据并合并 */ public Map<String, Object> fetchDataFromMultipleSources() { Map<String, Object> result = new HashMap<>(); // 从主数据库获取数据 List<Map<String, Object>> primaryData = primaryJdbcTemplate.queryForList("SELECT * FROM primary_table"); // 从第二个数据库获取数据 List<Map<String, Object>> secondaryData = secondaryJdbcTemplate.queryForList("SELECT * FROM secondary_table"); // 合并结果 result.put("primaryDatabase", primaryData); result.put("secondaryDatabase", secondaryData); return result; } /** * 并行查询多个数据库(提高性能) */ public Map<String, Object> fetchDataInParallel() { Map<String, Object> result = new HashMap<>(); // 使用CompletableFuture实现并行查询 CompletableFuture<List<Map<String, Object>>> primaryFuture = CompletableFuture.supplyAsync(() -> primaryJdbcTemplate.queryForList("SELECT * FROM primary_table") ); CompletableFuture<List<Map<String, Object>>> secondaryFuture = CompletableFuture.supplyAsync(() -> secondaryJdbcTemplate.queryForList("SELECT * FROM secondary_table") ); // 等待所有查询完成 CompletableFuture.allOf(primaryFuture, secondaryFuture).join(); try { result.put("primaryDatabase", primaryFuture.get()); result.put("secondaryDatabase", secondaryFuture.get()); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException("Error fetching data", e); } return result; } } ``` ### 5. 控制器层调用 ```java @RestController @RequestMapping("/api/data") public class DataController { private final MultiDatabaseService dataService; @Autowired public DataController(MultiDatabaseService dataService) { this.dataService = dataService; } @GetMapping("/combined") public ResponseEntity<Map<String, Object>> getCombinedData() { Map<String, Object> result = dataService.fetchDataFromMultipleSources(); return ResponseEntity.ok(result); } @GetMapping("/parallel") public ResponseEntity<Map<String, Object>> getParallelData() { Map<String, Object> result = dataService.fetchDataInParallel(); return ResponseEntity.ok(result); } } ``` ## 关键点解释 1. **数据源配置**: - 使用`@ConfigurationProperties`绑定配置文件中的数据库连接参数 - `@Primary`注解标记主数据源(必须指定一个主数据源) - 为每个数据源创建独立的JdbcTemplate实例 2. **服务层使用**: - 使用`@Qualifier`注解区分不同JdbcTemplate实例 - 提供两种查询方式:顺序查询和并行查询 - 并行查询使用`CompletableFuture`提高性能 3. **异常处理**: - 捕获并处理可能的查询异常 - 使用try-catch块处理并行查询中的异常 ## 高级优化技巧 1. **连接配置**: 为每个数据源添加连接配置(如HikariCP): ```java @Bean @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create() .type(HikariDataSource.class) // 使用HikariCP连接池 .build(); } ``` 2. **动态数据源切换**: 实现动态数据源路由机制,根据上下文动态切换数据源: ```java public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DatabaseContextHolder.getDataSourceKey(); } } ``` 3. **自定义RowMapper**: 使用自定义的RowMapper处理查询结果: ```java public class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getLong("id")); user.setName(rs.getString("name")); return user; } } // 使用 List<User> users = jdbcTemplate.query("SELECT * FROM users", new UserRowMapper()); ``` 4. **批量操作**: 使用JdbcTemplate的批量操作提高效率: ```java jdbcTemplate.batchUpdate( "INSERT INTO users (name, email) VALUES (?, ?)", new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { User user = users.get(i); ps.setString(1, user.getName()); ps.setString(2, user.getEmail()); } public int getBatchSize() { return users.size(); } }); ``` ## 工作流程示意图 ```mermaid graph TD A[客户端请求] --> B[Controller] B --> C[Service] C --> D1[主数据库 JdbcTemplate] C --> D2[次数据库 JdbcTemplate] D1 --> E1[数据库1] D2 --> E2[数据库2] E1 --> F[返回数据] E2 --> F F --> G[组合结果] G --> H[返回客户端] ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值