我们在进行实际项目开发的时候连接的数据库可能不止一个可能是主从备份或者是读写分离,那么我们应该如何来进行相应的配置保证我们的应用能够识别到我们希望应用访问的数据库,我将基于原有代码进行例证说明。
在之前我发布了spring data jpa和druid数据库连接池的文章,我将在这基础上进行完善将代码修改为能够适应多数据源,详细代码地址spring
环境:spring boot + spring data jpa + druid
首先我们来看看代码结构
resources目录下
├── application.properties
├── css
│ └── style.css
├── js
│ └── jquery.min.js
├── logback.xml
└── templates
├── upload.ftl
└── upload1.ftl
我将模板文件复制了一份简单修改了一下里面的内容
下面看java下面的
├── Application.java
├── ServletInitializer.java
├── TomcatInitializer.java
├── common
│ ├── dao
│ │ └── MyBaseRepository.java
│ └── service
│ ├── BaseService.java
│ └── BaseServiceImpl.java
├── config
│ ├── SpringConfig.java
│ ├── datasource
│ │ ├── DruidDataSource1Configuration.java
│ │ ├── DruidDataSource1Properties.java
│ │ ├── DruidDataSourceConfiguration.java
│ │ └── DruidDataSourceProperties.java
│ ├── jpa
│ │ ├── RepositoryConfig.java
│ │ └── RepositoryConfig1.java
│ └── tomcat
│ └── Myp2cTomcatEmbeddedServletContainerFactory.java
├── demo
│ ├── controller
│ │ ├── FileController.java
│ │ └── UserController.java
│ ├── controller1
│ │ ├── FileController1.java
│ │ └── UserController1.java
│ ├── dao
│ │ ├── FileDao.java
│ │ └── UserDao.java
│ ├── dao1
│ │ ├── FileDao1.java
│ │ └── UserDao1.java
│ ├── entity
│ │ ├── FileEntity.java
│ │ └── UserEntity.java
│ ├── entity1
│ │ ├── FileEntity.java
│ │ └── UserEntity.java
│ ├── service
│ │ ├── FileService.java
│ │ ├── FileServiceImpl.java
│ │ ├── FileSystemStorageService.java
│ │ ├── FileSystemstorageServiceImpl.java
│ │ ├── StorageService.java
│ │ ├── StorageServiceImpl.java
│ │ ├── UserService.java
│ │ └── UserServiceImpl.java
│ └── service1
│ ├── FileService1.java
│ ├── FileServiceImpl1.java
│ ├── FileSystemStorageService1.java
│ ├── FileSystemstorageServiceImpl1.java
│ ├── StorageService1.java
│ ├── StorageServiceImpl1.java
│ ├── UserService1.java
│ └── UserServiceImpl1.java
├── exception
│ ├── GenericException.java
│ ├── ItemNotFoundException.java
│ ├── ItemSaveException.java
│ └── StorageFileNotFoundException.java
├── message
│ └── entity
└── util
├── CommonUtil.java
├── DateUtil.java
├── MD5Util.java
└── XmlUtil.java
从代码结构看得出来我主要是对config包下面对数据源配置做了修改,还有就是demo包下面我为了方便将controller、service、dao、entity都复制了一份下一步我们就来看具体的配置。
首先我们需要在application.properties文件中添加第二个数据库的配置
跟第一个数据库配置没有太多差别唯一修改的就是数据库从spring修改为spring1
重点来了我们看数据源配置
DruidDataSource1Properties.java只是对应了数据库spring1的各种参数将@ConfigurationProperties改为@ConfigurationProperties(prefix = “spring.datasource1”)
重点是这两个类
DruidDataSourceConfiguration.java
DruidDataSourceConfiguration1.java
因为在spring IOC容器中实体的平级存在的因此我们需要告诉容器谁是默认的数据源,因此我们需要在DruidDataSourceConfiguration.java中添加@Primary注解告诉spring 容器这个是我默认采用的数据源。
package com.spring.config.datasource;
@Configuration
public class DruidDataSourceConfiguration {
@Autowired
private DruidDataSourceProperties properties;
@Bean(name = "druidDataSource", initMethod = "init", destroyMethod = "close")
@Primary
@Qualifier("druidDataSource")
public DataSource dataSource() throws Exception {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(properties.getUrl());
druidDataSource.setUsername(properties.getUsername());
druidDataSource.setPassword(properties.getPassword());
druidDataSource.setDriverClassName(properties.getDriverClassName());
druidDataSource.setInitialSize(properties.getInitialSize());
druidDataSource.setMaxActive(properties.getMaxActive());
druidDataSource.setMinIdle(properties.getMinIdle());
druidDataSource.setMaxWait(properties.getMaxWait());
druidDataSource.setTimeBetweenEvictionRunsMillis(properties
.getTimeBetweenEvictionRunsMillis());
druidDataSource.setMinEvictableIdleTimeMillis(properties
.getMinEvictableIdleTimeMillis());
druidDataSource.setValidationQuery(properties.getValidationQuery());
druidDataSource.setTestWhileIdle(properties.isTestWhileIdle());
druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
druidDataSource.setTestOnReturn(properties.isTestOnReturn());
druidDataSource.setPoolPreparedStatements(properties
.isPoolPreparedStatements());
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(properties
.getMaxPoolPreparedStatementPerConnectionSize());
druidDataSource.setFilters(properties.getFilters());
try {
if (null != druidDataSource) {
druidDataSource.setFilters("wall,stat");
druidDataSource.setUseGlobalDataSourceStat(true);
Properties properties = new Properties();
properties.setProperty("decrypt", "true");
druidDataSource.setConnectProperties(properties);
druidDataSource.init();
}
} catch (Exception e) {
throw new RuntimeException(
"load datasource error, dbProperties is :", e);
}
return druidDataSource;
}
}
因为项目采用了spring data jpa我们也需要针对多数据源进行配置,对比下面两个类,具体代码我就不贴了
RepositoryConfig.java
RepositoryConfig1.java
同样的需要修改相应的bean的名称、修改需要扫描的dao和entity、同时也要加上@Primary注解
package com.spring.config.jpa;
/**
*
* @author jackycheng
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = {"com.spring.*.dao"})
public class RepositoryConfig {
@Autowired
private JpaProperties jpaProperties;
@Autowired
@Qualifier("druidDataSource")
private DataSource druidDataSource;
@Bean(name = "entityManager")
@Primary
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactory(builder).getObject().createEntityManager();
}
/**
* 指定需要扫描的实体包实现与数据库关联
* @param builder
* @return
*/
@Bean(name = "entityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(druidDataSource)
.properties(getVendorProperties(druidDataSource))
.packages("com.spring.*.entity")
.persistenceUnit("persistenceUnitSpring")
.build();
}
/**
* 通过jpaProperties指定hibernate数据库方言以及在控制台打印sql语句
* @param dataSource
* @return
*/
private Map getVendorProperties(DataSource dataSource) {
Map map = jpaProperties.getHibernateProperties(dataSource);
map.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
map.put("hibernate.show_sql", "true");
return map;
}
/**
* 创建事务管理
* @param builder
* @return
*/
@Bean(name = "transactionManager")
@Primary
PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactory(builder).getObject());
}
}
到这里算是完成了多数据源的配置了,但是我们在实际使用多数据源的时候需要指定明确的事务管理,所以我在service层做了一些配置
package com.spring.demo.service1;
import com.spring.common.service.BaseServiceImpl;
import com.spring.demo.dao1.FileDao1;
import com.spring.demo.entity1.FileEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* com.spring.service
*
* @author jacky
* @date 2017/12/23
*
* 与数据库交互实现数据库层面的文件读写
*
**/
@Service
@Transactional(readOnly = true,transactionManager = "transactionManager1")
public class FileServiceImpl1 extends BaseServiceImpl<FileEntity, String> implements FileService1 {
private FileDao1 fileDao;
@Autowired
public FileServiceImpl1(FileDao1 fileDao) {
this.fileDao = fileDao;
}
/**
* 查询所有文件
*
* @return
*/
@Override
public List findAllFile() {
return fileDao.findAllFile();
}
}
在@Transactional这里指定了由哪个数据源的事务进行管理@Transactional(readOnly = true,transactionManager = “transactionManager1”)
这样就完成了多数据源的配置和使用,详细内容可以参见代码spring