You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot(Spring Data JPA)配置PostgreSQL只读副本最简方案咨询

Spring Boot + Spring Data JPA 配置AWS Aurora只读副本最简方案

我之前在项目里刚好做过类似的配置,对接AWS Aurora PostgreSQL的主备端点实现读写分离,给你分享下最简的落地方式——核心是用多数据源配置+分Repository包的模式,不需要复杂的AOP或路由逻辑,就能让读操作自动走副本,写操作走主库。

1. 配置多数据源(application.yml)

首先在配置文件里分别定义主库(写入)和副本库(读取)的连接信息:

spring:
  datasource:
    primary:
      url: jdbc:postgresql://<你的Aurora主节点端点>:5432/your_database_name
      username: your_db_user
      password: your_db_password
      driver-class-name: org.postgresql.Driver
    replica:
      url: jdbc:postgresql://<你的Aurora副本节点端点>:5432/your_database_name
      username: your_db_user
      password: your_db_password
      driver-class-name: org.postgresql.Driver
  jpa:
    hibernate:
      ddl-auto: none # 根据实际需求调整,生产环境建议用none
    properties:
      hibernate:
        show_sql: true # 开发环境开启,生产建议关闭
        format_sql: true
        dialect: org.hibernate.dialect.PostgreSQLDialect

2. 注册数据源Bean

创建一个配置类,把两个数据源注册为Spring Bean,主库加上@Primary注解作为默认数据源:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "replicaDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.replica")
    public DataSource replicaDataSource() {
        return DataSourceBuilder.create().build();
    }
}

3. 配置主库JPA实体管理器与事务管理器

创建主库的JPA配置类,指定主库Repository的存放路径,关联主数据源:

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableJpaRepositories(
        basePackages = "com.yourproject.repository.primary", // 主库Repository包路径
        entityManagerFactoryRef = "primaryEntityManagerFactory",
        transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryJpaConfig {

    private final DataSource primaryDataSource;

    public PrimaryJpaConfig(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
        this.primaryDataSource = primaryDataSource;
    }

    @Primary
    @Bean(name = "primaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        return builder
                .dataSource(primaryDataSource)
                .packages("com.yourproject.entity") // 实体类包路径
                .persistenceUnit("primary")
                .properties(properties)
                .build();
    }

    @Primary
    @Bean(name = "primaryTransactionManager")
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("primaryEntityManagerFactory") LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory) {
        return new JpaTransactionManager(primaryEntityManagerFactory.getObject());
    }
}

4. 配置副本库JPA实体管理器与事务管理器

同理,创建副本库的JPA配置类,指定副本库Repository的存放路径,关联副本数据源,同时开启只读优化:

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableJpaRepositories(
        basePackages = "com.yourproject.repository.replica", // 副本库Repository包路径
        entityManagerFactoryRef = "replicaEntityManagerFactory",
        transactionManagerRef = "replicaTransactionManager"
)
public class ReplicaJpaConfig {

    private final DataSource replicaDataSource;

    public ReplicaJpaConfig(@Qualifier("replicaDataSource") DataSource replicaDataSource) {
        this.replicaDataSource = replicaDataSource;
    }

    @Bean(name = "replicaEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean replicaEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        properties.put("hibernate.connection.readOnly", true); // 标记为只读连接,Hibernate会做相应优化
        return builder
                .dataSource(replicaDataSource)
                .packages("com.yourproject.entity")
                .persistenceUnit("replica")
                .properties(properties)
                .build();
    }

    @Bean(name = "replicaTransactionManager")
    public PlatformTransactionManager replicaTransactionManager(
            @Qualifier("replicaEntityManagerFactory") LocalContainerEntityManagerFactoryBean replicaEntityManagerFactory) {
        return new JpaTransactionManager(replicaEntityManagerFactory.getObject());
    }
}

5. 拆分Repository实现读写分离

最后一步很简单:

  • 把需要写入/更新/删除操作的Repository(比如带save()delete()方法的)放到com.yourproject.repository.primary包下
  • 把只需要读取操作的Repository(比如带findAll()findById()方法的)放到com.yourproject.repository.replica包下

Spring Data JPA会自动根据Repository所在的包,匹配对应的EntityManager和事务管理器,实现读写操作自动路由到对应节点。

注意事项

  • 确保Aurora副本节点的数据库用户只有只读权限,避免误操作导致报错
  • 如果需要在同一个业务方法里同时执行读写操作,建议统一使用主库的事务管理器(比如在方法上加@Transactional("primaryTransactionManager")
  • 开发环境可以开启Hibernate的SQL日志,验证读写操作是否路由到了正确的数据源

内容的提问来源于stack exchange,提问作者msiakis

火山引擎 最新活动