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;
    }
}
复制代码

启动服务,浏览器访问:

没问题,已是各自使用的本身对应的数据源了,大功告成。明天还要上班,洗洗睡了。。。
相关文章
相关标签/搜索