Springboot JPA配置多數據源

項目中用到兩個或者多個數據源仍是挺常見的,最近也是由於作日誌分析,日誌彙總信息在一個數據庫,DBA用的數據庫又是另外一個,所以必需要配置多數據源才能實現。本文以SpringBoot的 2.0.3版本爲前提。聽說SpringBoot的 2.x版本和1.x版本的配置仍是有些不一樣的,至於1.x版本的配置請自行Google,這裏就不說明了。java

下面是個人項目目錄結構:mysql

1、首先建立兩個數據庫,分別命名爲 springbootspringboot2,在 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;
    }
}

複製代碼

這裏須要注意的一點就是,在咱們配置DataSourceBean時,添加了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;
    }
}
複製代碼

啓動服務,瀏覽器訪問:

沒問題,已是各自使用的本身對應的數據源了,大功告成。明天還要上班,洗洗睡了。。。
相關文章
相關標籤/搜索