Java案例如何实现跨库查询?

wen java案例 82

本文目录导读:

Java案例如何实现跨库查询?

  1. 数据库直连方案(最简单)
  2. MyBatis 多数据源方案(推荐)
  3. 使用MySQL的Federated引擎(数据库层跨库)
  4. 使用Apache Calcite(高级方案)
  5. 实用案例:Spring Boot + MyBatis多数据源
  6. 性能优化建议
  7. 推荐方案

在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;
}

推荐方案

  1. 简单场景:使用JDBC多数据源或MyBatis多数据源
  2. 数据量大:使用数据库本身的跨库查询功能(如MySQL Federated)
  3. 复杂查询:使用Apache Calcite
  4. 微服务架构:考虑使用分布式查询中间件(如ShardingSphere)

选择方案时需要考虑数据一致性性能维护成本的平衡。

抱歉,评论功能暂时关闭!