配置多数据源
1.什么是数据源?
数据源中存储了所有建立数据库连接的信息,通过提供正确的数据源名称可以找到相应的数据库连接。
2.什么是多数据源?
就是使用多个数据源方法的多个数据库,它们是一一对应的关系。
3.为什么要使用多数据源?
减少单个数据库的压力。
4.开始配置多数据源
4.1 配置test和test2数据源
#与单数据源的区别是多了个数据源的名称 #test数据源 spring.datasource.test.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.test.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true spring.datasource.test.username=root spring.datasource.test.password=123456abc #test2数据源 spring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.test2.url=jdbc:mysql://localhost:3306/test2?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true spring.datasource.test2.username=root spring.datasource.test2.password=123456abc #版本在5及以上的要加上MySQL5Dialect而不是MySQLDialect spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
#利用hibernate根据实体类生成数据库表
spring.jpa.properties.hibernate.hbm2ddl.auto=update
4.2 配置好数据源后,新建配置类接收刚刚配置的参数,用@Primary注明主数据源
这里有两种都可以
第一种:
@Configuration public class DataSourceConfig { @Bean(name = "testDataSource") @Qualifier("testDataSource") @Primary @ConfigurationProperties(prefix="spring.datasource.test") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "test2DataSource") @Qualifier("test2DataSource") @ConfigurationProperties(prefix="spring.datasource.test2") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } }
第二种:
@Configuration public class DataSourceConfig{ //test库 @Primary @Bean(name = "testDataSourceProperties") @Qualifier("testDataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.test") public DataSourceProperties testDataSourceProperties() { return new DataSourceProperties(); } @Primary @Bean(name = "testDataSource") @Qualifier("testDataSource") @ConfigurationProperties(prefix = "spring.datasource.test") public DataSource testDataSource(@Qualifier("testDataSourceProperties") DataSourceProperties dataSourceProperties) { return dataSourceProperties.initializeDataSourceBuilder().build(); } //test2库 @Bean(name = "test2DataSourceProperties") @Qualifier("test2DataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.test2") public DataSourceProperties test2DataSourceProperties() { return new DataSourceProperties(); } @Bean(name = "test2DataSource") @Qualifier("test2DataSource") @ConfigurationProperties(prefix = "spring.datasource.test2") public DataSource test2DataSource(@Qualifier("test2DataSourceProperties") DataSourceProperties dataSourceProperties) { return dataSourceProperties.initializeDataSourceBuilder().build(); } }
4.3 对每个数据源进行详细配置
以test数据源为例:
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "testEntityManagerFactory", transactionManagerRef = "testTransactionManager",
//设置repository类所在包位置 basePackages = {"com.mrcoder.sbjpamultidb.repository"}) public class testConfig { @Autowired private HibernateProperties hibernateProperties; @Resource @Qualifier("testDataSource") private DataSource testDataSource; @Primary @Bean(name = "testEntityManager") public EntityManager entityManager(EntityManagerFactoryBuilder builder) { return testEntityManagerFactory(builder).getObject().createEntityManager(); } @Resource private JpaProperties jpaProperties; /** * 设置实体类所在位置 */ @Primary @Bean(name = "testEntityManagerFactory") public LocalContainerEntityManagerFactoryBean testEntityManagerFactory(EntityManagerFactoryBuilder builder) { Map<String, Object> properties = hibernateProperties.determineHibernateProperties( jpaProperties.getProperties(), new HibernateSettings()); return builder .dataSource(testDataSource)
//设置实体类所在包位置 .packages("com.mrcoder.sbjpamultidb.entity") .persistenceUnit("testPersistenceUnit") .properties(properties) .build(); } @Primary @Bean(name = "testTransactionManager") public PlatformTransactionManager testTransactionManager(EntityManagerFactoryBuilder builder) { return new JpaTransactionManager(testEntityManagerFactory(builder).getObject()); } }
以上两个类的位置一定要设计准确,不然会报错,找不到beean。
test2数据源是一样的,只是没有primary,把所有test改成test2就ok。
4.4 基于JPA使用多数据源
新建对应两个数据源的实体类:
@Entity @Table(name="city") public class City { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int cityId; private String cityName; private String cityIntroduce; //getter setter 构造方法 } @Entity @Table(name="house") public class House { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int houseId; private String houseName; private String houseIntroduce; //getter setter 构造方法 }
创建两个实体类对应的数据操作层:
public interface CityRepository extends JpaRepository<City,Integer> { } public interface HouseRepository extends JpaRepository<House,Integer> { }
4.5 创建controller类测试
@RestController public class TestController { @Autowired CityRepository cityRepository; @Autowired HouseRepository houseRepository; @GetMapping("/testDataSource") public String testDataSource(){ City city = new City("北京","中国首都"); cityRepository.save(city); House house = new House("豪宅","特别大的豪宅"); houseRepository.save(house); return "success"; } }
查询数据库数据已经输入进去。
基于MyBatis使用多数据源
将依赖改为mybatis,配置略有不同。
@Mapper public interface CityMapper { @Select("SELECT * FROM City") @Results({ @Result(property = "cityId", column = "city_id"), @Result(property = "cityName", column = "city_name"), @Result(property = "cityIntroduce", column = "city_introduce") }) List<City> getAllCity(); } @Mapper public interface HouseMapper { @Select("SELECT * FROM House") @Results({ @Result(property = "houseId", column = "house_id"), @Result(property = "houseName", column = "house_name"), @Result(property = "houseIntroduce", column = "house_introduce") }) List<House> getAllHouse(); }
@Configuration @MapperScan(basePackages = "com.springboot.mapper.datasource", sqlSessionTemplateRef = "sqlSessionTemplatePrimary") public class TestDataSourceConfig { @Bean(name = "sqlSessionFactoryPrimary") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); //如果使用xml写SQL的话在这里配置 //bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/datasource/*.xml")); return bean.getObject(); } @Bean(name = "transactionManagerPrimary") @Primary public DataSourceTransactionManager masterDataSourceTransactionManager(@Qualifier("testDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "sqlSessionTemplatePrimary") @Primary public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("sqlSessionFactoryPrimary") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }
写controller测试
@RestController public class TestController { @Autowired HouseMapper houseMapper; @Autowired CityMapper cityMapper; @GetMapping("/testDataSource") public Map testDataSource(){ Map map = new HashMap(); List<City> cityList=cityMapper.getAllCity(); List<House> houseList=houseMapper.getAllHouse(); map.put("cityList",cityList); map.put("houseList",houseList); return map; } }
在这个过程中也出现很多问题,总结如下:
1.
Description: Field cityRepository in com.example.demo.controller.TestController required a bean named 'entityManagerFactory' that could not be found. The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true) Action: Consider defining a bean named 'entityManagerFactory' in your configuration.
网上答案都是说删掉hibernate相关包然后update,我试了并没有用。后来发现是配置数据源时://设置repository类所在包位置 basePackages = {"com.mrcoder.sbjpamultidb.repository"}) 这个地方包的位置没配对。
2.
Description: The bean 'userRepository', defined in null, could not be registered. A bean with that name has already been defined in file [C:\MyWork\workspace_idea\springbootssm\target\classes\com\fantasy\springbootssm\mapper\UserRepository.class] and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
我把所有的实体类和repository放在一起,就报了这个错,直接在默认配置文件设置:spring.main.allow-bean-definition-overriding=true,就可以了,所以说不同种类最好分开放,养成良好习惯。