项目中用到两个或者多个数据源仍是挺常见的,最近也是由于作日志分析,日志汇总信息在一个数据库,DBA用的数据库又是另外一个,所以必需要配置多数据源才能实现。本文以SpringBoot的 2.0.3
版本为前提。听说SpringBoot的 2.x
版本和1.x
版本的配置仍是有些不一样的,至于1.x版本的配置请自行Google,这里就不说明了。java
下面是个人项目目录结构:mysql
springboot
和
springboot2
,在
springboot
中有一张
t_user
表:
create table t_user (
Id int auto_increment,
user_name varchar(20) null,
password varchar(20) null,
age int null,
gender int null,
address varchar(128) null,
constraint t_user_Id_uindex
unique (Id)
);
alter table t_user add primary key (Id);
复制代码
springboot2
中有一张
t_dept
部门表:
create table t_dept
(
id int auto_increment,
dept_name varchar(24) null,
parent_id int null,
emp_count int null,
constraint t_dept_id_uindex
unique (id)
);
alter table t_dept add primary key (id);
复制代码
2、springBoot的配置文件里加上咱们的两个数据源的连接信息。web
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/springboot?autoReconnect=true&useUnicode=true
username: root
password: spdb1234
secondary:
url: jdbc:mysql://localhost:3306/springboot2?autoReconnect=true&useUnicode=true
username: root
password: spdb1234
jpa:
database: mysql
generate-ddl: true
show-sql: true
hibernate:
ddl-auto: update
open-in-view: true //很重要
复制代码
没什么好说的,且向下看,就是咱们要添加连个数据源的配置类。spring
package com.uu.configuration;
import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.sql.SQLException;
/**
* 建立多个数据源
*/
@ServletComponentScan
@Configuration
public class DataSourceConfiguration {
private static Logger logger = LoggerFactory.getLogger(DataSourceConfiguration.class);
@Value("${spring.datasource.primary.url}")
private String dbUrl1;
@Value("${spring.datasource.primary.username}")
private String username1;
@Value("${spring.datasource.primary.password}")
private String password1;
@Value("${spring.datasource.secondary.username}")
private String username2;
@Value("${spring.datasource.secondary.password}")
private String password2;
@Value("${spring.datasource.secondary.url}")
private String dbUrl2;
@Value("com.mysql.jdbc.Driver")
private String driverClassName;
@Value("5")
private int initialSize;
@Value("5")
private int minIdle;
@Value("20")
private int maxActive;
@Value("60000")
private int maxWait;
/**
* 配置间隔多久才进行一次检测,检测须要关闭的空闲链接,单位是毫秒
*/
@Value("60000")
private int timeBetweenEvictionRunsMillis;
/**
* 配置一个链接在池中最小生存的时间,单位是毫秒
*/
@Value("300000")
private int minEvictableIdleTimeMillis;
@Value("SELECT 1 FROM DUAL")
private String validationQuery;
@Value("true")
private boolean testWhileIdle;
@Value("false")
private boolean testOnBorrow;
@Value("false")
private boolean testOnReturn;
/**
* 打开PSCache,而且指定每一个链接上PSCache的大小
*/
@Value("true")
private boolean poolPreparedStatements;
@Value("20")
private int maxPoolPreparedStatementPerConnectionSize;
/**
* 配置监控统计拦截的filters,去掉后监控界面sql没法统计,'wall'用于防火墙
*/
@Value("stat,wall,log4j")
private String filters;
/**
* 经过connectProperties属性来打开mergeSql功能;慢SQL记录
*/
@Value("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500")
private String connectionProperties;
@Primary
@Bean(name = "primaryDataSource",autowire = Autowire.BY_NAME)
public DataSource dataSource() {
return getDruidDataSource(username1, password1, dbUrl1);
}
@Bean(name = "secondDataSource",autowire = Autowire.BY_NAME)
public DataSource secondaryDataSource() {
return getDruidDataSource(username2, password2, dbUrl2);
}
private DruidDataSource getDruidDataSource(String username, String password, String url) {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(url);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
//configuration
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try {
datasource.setFilters(filters);
} catch (SQLException e) {
logger.error("druid configuration initialization filter : {0}", e);
}
datasource.setConnectionProperties(connectionProperties);
return datasource;
}
}
复制代码
这里须要注意的一点就是,在咱们配置DataSource
的Bean
时,添加了autowire = Autowire.BY_NAME
属性,由于@Autowire
默认是根据类型注入的,这样后面就能够根据Name去注入了,这样就不会致使Bean冲突。sql
3、事务管理器、EntityManager等配置类数据库
1 第一个配置类,使用第一个数据源浏览器
package com.uu.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
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.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.uu.dao.user"},//这里要指向你的repository路径
entityManagerFactoryRef = "entityManagerFactoryPrimary",
transactionManagerRef = "transactionManagerPrimary")
public class PrimaryJpaConfiguration {
@Autowired
private DataSource primaryDatasource;
@Bean(name = "primaryEntityManager")
@Primary
public EntityManager primaryEntityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(primaryDatasource)
.packages("com.uu.domain.user")//这个是指向你的domain的路径
.persistenceUnit("primaryPersistenceUnit")
.properties(getVendorProperties())
.build();
}
@Autowired
private JpaProperties jpaProperties;
private Map<String, Object> getVendorProperties() {
return jpaProperties.getHibernateProperties(new HibernateSettings());
}
@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
复制代码
2 第二个配置类,使用第二个数据源springboot
package com.uu.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
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.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.uu.dao.dept"},//这里要指向你的repository路径
entityManagerFactoryRef = "secondEntityManagerFactory",
transactionManagerRef = "secondTransactionManager")
public class SecondJpaConfiguration {
@Autowired
@Qualifier("secondDataSource")
private DataSource secondDataSource;
@Bean(name = "secondEntityManager")
@Primary
public EntityManager secondEntityManager(EntityManagerFactoryBuilder builder){
return entityManagerFactorySecond(builder).getObject().createEntityManager();
}
@Bean(name = "secondEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecond (EntityManagerFactoryBuilder builder) {
return builder
.dataSource(secondDataSource)
.packages("com.uu.domain.dept")//这个是指向你的domain的路径
.persistenceUnit("secondPersistenceUnit")
.properties(getVendorProperties())
.build();
}
@Autowired
private JpaProperties jpaProperties;
private Map<String, Object> getVendorProperties() {
return jpaProperties.getHibernateProperties(new HibernateSettings());
}
@Bean(name = "secondTransactionManager")
public PlatformTransactionManager transactionManagerSecond(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactorySecond(builder).getObject());
}
}
复制代码
这里要注意的是上边的两个添加注释的地方,路径必定要分清楚写正确,否则会让你很蛋疼,这也是个人项目目录为何先分层再分业务的缘由,固然这两个地方都支持通配符和正则的,同窗不妨本身试试。bash
还有一点说明一下,就是上边这两个配置类,在SpringBoot1.x里面的配置是稍有差别的,具体差别请自行Google吧。session
那么剩下的就很是简单了,编写咱们的CRUD代码: 首先是两个Domain类:
package com.uu.domain.user;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "t_user")
@JsonIgnoreProperties(value = {"hibernateLazyInitializer","handler"})
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "user_name")
private String userName;
@Column(name = "password")
private String password;
@Column(name = "age")
private Integer age;
@Column(name = "gender")
private Integer gender;
@Column(name = "address")
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
复制代码
Dept:
package com.uu.domain.dept;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "t_dept")
@JsonIgnoreProperties(value = {"hibernateLazyInitializer","handler"})
public class Dept implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "dept_name")
private String deptName;
@Column(name = "parent_id")
private Integer parentId;
@Column(name = "emp_count")
private int empCount;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public int getEmpCount() {
return empCount;
}
public void setEmpCount(int empCount) {
this.empCount = empCount;
}
@Override
public String toString() {
return "Dept{" +
"id=" + id +
", deptName='" + deptName + '\'' + ", parentId=" + parentId + ", empCount=" + empCount + '}'; } } 复制代码
细心的同窗必定发现我在两个Domian类上都添加了一个注解@JsonIgnoreProperties(value = {"hibernateLazyInitializer","handler"})
,这里尤为要注意一下,由于Hibernate
的session默认是在Service层有效的,在咱们Controller层返回对应的Entity的时候,Session已经关闭,就会报错。另外若是在查询的结果中,若是对应的某个字段是null
,那么也一样会报错。添加了这个注解就OK了。
Repository层:
UserRepository:
package com.uu.dao.user;
import com.uu.domain.user.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User,Integer> {
User getUserById(Integer id);
}
复制代码
DeptRepository:
package com.uu.dao.dept;
import com.uu.domain.dept.Dept;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface DeptRepository extends JpaRepository<Dept,Integer> {
Dept getDeptById(Integer id);
}
复制代码
Service层:
UserService及其实现类
package com.uu.service;
import com.uu.domain.user.User;
public interface UserService {
User selectOne(Integer id);
}
复制代码
package com.uu.service.impl;
import com.uu.dao.user.UserRepository;
import com.uu.domain.user.User;
import com.uu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public User selectOne(Integer id) {
User user = userRepository.getOne(id);
System.out.println(user.getUserName() + "," + user.getId());
return user;
}
}
复制代码
DeptService及其实现类
package com.uu.service;
import com.uu.domain.dept.Dept;
public interface DeptService {
Dept queryOneById(Integer id);
}
复制代码
package com.uu.service.impl;
import com.uu.dao.dept.DeptRepository;
import com.uu.domain.dept.Dept;
import com.uu.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptRepository deptRepository;
@Override
public Dept queryOneById(Integer id) {
Dept dept = deptRepository.getDeptById(id);
System.out.println(dept);
return dept;
}
}
复制代码
最后Controller层:
package com.uu.controller;
import com.uu.domain.user.User;
import com.uu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/user/getUser")
public User getUser(String userId){
return userService.selectOne(Integer.valueOf(userId));
}
}
复制代码
package com.uu.controller;
import com.uu.domain.dept.Dept;
import com.uu.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@RequestMapping("dept/getDept")
public Dept getOne(String id){
Dept dept = deptService.queryOneById(Integer.valueOf(id));
return dept;
}
}
复制代码
启动服务,浏览器访问: