Spring DATA JPA 配置多数据源

Spring DATA JPA 配置多数据源

本文我们通过一个简单示例说明如何在spring data jpa中配置多数据源。

示例实体

首先,我们创建两个简单实体,每个独立存储在不同数据库中。
第一个是User:

package org.dataz.persistence.multiple.model.user;

@Entity
@Table(schema = "spring_jpa_user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String name;

    @Column(unique = true, nullable = false)
    private String email;

    private int age;
}

第二个是Product:

package org.dataz.persistence.multiple.model.product;

@Entity
@Table(schema = "spring_jpa_product")
public class Product {

    @Id
    private int id;

    private String name;

    private double price;
}

这连个实体被放在不同的包中,这对后续实现多数据源配置很重要。

JPA Repositories

下面看各自的 JPA Repository —— UserRepository:

package org.dataz.persistence.multiple.dao.user;

public interface UserRepository
  extends JpaRepository<User, Integer> { }

ProductRepository:

package org.dataz.persistence.multiple.dao.product;

public interface ProductRepository
  extends JpaRepository<Product, Integer> { }

同样,两个Repository在不同的独立包中。

使用javaConfig方式配置JPA

接下来进行实际Spring 配置。我们设置两个配置类,一个为User,另一个为Product:
每个配置类都需要定义下面内容:

  • User DataSource
  • User EntityManagerFactory (userEntityManager)
  • User TransactionManager (userTransactionManager)

首先开始User配置:

@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
    basePackages = "org.dataz.persistence.multiple.dao.user", 
    entityManagerFactoryRef = "userEntityManager", 
    transactionManagerRef = "userTransactionManager"
)
public class UserConfig {
    @Autowired
    private Environment env;

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean userEntityManager() {
        LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(userDataSource());
        em.setPackagesToScan(
          new String[] { "org.dataz.persistence.multiple.model.user" });

        HibernateJpaVendorAdapter vendorAdapter
          = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
          env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Primary
    @Bean
    public DataSource userDataSource() {

        DriverManagerDataSource dataSource
          = new DriverManagerDataSource();
        dataSource.setDriverClassName(
          env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("user.jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.user"));
        dataSource.setPassword(env.getProperty("jdbc.pass"));

        return dataSource;
    }

    @Primary
    @Bean
    public PlatformTransactionManager userTransactionManager() {

        JpaTransactionManager transactionManager
          = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
          userEntityManager().getObject());
        return transactionManager;
    }
}

注意我们通过@Primary注解标记userTransactionManager为首选TransactionManager ,后续无论要隐式或显式地注入事务管理器时无需指定事务名称。

下面我们讨论ProductConfig,定义内容同上:

@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
    basePackages = "org.dataz.persistence.multiple.dao.product", 
    entityManagerFactoryRef = "productEntityManager", 
    transactionManagerRef = "productTransactionManager"
)
public class ProductConfig {
    @Autowired
    private Environment env;

    @Bean
    public LocalContainerEntityManagerFactoryBean productEntityManager() {
        LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(productDataSource());
        em.setPackagesToScan(
          new String[] { "org.dataz.persistence.multiple.model.product" });

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
          env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    public DataSource productDataSource() {

        DriverManagerDataSource dataSource
          = new DriverManagerDataSource();
        dataSource.setDriverClassName(
          env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("product.jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.user"));
        dataSource.setPassword(env.getProperty("jdbc.pass"));

        return dataSource;
    }

    @Bean
    public PlatformTransactionManager productTransactionManager() {

        JpaTransactionManager transactionManager
          = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
          productEntityManager().getObject());
        return transactionManager;
    }
}

测试配置

最后,我们测试我们的配置。通过创建每个实体的实例进行简单测试,确保每个实例可以在不同的库中进行保存。测试示例如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { UserConfig.class, ProductConfig.class })
@TransactionConfiguration
public class JPAMultipleDBTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ProductRepository productRepository;

    @Test
    @Transactional("userTransactionManager")
    public void whenCreatingUser_thenCreated() {
        User user = new User();
        user.setName("John");
        user.setEmail("[email protected]");
        user.setAge(20);
        user = userRepository.save(user);

        assertNotNull(userRepository.findOne(user.getId()));
    }

    @Test
    @Transactional("userTransactionManager")
    public void whenCreatingUsersWithSameEmail_thenRollback() {
        User user1 = new User();
        user1.setName("John");
        user1.setEmail("[email protected]");
        user1.setAge(20);
        user1 = userRepository.save(user1);
        assertNotNull(userRepository.findOne(user1.getId()));

        User user2 = new User();
        user2.setName("Tom");
        user2.setEmail("[email protected]");
        user2.setAge(10);
        try {
            user2 = userRepository.save(user2);
        } catch (DataIntegrityViolationException e) {
        }

        assertNull(userRepository.findOne(user2.getId()));
    }

    @Test
    @Transactional("productTransactionManager")
    public void whenCreatingProduct_thenCreated() {
        Product product = new Product();
        product.setName("Book");
        product.setId(2);
        product.setPrice(20);
        product = productRepository.save(product);

        assertNotNull(productRepository.findOne(product.getId()));
    }
}

总结

文本通过实战方式配置spring data Jpa的多数据源实现。需要提醒注意两点:

  • 无论通过xml或javaConfig方式配置,配置信息中@EnableJpaRepositories需要一致,至少都需要指明下面三项:
@EnableJpaRepositories(
    basePackages = "org.dataz.persistence.multiple.dao.user", 
    entityManagerFactoryRef = "userEntityManager", 
    transactionManagerRef = "userTransactionManager"
)

当然还可以指定repositoryBaseClass等。
- 属性文件key
本文采用一个属性文件,定义两种数据源。如果使用两个属性文件,则key命名不能相同,避免后续覆盖前者,无法实现多数据源。

你可能感兴趣的:(spring)