java 基于springboot使用ssh(spring + springmvc + hibernate)分库配置多数据源方式
今天我们来看一下当我们需要配置多数据源时,如何进行配置。我们的项目还是基于springboot以及ssh进行搭建,项目搭建结构还是使用分布式的结构来进行搭建。如果对springboot不是很了解以及对springboot如何搭建多module没有思路的同学,可以先阅读java 搭建基于springboot的ssh(spring + springmvc + hibernate)的gradle项目(基础篇)以及java 搭建基于springboot的ssh(spring + springmvc + hibernate)的gradle项目(添加多Module篇) ,因为今天的侧重点是如何添加多数据源,所以不会将过多的解释放到项目搭建上。
需求
我们将创建三个module 分别为datasource、one-service以及two-service,其中one-service以及two-service中分别对应不同的库(datasourceone库和datasourcetwo库)。最终我们将使用datasource依赖one-service以及two-service查询所需要的数据。
搭建环境
jdk:1.8
框架:springboot + spring + springmvc + hibernate
数据库 : mysql
项目管理 : gradle
最终结构图
整体结构
datasource结构
one-service结构
two-service结构
搭建过程
我们主要拿one-service进行讲解,然后只会对one-service以及two-service之间的不同进行说明。
第一步
首先因为我们采用了分布式的开发,所以我们的one-service会被打成jar包,因此springboot无法自动读取到one-service中的application.properties文件,所以我们需要添加配置让其读取到properties文件中我们所配置的数据源。
OneLoadProperties
package com.beyondli.oneservice.config;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* 启动项目时自动读取配置文件
*/
//value值为properties文件名
@PropertySource(value = "application-one.properties")
@Component
public class OneLoadProperties {
}
application-one.properties
spring.datasource.one.url=jdbc:mysql://localhost:3306/datasourceone
spring.datasource.one.username=root
spring.datasource.one.password=123456
spring.datasource.one.driverClassName=com.mysql.jdbc.Driver
第二步
配置一个dataSource,具体代码解释在代码的注释中,可有助于理解。
OneDataSourceConfig
package com.beyondli.oneservice.config;
/**
* Created by beyondLi on 2017/7/26.
*/
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 OneDataSourceConfig {
//此注解代表此数据源为主数据源,因为当多数据源时,springboot需要一个默认主数据源(当然也可以通过配置将此要求取消),并且当我们controller层依赖多service层时,必须依赖一个有主数据源注解的service,否则项目启动报错.
@Primary
@Bean(name = "oneDataSource")
//此注解表示在properties文件中的key的前缀
@ConfigurationProperties(prefix = "spring.datasource.one")
public DataSource oneDataSource() {
return DataSourceBuilder.create().build();
}
}
第三步
我们需要给EntityManager进行属性设置,从而达到我们注入此对象后可拿到对应数据源的操作对象。简单地说就是配置jpa以及事务。
OneJpaConfig
package com.beyondli.oneservice.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
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 org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.Map;
/**
* Created by beyondLi on 2017/7/26.
* 设置jpa对象
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "studentEntityManagerFactory",
transactionManagerRef = "studentTransactionManager",
basePackages = {"com.beyondli.oneservice.domain"}) //设置Repository所在位置
public class OneJpaConfig {
//注入dataSource
@Autowired
@Qualifier("oneDataSource")
private DataSource oneDataSource;
//声明是主数据源,原理同上
@Primary
//因为此bean没有设置name属性,所以名称默认为方法名
@Bean
public LocalContainerEntityManagerFactoryBean studentEntityManagerFactory(EntityManagerFactoryBuilder builder) {
//packages中设置实体类所在位置,persistencuUnit中的值个人感觉只是一个标示,名称对项目并无影响.
return builder.dataSource(oneDataSource).properties(getVendorProperties(oneDataSource)).packages("com.beyondli.oneservice.domain").persistenceUnit("onePersistenceUnit").build();
}
@Autowired
private JpaProperties jpaProperties;
private Map<String, String> getVendorProperties(DataSource dataSource) {
return jpaProperties.getHibernateProperties(dataSource);
}
//声明是主数据源,原理同上
@Primary
//因为此bean没有设置name属性,所以名称默认为方法名
@Bean
public PlatformTransactionManager studentTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(studentEntityManagerFactory(builder).getObject());
}
}
第四步
现在我们来添加事务以及注入EntityManager
StudentServiceImpl
package com.beyondli.oneservice.application.service;
import com.beyondli.oneservice.application.responsedo.StudentInfoResponseDO;
import com.beyondli.oneservice.domain.teacher.Student;
import com.beyondli.oneservice.domain.teacher.StudentRepository;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by beyondLi on 2017/7/25.
*/
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentRepository studentRepository;
/**
*
* @return
*/
@Override
//添加事务,名称为当时配置的bean的方法名
@Transactional(value = "studentTransactionManager")
public StudentInfoResponseDO getStudent() {
Student student = studentRepository.getStudent();
//1.获取mapperFactory对象
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
return mapperFactory.getMapperFacade(Student.class, StudentInfoResponseDO.class).map(student);
}
}
StudentRepositoryImpl
package com.beyondli.oneservice.infrastructure;
import com.beyondli.oneservice.domain.teacher.Student;
import com.beyondli.oneservice.domain.teacher.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import java.util.List;
/**
* Created by beyondLi on 2017/7/25.
*/
@Repository
public class StudentRepositoryImpl implements StudentRepository{
@Autowired
@Qualifier(value = "studentEntityManagerFactory")
EntityManager entityManager;
@Override
public Student getStudent() {
List<Student> resultList = entityManager.createQuery("FROM Student")
.setFirstResult(0)
.setMaxResults(1)
.getResultList();
if (resultList.size() == 0) {
return null;
}
return resultList.get(0);
}
}
one-service的数据源基本已完成,对于two-service的module中的数据源配置与one-service数据源配置相同。需要注意的是,在two-service的config文件中的所有配置文件需要将@Primary全部进行删除。因为springboot需要一个主数据源,但是也只能有一个主数据源。如果当出现多主数据源或没有主数据源项目将报错无法启动。
测试
最后我们启动项目检验一下是否达到了效果。
数据库
测试结果
好了 效果达成,我们分别从不同的数据库中取出了所需要的数据。
为防止在讲解过程中有疏漏,本人将代码的demo已上传到网上,如有需要自行下载。
http://download.csdn.net/download/liboyang71/9913785
链接:http://pan.baidu.com/s/1pLHkKrh 密码:sbgb
以上观点均属本人个人理解,如有错误或不足,望指出,共同成长。