項目中用到兩個或者多個數據源仍是挺常見的,最近也是由於作日誌分析,日誌彙總信息在一個數據庫,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;
}
}
複製代碼
啓動服務,瀏覽器訪問: