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




