本文目录导读:

- 数据库直连方案(最简单)
- MyBatis 多数据源方案(推荐)
- 使用MySQL的Federated引擎(数据库层跨库)
- 使用Apache Calcite(高级方案)
- 实用案例:Spring Boot + MyBatis多数据源
- 性能优化建议
- 推荐方案
在Java中实现跨库查询,主要有以下几种常见方案,具体选择取决于你的业务场景和数据源类型:
数据库直连方案(最简单)
使用 JDBC 多数据源
public class CrossDatabaseQuery {
// 查询数据库A
public List<User> queryFromDB1() {
String sql = "SELECT * FROM user WHERE id = ?";
try (Connection conn = DataSource1.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
// 执行查询
}
}
// 查询数据库B
public List<Order> queryFromDB2() {
String sql = "SELECT * FROM order WHERE user_id = ?";
try (Connection conn = DataSource2.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
// 执行查询
}
}
// 在Java中组合结果
public UserDTO getCombinedData(Long userId) {
User user = queryFromDB1(userId);
List<Order> orders = queryFromDB2(userId);
UserDTO dto = new UserDTO();
dto.setUser(user);
dto.setOrders(orders);
return dto;
}
}
MyBatis 多数据源方案(推荐)
配置多数据源
# application.yml
spring:
datasource:
# 数据源1
db1:
url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
# 数据源2
db2:
url: jdbc:mysql://localhost:3306/db2
username: root
password: 123456
配置类
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource ds) {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(ds);
// 指定mapper路径
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/db1/*.xml"));
return bean.getObject();
}
@Bean
public SqlSessionFactory sqlSessionFactory2(@Qualifier("dataSource2") DataSource ds) {
// 类似配置
}
}
使用MySQL的Federated引擎(数据库层跨库)
MySQL配置
-- 创建FEDERATED表
CREATE TABLE federated_user (
id INT NOT NULL,
name VARCHAR(50),
PRIMARY KEY (id)
) ENGINE=FEDERATED
CONNECTION='mysql://user:password@host:port/db1/user';
Java调用
// 直接查询联合表
@Service
public class CrossDatabaseService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<Map<String, Object>> crossQuery() {
String sql = "SELECT u.*, o.order_no " +
"FROM local_user u " +
"JOIN federated_order o ON u.id = o.user_id";
return jdbcTemplate.queryForList(sql);
}
}
使用Apache Calcite(高级方案)
Maven依赖
<dependency>
<groupId>org.apache.calcite</groupId>
<artifactId>calcite-core</artifactId>
<version>1.32.0</version>
</dependency>
实现代码
public class CalciteCrossQuery {
public ResultSet executeCrossQuery() throws Exception {
// 创建连接
Properties info = new Properties();
info.setProperty("lex", "JAVA");
Connection connection = DriverManager.getConnection("jdbc:calcite:", info);
// 创建Schema
SchemaPlus rootSchema =
connection.unwrap(CalciteConnection.class).getRootSchema();
// 添加数据源1
SchemaPlus db1Schema = rootSchema.add("DB1",
new JdbcSchema(dataSource1));
// 添加数据源2
SchemaPlus db2Schema = rootSchema.add("DB2",
new JdbcSchema(dataSource2));
// 执行跨库查询
String sql = "SELECT u.name, o.order_no " +
"FROM DB1.USER u " +
"JOIN DB2.ORDER o ON u.id = o.user_id";
Statement stmt = connection.createStatement();
return stmt.executeQuery(sql);
}
}
实用案例:Spring Boot + MyBatis多数据源
完整示例
@Service
public class CrossDatabaseServiceImpl implements CrossDatabaseService {
@Resource(name = "db1SqlSessionTemplate")
private SqlSessionTemplate db1SqlSessionTemplate;
@Resource(name = "db2SqlSessionTemplate")
private SqlSessionTemplate db2SqlSessionTemplate;
@Override
@Transactional(rollbackFor = Exception.class)
public List<Map<String, Object>> crossQueryWithTransaction() {
// 使用动态数据源切换
DatabaseContextHolder.setDatabaseType("DB1");
List<User> users = userMapper.selectAll();
DatabaseContextHolder.setDatabaseType("DB2");
List<Order> orders = orderMapper.selectByUserIds(
users.stream().map(User::getId).collect(Collectors.toList())
);
// Java层面组合数据
return combineData(users, orders);
}
}
性能优化建议
// 批量查询优化
public List<Map<String, Object>> optimizedCrossQuery(List<Long> userIds) {
// 1. 分批查询
int batchSize = 100;
List<Map<String, Object>> result = new ArrayList<>();
for (int i = 0; i < userIds.size(); i += batchSize) {
List<Long> batch = userIds.subList(i,
Math.min(i + batchSize, userIds.size()));
// 2. 并行查询不同数据库
CompletableFuture<List<User>> usersFuture =
CompletableFuture.supplyAsync(() -> queryUsers(batch));
CompletableFuture<List<Order>> ordersFuture =
CompletableFuture.supplyAsync(() -> queryOrders(batch));
// 3. 等待两个查询完成
CompletableFuture<Void> combinedFuture =
usersFuture.thenCombine(ordersFuture, (users, orders) -> {
// 合并结果
return combine(users, orders);
});
result.addAll(combinedFuture.join());
}
return result;
}
推荐方案
- 简单场景:使用JDBC多数据源或MyBatis多数据源
- 数据量大:使用数据库本身的跨库查询功能(如MySQL Federated)
- 复杂查询:使用Apache Calcite
- 微服务架构:考虑使用分布式查询中间件(如ShardingSphere)
选择方案时需要考虑数据一致性、性能和维护成本的平衡。