zoukankan      html  css  js  c++  java
  • Spring Boot从入门到精通(五)多数据源配置实现及源码分析

    多数据源配置在项目软件中是比较常见的开发需求,Spring和Spring Boot中对此都有相应的解决方案可供大家参考。在Spring Boot中,如MyBatis、JdbcTemplate以及Jpa都可以配置多数据源。

    本文在前一篇“Spring Boot从入门到精通(四)连接MySQL数据库(附源码)”文章中项目源码的基础上,来实现Spring Boot集成MyBatis和使用JdbcTemplate两种方式配置多数据源。

    Spring Boot集成MyBatis和使用JdbcTemplate配置公共文件

    1、配置数据源application.properties文件
    假定有两个数据源来配置实现,分别对应的名字是oneDataSource和twoDataSource。
    在application.properties文件中配置数据源信息如下:

    spring.datasource.one.url=jdbc:mysql://123.57.47.154:3306/springboot1
    spring.datasource.one.username=root
    spring.datasource.one.password=wangyoodb
    spring.datasource.one.driverClassName=com.mysql.cj.jdbc.Driver
    
    spring.datasource.two.url=jdbc:mysql://123.57.47.154:3306/springboot2
    spring.datasource.two.username=root
    spring.datasource.two.password=wangyoodb
    spring.datasource.two.driverClassName=com.mysql.cj.jdbc.Driver

    2、数据源配置java类文件
    通过上一步操作,利用关键词one和two对数据源进行区分,只是简单增加这些信息是无法被Spring Boot自动加载的,需要增加代码来实现加载DataSource,下面小编手动配置DataSourceConfig,用来提供DataSource Bean。
    数据源一:新增命名DataSourceOneConfig的java类文件,具体代码如下:

    package com.yoodb.study.demo03.datasource;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import javax.sql.DataSource;
    
    @Configuration
    @MapperScan(basePackages = "com.yoodb.study.demo03.mapper.one", sqlSessionFactoryRef = "oneSqlSessionFactory")
    public class DataSourceOneConfig {
        @Bean(name = "oneDataSource")
        @Primary
        @ConfigurationProperties(prefix = "spring.datasource.one")
        public DataSource getDateSourceOne() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "oneSqlSessionFactory")
        @Primary
        public SqlSessionFactory oneSqlSessionFactory(@Qualifier("oneDataSource") DataSource datasource)
                throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(datasource);
            bean.setMapperLocations(
                    new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/one/*.xml"));
            return bean.getObject();
        }
        //
        @Bean("oneSqlSessionTemplate")
        @Primary
        public SqlSessionTemplate onesqlsessiontemplate(
                @Qualifier("oneSqlSessionFactory") SqlSessionFactory sessionfactory) {
            return new SqlSessionTemplate(sessionfactory);
        }
    }

    数据源二:新增命名DataSourceTwoConfig的java类文件,具体代码如下:

    package com.yoodb.study.demo03.datasource;
    
    import com.zaxxer.hikari.HikariDataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import javax.sql.DataSource;
    
    @Configuration
    @MapperScan(basePackages = "com.yoodb.study.demo03.mapper.two", sqlSessionFactoryRef = "twoSqlSessionFactory")
    public class DataSourceTwoConfig {
        @Bean(name = "twoDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.two")
        public DataSource getDateSourceTwo(DataSourceProperties properties) {
            return DataSourceBuilder.create(properties.getClassLoader())
                    .type(HikariDataSource.class)
                    .driverClassName(properties.determineDriverClassName())
                    .url(properties.determineUrl())
                    .username(properties.determineUsername())
                    .password(properties.determinePassword())
                    .build();
        }
        @Bean(name = "twoSqlSessionFactory")
        public SqlSessionFactory twoSqlSessionFactory(@Qualifier("twoDataSource") DataSource datasource)
                throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(datasource);
            bean.setMapperLocations(
                    new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/two/*.xml"));
            return bean.getObject();
        }
        @Bean("twoSqlSessionTemplate")
        public SqlSessionTemplate twosqlsessiontemplate(
                @Qualifier("twoSqlSessionFactory") SqlSessionFactory sessionfactory) {
            return new SqlSessionTemplate(sessionfactory);
        }
    }

    在上述代码中使用的注解含义可以参考之前写过“Spring Boot从入门到精通(三)常用注解含义及用法分析总结”的文章(见微信公众号“Java精选”),具体使用注解含义如下:

    @Configuration

    用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

    @MapperScan

    指定要扫描mapper类包的路径,配置mybatis的接口存放位置。

    @Bean

    用于告诉方法产生一个Bean对象,然后这个Bean对象交给Spring管理。

    @ConfigurationProperties

    是Spring Boot提供的类型安全的属性绑定,以第一个Bean为例,@ConfigurationProperties(prefix = “spring.datasource.one”)表示使用spring.datasource.one前缀的数据库配置去创建一个DataSource。

    @Primary

    可以理解为默认优先选择,不可以同时设置多个,必须增加此注解用于区分主数据库(默认数据库)。

    @Qualifier

    来达到注入某个特指bean的作用,表示查找Spring容器中指定名字的对象。

    注意:

    bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“***”)) mapper的xml形式文件位置必须要配置,不然项目会报no statement错误信息。

    3、实体类文件

    新增实体类文件,两个数据源公用一个实体类,具体代码如下:

    package com.yoodb.study.demo03.bean;
    
    public class BootUser {
        private String id;
        private String name;
        private String detail;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDetail() {
            return detail;
        }
    
        public void setDetail(String detail) {
            this.detail = detail;
        }
    }

    集成Mybatis

    1、新增mapper xml文件
    数据源一:在src/main/resources/mapper/one(不存在文件加新建)创建BootUserOneMapper.xml文件,具体配置信息如下:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.yoodb.study.demo03.mapper.one.BootUserOneMapper" >
        <resultMap id="BaseResultMap" type="com.yoodb.study.demo03.bean.BootUser" >
            <id column="id" property="id" jdbcType="VARCHAR" />
            <result column="user_name" property="name" jdbcType="VARCHAR" />
            <result column="detail" property="detail" jdbcType="VARCHAR" />
        </resultMap>
    
        <select id="selectAll" resultMap="BaseResultMap">
        select
             id, user_name, detail
        from boot_user order by detail asc
        </select>
    </mapper>

    数据源二:在src/main/resources/mapper/two(不存在文件加新建)创建BootUserTwoMapper.xml文件,具体配置信息如下:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.yoodb.study.demo03.mapper.two.BootUserTwoMapper" >
        <resultMap id="BaseResultMap" type="com.yoodb.study.demo03.bean.BootUser" >
            <id column="id" property="id" jdbcType="VARCHAR" />
            <result column="user_name" property="name" jdbcType="VARCHAR" />
            <result column="detail" property="detail" jdbcType="VARCHAR" />
        </resultMap>
    
        <select id="selectAll" resultMap="BaseResultMap">
        select
             id, user_name, detail
        from boot_user order by detail asc
        </select>
    </mapper>

    2、新增mapper接口类文件

    数据源一:mapper接口类文件,具体代码如下:

    package com.yoodb.study.demo03.mapper.one;
    import com.yoodb.study.demo03.bean.BootUser;
    import java.util.List;
    public interface BootUserOneMapper {
      List<BootUser> selectAll();
    }

    数据源二:mapper接口类文件,具体代码如下:

    package com.yoodb.study.demo03.mapper.two;
    import com.yoodb.study.demo03.bean.BootUser;
    import java.util.List;
    public interface BootUserTwoMapper {
      List<BootUser> selectAll();
    }

    3、创建service类文件
    新增文件名BootUserService类文件,具体代码如下:

    package com.yoodb.study.demo03.service;
    
    import com.yoodb.study.demo03.bean.BootUser;
    import com.yoodb.study.demo03.mapper.one.BootUserOneMapper;
    import com.yoodb.study.demo03.mapper.two.BootUserTwoMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service
    public class BootUserService {
        
        @Autowired
        private BootUserOneMapper onemapper;
    
        @Autowired
        private BootUserTwoMapper twomapper;
    
        public List<BootUser> getUsers(){
            List<BootUser> listone = onemapper.selectAll();
            List<BootUser> listtwo = twomapper.selectAll();
            listone.addAll(listtwo);
            return listone;
        }
    }

    4、创建controller类文件
    新增文件名BootUserController类文件,具体代码如下:

    package com.yoodb.study.demo03;
    
    import java.util.List;
    
    import com.yoodb.study.demo03.bean.BootUser;
    import com.yoodb.study.demo03.service.BootUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/myt")
    public class BootUserController {
        @Autowired
        private BootUserService service;
    
        @RequestMapping("/getUsers")
        public List<BootUser> getUsers() {
            List<BootUser> list = service.getUsers();
            return list;
        }
    
    }

    mapper的接口、xml文件及实体文件、service层、controller层创建完成后,目录如图:

    5、项目启动

    以上操作完成后,项目启动过程中控制台报错,错误信息如下:

    ### Error querying database. Cause: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "BOOT_USER" not found; SQL statement:
    select
    id, user_name, detail
    from boot_user order by detail asc [42102-200]
    ### The error may exist in file [F:projectstudyspringboot-study-demo03	argetclassesmapperoneBootUserOneMapper.xml]
    ### The error may involve com.yoodb.study.demo03.mapper.one.BootUserOneMapper.selectAll
    ### The error occurred while executing a query
    ### SQL: select id, user_name, detail from boot_user order by detail asc
    ### Cause: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "BOOT_USER" not found; SQL statement:

    问题分析:目前Spring Boot最新版本不会将application.properties文件加载到classes目录下,导致无法读取到application.properties文件信息,从而在项目访问的时候控制台报上述错误信息。
    解决方法:就是在pom.xml文件中增加如下配置信息:

    <resources>
        <resource>
            <!-- 指定resources插件处理哪个目录下的资源文件 -->
            <directory>src/main/resources</directory>
            <includes>
                <include>**/**</include>
            </includes>
        </resource>
    </resources>

    上述问题解决完后系统可以正常启动,但是在通过浏览器访问时还是会报错。

    访问地址:

    http://localhost:8080/myt/getUsers

    错误信息如下:

    ### Error querying database. Cause: java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
    ### The error may exist in file [F:projectstudyspringboot-study-demo03	argetclassesmapperoneBootUserOneMapper.xml]
    ### The error may involve com.yoodb.study.demo03.mapper.BootUserOneMapper.selectAll
    ### The error occurred while executing a query
    ### Cause: java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.] with root cause
    java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.

    问题分析:

    1)在Spring Boot 1.* 版本中配置数据源是spring.datasource.url和spring.datasource.driverClassName;

    2)在Spring Boot 2.* 版本中配置数据源是spring.datasource.jdbc-url和spring.datasource.driver-class-name;

    3)Spring Boot项目中(本文是基于Spring Boot 2.3.0.M2 版本)由于无法读取到application.properties文件中“driver-class-name”和“jdbc-url”两个参数导致项目报错。

    解决方法:
    将application.properties文件中spring.datasource..url和spring.datasource..driverClassName分别替换成spring.datasource..jdbc-url和spring.datasource..driver-class-name。

    *Spring Boot源码分析:

    1)spring.datasource.*.url替换成spring.datasource.*.jdbcUrl也没有任何问题。

    源码org.springframework.boot.jdbc包中DatabaseDriver.class文件反编译后,发现url参数必须以“jdbc”开始,具体源码如下:

    public static DatabaseDriver fromJdbcUrl(String url) {
        if (StringUtils.hasLength(url)) {
            Assert.isTrue(url.startsWith("jdbc"), "URL must start with 'jdbc'");
            String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(Locale.ENGLISH);
            DatabaseDriver[] var2 = values();
            int var3 = var2.length;
    
            for(int var4 = 0; var4 < var3; ++var4) {
                DatabaseDriver driver = var2[var4];
                Iterator var6 = driver.getUrlPrefixes().iterator();
    
                while(var6.hasNext()) {
                    String urlPrefix = (String)var6.next();
                    String prefix = ":" + urlPrefix + ":";
                    if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) {
                        return driver;
                    }
                }
            }
        }
    
        return UNKNOWN;
    }

    2)spring.datasource.*.driverClassName不替换也没有影响。

    源码org.springframework.boot.autoconfigure.jdbc包中ConfigurationProperties.class文件反编译后,发现属性即为driverClassName字段,具体源码如下:

    public String determineDriverClassName() {
        if (StringUtils.hasText(this.driverClassName)) {
            Assert.state(this.driverClassIsLoadable(), () -> {
                return "Cannot load driver class: " + this.driverClassName;
            });
            return this.driverClassName;
        } else {
        ...

    修改application.properties文件配置,参考信息如下:

    spring.datasource.one.jdbc-url=jdbc:mysql://123.57.47.154:3306/springboot1
    spring.datasource.one.username=root
    spring.datasource.one.password=wangyoodb
    spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
    
    spring.datasource.two.jdbc-url=jdbc:mysql://123.57.47.154:3306/springboot2
    spring.datasource.two.username=root
    spring.datasource.two.password=wangyoodb
    spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver

    引入话题
    之前有群里的网友问Spring Boot中jdbc-url和url有什么区别?(题外话:关注微信公众号“Java精选”,留言切换工会总好后台发送消息可以进群,群内只允许探讨技术,完全免费,帮助大家解决各位技术难题。)
    相信现在通过上述的分析,大家应该很清楚了吧,两者是因为Spring Boot升级版本调整了源码,所以“url”参数被重新命名了。
    至此集成mybatis实现多数据源的方法已完成,项目启动成功后可以正常访问,通过浏览器访问输出如下信息:

    [{"id":"1","name":"素文宅博客","detail":"欢迎关注“Java精选”微信公众号,专注程序员推送一些Java开发知识,包括基础知识、各大流行框架(Mybatis、Spring、Spring Boot等)、大数据技术(Storm、Hadoop、MapReduce、Spark等)、数据库(Mysql、Oracle、NoSQL等)、算法与数据结构、面试专题、面试技巧经验、职业规划以及优质开源项目等。"},{"id":"2","name":"素文宅博客导航","detail":"欢迎关注“Java精选”微信公众号,一部分由小编总结整理,另一部分来源于网络上优质资源,希望对大家的学习和工作有所帮助。"}]

    使用JdbcTemplate
    1、新增controller类文件
    新增文件名JdbcTemplateController类文件,具体代码如下:

    package com.yoodb.study.demo03;
    
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Set;
    
    import com.yoodb.study.demo03.bean.BootUser;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @RestController
    @RequestMapping("/jte")
    public class JdbcTemplateController {
        @Autowired
        private JdbcTemplate oneSqlSessionTemplate;
    
        @Autowired
        private JdbcTemplate twoSqlSessionTemplate;
    
        @RequestMapping("/getOneUsers")
        public List<Map<String, Object>> getOneUsers(){
            String sql = "select * from boot_user";
            List<Map<String, Object>> list =  twoSqlSessionTemplate.queryForList(sql);
            for (Map<String, Object> map : list) {
                Set<Entry<String, Object>> entries = map.entrySet( );
                if(entries != null) {
                    Iterator<Entry<String, Object>> iterator = entries.iterator( );
                    while(iterator.hasNext( )) {
                        Entry<String, Object> entry =(Entry<String, Object>) iterator.next( );
                        Object key = entry.getKey( );
                        Object value = entry.getValue();
                        System.out.println(key+":"+value);
                    }
                }
            }
            return list;
        }
    
        @RequestMapping("/getTwoUsers")
        public List<BootUser> getTwoUsers() {
            List<BootUser> list = twoSqlSessionTemplate.query("select id,user_name " +
                    "name,detail from boot_user", new BeanPropertyRowMapper<>(BootUser.class));
            return list;
        }
        
    }

    多数据源配置文件、多数据源类文件、实体类文件、controller层创建完成后,目录如图:


    上述操作完成后,因为集成mybatis时部分代码已存在(配置多数据源类文件),所以使用JdbcTemplate配置完成。

    2、项目启动

    项目启动后访问多数据源一请求地址:
    http://localhost:8080/jte/getOneUsers
    通过浏览器访问输出如下信息:

    [{"id":1,"user_name":"素文宅博客","password":"e10adc3949ba59abbe56e057f20f883e","role_name":"素文宅博客","detail":"欢迎关注“Java精选”微信公众号,专注程序员推送一些Java开发知识,包括基础知识、各大流行框架(Mybatis、Spring、Spring Boot等)、大数据技术(Storm、Hadoop、MapReduce、Spark等)、数据库(Mysql、Oracle、NoSQL等)、算法与数据结构、面试专题、面试技巧经验、职业规划以及优质开源项目等。"}]

    项目启动后访问多数据源二请求地址:
    http://localhost:8080/jte/getTwoUsers
    通过浏览器访问输出如下信息:

    [{"id":"1","name":"素文宅博客","detail":"欢迎关注“Java精选”微信公众号,专注程序员推送一些Java开发知识,包括基础知识、各大流行框架(Mybatis、Spring、Spring Boot等)、大数据技术(Storm、Hadoop、MapReduce、Spark等)、数据库(Mysql、Oracle、NoSQL等)、算法与数据结构、面试专题、面试技巧经验、职业规划以及优质开源项目等。"}]

    Spring容器中JdbcTemplate提供了两种注入方式,一种是使用@Resource注解,直接通过byName的方式注入进来,另外一种就是@Autowired注解加上@Qualifier注解,两者联合起来,实际上也是byName。

    注意:

    将JdbcTemplate注入成功后,oneSqlSessionTemplate和twoSqlSessionTemplate此时就代表操作不同的数据源,使用不同的JdbcTemplate操作不同的数据源,实现了多数据源配置。
    Spring Boot从入门到精通项目源码(多数据源配置springboot-study-demo03)地址:
    https://github.com/yoodb/springboot
    至此,Spring Boot集成MyBatis和使用JdbcTemplate两种方式多数据源配置完成,下面大家有什么问题欢迎留言评论。

  • 相关阅读:
    linux服务器安全配置攻略
    Linux服务器调优
    Tengine + Lua + GraphicsMagick 实现图片自动裁剪/缩放
    nginx缓存设置
    动态缓存技术之CSI,SSI,ESI
    ionic环境配置
    Node.js企业开发:应用场景
    HashMap实现原理、核心概念、关键问题的总结
    《Windows核心编程》读书笔记.Chapter06线程基础
    开发环境eclipse for Mac 下的常用快捷键汇总(基本参照Win系,将Ctrl换为Command)
  • 原文地址:https://www.cnblogs.com/MrYoodb/p/12383673.html
Copyright © 2011-2022 走看看