基于Spring Boot与Spring Data JPA的多数据源配置

本文介绍了如何在Spring Boot项目中结合Spring Data JPA配置多数据源,通过代码示例展示了使用Hibernate作为ORM框架,创建BaseJpaConfg基类,并由两个子类分别配置两个不同数据库Schema的连接。这种配置方式允许项目中无缝切换数据源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于项目需要,最近研究了一下基于Spring Boot与Spring Data JPA的多数据源配置问题。以下是传统的单数据源配置代码。这里使用的是Spring的Annotation在代码内部直接配置的方式,没有使用任何XML文件。

@Configuration
@EnableJpaRepositories(basePackages = "org.lyndon.repository")
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class JpaConfig {
	
	private static final String DATABASE_DRIVER = "db.driver";
	private static final String DATABASE_URL = "db.url";
	private static final String DATABASE_USER = "db.user";
	private static final String DATABASE_PASSWORD = "db.password";
	private static final String PACKAGES_TO_SCAN = "packages.to.scan";
	private static final String HIBERNATE_DIALECT = "hibernate.dialect";
	private static final String HIBERNATE_SHOW_SQL = "hibernate.show.sql";
	
	@Resource
	private Environment env;
	
	@Bean
	public DataSource dataSource() {
		DruidDataSource source = new DruidDataSource();
		source.setDriverClassName(env.getRequiredProperty(DATABASE_DRIVER));
		source.setUrl(env.getRequiredProperty(DATABASE_URL));
		source.setUsername(env.getRequiredProperty(DATABASE_USER));
		source.setPassword(env.getRequiredProperty(DATABASE_PASSWORD));
		return source;
	}
	
	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
		factory.setDataSource(dataSource());
		factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);
		factory.setPackagesToScan(env.getRequiredProperty(PACKAGES_TO_SCAN));
		factory.setJpaProperties(hibernateProperties());
		factory.afterPropertiesSet();
		return factory;
	}
	
	@Bean
	public PlatformTransactionManager transactionManager() {
		JpaTransactionManager manager = new JpaTransactionManager();
		manager.setEntityManagerFactory(entityManagerFactory().getObject());
		return manager;
	}
	
	@Bean
	public HibernateExceptionTranslator hibernateExceptionTranslator() {
		return new HibernateExceptionTranslator();
	}
	
	private Properties hibernateProperties() {
		Properties properties = new Properties();
		properties.put(HIBERNATE_DIALECT, env.getRequiredProperty(HIBERNATE_DIALECT));
		properties.put(HIBERNATE_SHOW_SQL, env.getRequiredProperty(HIBERNATE_SHOW_SQL));
		return properties;
	}
	
}
但是这一配置是不能简单地扩展到多数据源配置的,因为Spring Boot默认会为开发人员做很多工作,而这些工作与多数据源的配置相冲突,因此需要修改原来的配置内容。网上有很多讲解多数据源配置的文章,但是这些文章大多使用的是XML配置的方式,而且没有使用Spring Boot等比较新的Spring框架技术(比如很多人使用实现AbstractRoutingDataSource这一Spring提供的抽象类的方式,还需要切面的支持,无疑是相当繁琐的),已经不适用于在最新的工程项目中使用了,因为最新的Spring框架可以为我们完成很多事情,我们只需要去适应新的方法即可。为此,我通过研究Spring的官方文档和不断调试,实现了新的多数据源配置的方法,在此贴出,仅供参考。

首先,Spring的JPA是直接支持多数据源配置的,因此我们可以在配置文件或者代码中直接配置多个数据源。由于多数据源配置可能会共享一些配置信息,因此使用继承体系实现这种配置是最合适的。在例子中,我的ORM框架使用的是Hibernate,而与Hibernate相关的配置信息我都放在了配置基类BaseJpaConfg中,代码如下。

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class BaseJpaConfig {

    private static final String HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String HIBERNATE_SHOW_SQL = "hibernate.show.sql";

    @Resource
    protected Environment env;

    @Bean
    public HibernateExceptionTranslator hibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }

    protected Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put(HIBERNATE_DIALECT, env.getRequiredProperty(HIBERNATE_DIALECT));
        properties.put(HIBERNATE_SHOW_SQL, env.getRequiredProperty(HIBERNATE_SHOW_SQL));
        return properties;
    }

}


该类提供Hibernate相关信息。它有两个子类,分别是SpringJpaConfig以及MysqlJpaConfig,分别配置了两个数据源,连接到两个不同的数据库Schema,代码如下。

@Configuration
@EnableJpaRepositories(basePackages = {"org.lyndon.repository1"}, entityManagerFactoryRef = "springEntityManagerFactory",
        transactionManagerRef = "springTransactionManager")
public class SpringJpaConfig extends BaseJpaConfig {

    private static final String DATABASE_DRIVER = "db.driver";
    private static final String DATABASE_URL = "db.url";
    private static final String DATABASE_USER = "db.user";
    private static final String DATABASE_PASSWORD = "db.password";
    private static final String PACKAGES_TO_SCAN = "packages.to.scan";

    @Bean
    @ConfigurationProperties(prefix = "datasource.primary")
    public DataSource dataSource() {
        DruidDataSource source = new DruidDataSource();
        source.setDriverClassName(env.getRequiredProperty(DATABASE_DRIVER));
        source.setUrl(env.getRequiredProperty(DATABASE_URL));
        source.setUsername(env.getRequiredProperty(DATABASE_USER));
        source.setPassword(env.getRequiredProperty(DATABASE_PASSWORD));
        return source;
    }

    @Bean(name = "springEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean springEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setDataSource(dataSource());
        factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        factory.setPackagesToScan(env.getRequiredProperty(PACKAGES_TO_SCAN).split(","));
        factory.setJpaProperties(hibernateProperties());
        factory.afterPropertiesSet();
        return factory;
    }

    @Bean(name = "springTransactionManager")
    @Primary
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager manager = new JpaTransactionManager();
        manager.setEntityManagerFactory(springEntityManagerFactory().getObject());
        return manager;
    }

}

@Configuration
@EnableJpaRepositories(basePackages = {"org.lyndon.repository2"}, entityManagerFactoryRef = "mysqlEntityManagerFactory",
        transactionManagerRef = "mysqlTransactionManager")
public class MysqlJpaConfig extends BaseJpaConfig {

    private static final String DATABASE_DRIVER = "db.driver";
    private static final String DATABASE_URL = "db.url2";
    private static final String DATABASE_USER = "db.user";
    private static final String DATABASE_PASSWORD = "db.password";
    private static final String PACKAGES_TO_SCAN = "packages.to.scan2";

    @Bean
    @ConfigurationProperties(prefix = "datasource.secondary")
    public DataSource dataSource2() {
        DruidDataSource source = new DruidDataSource();
        source.setDriverClassName(env.getRequiredProperty(DATABASE_DRIVER));
        source.setUrl(env.getRequiredProperty(DATABASE_URL));
        source.setUsername(env.getRequiredProperty(DATABASE_USER));
        source.setPassword(env.getRequiredProperty(DATABASE_PASSWORD));
        return source;
    }

    @Bean(name = "mysqlEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setDataSource(dataSource2());
        factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        factory.setPackagesToScan(env.getRequiredProperty(PACKAGES_TO_SCAN).split(","));
        factory.setJpaProperties(hibernateProperties());
        factory.afterPropertiesSet();
        return factory;
    }

    @Bean(name = "mysqlTransactionManager")
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager manager = new JpaTransactionManager();
        manager.setEntityManagerFactory(mysqlEntityManagerFactory().getObject());
        return manager;
    }

}

两份配置代码使用了各自的数据源、实体管理对象以及事务管理对象。这里要注意的有两点,首先是实体管理对象。Spring Boot框架为方便开发者,默认会寻找名为“entityManagerFactory”的Bean作为实体管理的实现,但是我们这里使用了两个实体管理对象,名称也并不是默认的“entityManagerFactory”,因此必须在各自的EnableJpaRepositories的Annotation中指明使用的实体管理对象。第二点,就是事务管理对象。Spring Boot会提供一个默认的事务管理对象的实现,但是我们在这里使用了两个不同的事务管理对象,因此我们也需要在各自的EnableJpaRepositories的Annotation中指明使用的事务管理对象。除此以外,我们还必须指定其中一个事务管理对象为主要对象(使用Primary这一Annotation),让Spring能有主次地使用相应的事务管理对象。

以上就是本文的主要内容。关于Service的配置等问题,由于和单数据源时是一样的,因此就不再赘述了。基于上述代码,我们就可以使用Spring Boot实现多数据源之间的无缝切换了,是不是很轻松?如果你不想使用代码配置的方式,也可以使用XML文件代替,配置的核心属性与上文相同,可以自己加以琢磨。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值