Spring Boot學習筆記(十二)MyBatis Druid 多數據源 、Spring Boot 集成 Druid—— 概念與實戰

MyBatis Druid 多數據源 、Spring Boot 集成 Druid—— 概念與實戰

  • Druid介紹
    • Druid 是阿里巴巴開源平臺上的一個項目,整個項目由數據庫連接池、插件框架和 SQL 解析器組成
    • 該項目主要是爲了擴展 JDBC 的一些限制,可以讓程序員實現一些特殊的需求,比如向密鑰服務請求憑證、統計SQL 信息、SQL 性能收集、SQL 注入檢查、SQL 翻譯等,程序員可以通過定製來實現自己需要的功能。
    • Druid 不僅是一個數據庫連接池,它還包含一個 ProxyDriver,一系列內置的 JDBC 組件庫,一個 SQL Parser。在 Javad 的世界中 Druid 是目前最好的數據庫連接池,在功能、性能、擴展性上都超過其他數據庫連接池,包括 DBCP、C3P0、BoneCP、Proxool、JBoss、DataSource。
  • 用途
    • 替換其他 Java 連接池,Druid 提供了一個高效、功能強大、可擴展性好的數據庫連接池。可以監控數據庫訪問性能,Druid 內置提供了一個功能強大的 StatFilter 插件,能夠詳細統計 SQL 的執
      行性能,這對於線上分析數據庫訪問性能有幫助。
    • 數據庫密碼加密。直接把數據庫密碼寫在配置文件中,容易導致安全問題。DruidDruiver 和 DruidDataSource 都支持 PasswordCallback。
    • SQL 執行日誌,Druid 提供了不同的 LogFilter,能夠支持 Common-Logging、Log4j 和JdkLog,可以按需要選擇相應的 LogFilter,監控應⽤用的數據庫訪問情況。
    • 擴展 JDBC,如果對 JDBC 層有編程的需求,可以通過 Druid 提供的 Filter 機制,很方便編寫JDBC 層的擴展插件。
  • 初始化Spring Boot項目

    在這裏插入圖片描述

  • 解壓之後用IDEA打開 —— 項目目錄總覽(pom.xml文件未顯示)

    在這裏插入圖片描述

  • pom.xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.8.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>wen</groupId>
        <artifactId>druid</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>druid</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.2.0</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.18</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    值得一提的是:pom文件有個log4j文件包,這個必須要添加,因爲後面的druid配置文件內有個log4j的值,不加上的話會導致配置文件出錯而連不上druid

  • application.properties:
    # 主數據源,默認的
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone = GMT
    spring.datasource.username=root
    spring.datasource.password=
    spring.datasource.filters=stat,wall,log4j
    # 更多數據源
    custom.datasource.names=ds1,ds2
    custom.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
    custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone = GMT
    custom.datasource.ds1.username=root
    custom.datasource.ds1.password=
    custom.datasource.ds1.filters=stat,wall,log4j
    custom.datasource.ds2.driver-class-name=com.mysql.cj.jdbc.Driver
    custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone = GMT
    custom.datasource.ds2.username=root
    custom.datasource.ds2.password=
    spring.http.encoding.charset=utf-8
    # 下面爲連接池的補充設置,應用到上面所有數據源中
    # 初始化大小,最小,最大
    spring.datasource.initialSize=5
    spring.datasource.minIdle=5
    spring.datasource.maxActive=20
    # 配置獲取連接等待超時的時間
    spring.datasource.maxWait=60000
    # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
    spring.datasource.timeBetweenEvictionRunsMillis=60000
    # 配置一個連接在池中最小生存的時間,單位是毫秒
    spring.datasource.minEvictableIdleTimeMillis=300000
    spring.datasource.validationQuery=SELECT 1 FROM DUAL
    spring.datasource.testWhileIdle=true
    spring.datasource.testOnBorrow=false
    spring.datasource.testOnReturn=false
    # 打開PSCache,並且指定每個連接上PSCache的大小
    spring.datasource.poolPreparedStatements=true
    spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
    # 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
    # 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
    spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    

    數據庫連接的url賬戶密碼根據自己的情況作出修改

  • 數據庫設計

    數據庫設計爲一個主表,兩個從表,設定爲都包含test表
    在這裏插入圖片描述
    建表語句:

    CREATE TABLE `test` (
      `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
      `name` varchar(36) NOT NULL,
      `age` int(2) NOT NULL,
      `gender` varchar(2) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
    
  • 實體類

    entity包內的MasterUser.java 和 CustomUser.java:

    package wen.druid.entity;
    
    public class MasterUser {
    
        private Integer id;
    
        private String name;
    
        private Integer age;
    
        private String gender;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name == null ? null : name.trim();
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getGender() {
            return gender;
        }
    
        public void setGender(String gender) {
            this.gender = gender == null ? null : gender.trim();
        }
    
    }
    
    
    package wen.druid.entity;
    
    public class CustomUser {
    
        private Integer id;
    
        private String name;
    
        private Integer age;
    
        private String gender;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name == null ? null : name.trim();
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getGender() {
            return gender;
        }
    
        public void setGender(String gender) {
            this.gender = gender == null ? null : gender.trim();
        }
    
    }
    
  • 底層xml文件

    MasterUserMapper.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="wen.druid.mapper.MasterUserMapper">
    
        <resultMap id="BaseResultMap" type="wen.druid.entity.MasterUser">
            <id column="id" property="id" jdbcType="INTEGER"/>
            <result column="name" property="name" jdbcType="VARCHAR"/>
            <result column="age" property="age" jdbcType="INTEGER"/>
            <result column="gender" property="gender" jdbcType="VARCHAR"/>
        </resultMap>
    
        <sql id="BaseColumnList">
            id, name,age,gender
        </sql>
    
        <insert id="insert" parameterType="wen.druid.entity.MasterUser">
            insert into test (id, name, age, gender)
            values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
            #{age,jdbcType=INTEGER}, #{gender,jdbcType=VARCHAR})
        </insert>
    
        <insert id="insertSelective" parameterType="wen.druid.entity.MasterUser">
            insert into test
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="id != null">
                    id,
                </if>
                <if test="name != null">
                    name,
                </if>
                <if test="age != null">
                    age,
                </if>
                <if test="gender != null">
                    gender,
                </if>
            </trim>
            <trim prefix="values (" suffix=")" suffixOverrides=",">
                <if test="id != null">
                    #{id,jdbcType=INTEGER},
                </if>
                <if test="name != null">
                    #{name,jdbcType=VARCHAR},
                </if>
                <if test="age != null">
                    #{age,jdbcType=INTEGER},
                </if>
                <if test="gender != null">
                    #{gender,jdbcType=VARCHAR},
                </if>
            </trim>
        </insert>
    
        <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
            delete from test
            where id = #{id,jdbcType=INTEGER}
        </delete>
    
        <update id="updateByPrimaryKeySelective" parameterType="wen.druid.entity.MasterUser">
            update test
            <set>
                <if test="name != null">
                    name = #{name,jdbcType=VARCHAR},
                </if>
                <if test="age != null">
                    age = #{age,jdbcType=INTEGER},
                </if>
                <if test="gender != null">
                    gender = #{gender,jdbcType=VARCHAR},
                </if>
            </set>
            where id = #{id,jdbcType=INTEGER}
        </update>
    
        <update id="updateByPrimaryKey" parameterType="wen.druid.entity.MasterUser">
            update test
            set name = #{name,jdbcType=VARCHAR},
              age = #{age,jdbcType=INTEGER},
              gender = #{gender,jdbcType=VARCHAR}
            where id = #{id,jdbcType=INTEGER}
        </update>
    
        <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">
            select
            <include refid="BaseColumnList"/>
            from test
            where id = #{id,jdbcType=INTEGER}
        </select>
    
    </mapper>
    

    CustomUserMapper.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="wen.druid.mapper.CustomUserMapper">
    
        <resultMap id="BaseResultMap" type="wen.druid.entity.CustomUser">
            <id column="id" property="id" jdbcType="INTEGER"/>
            <result column="name" property="name" jdbcType="VARCHAR"/>
            <result column="age" property="age" jdbcType="INTEGER"/>
            <result column="gender" property="gender" jdbcType="VARCHAR"/>
        </resultMap>
    
        <sql id="BaseColumnList">
            id, name,age,gender
        </sql>
    
        <insert id="insert" parameterType="wen.druid.entity.CustomUser">
            insert into test
            (id, name, age, gender)
            values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
            #{age,jdbcType=INTEGER}, #{gender,jdbcType=VARCHAR})
        </insert>
    
        <insert id="insertSelective" parameterType="wen.druid.entity.CustomUser">
            insert into test
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="id != null">
                    id,
                </if>
                <if test="name != null">
                    name,
                </if>
                <if test="age != null">
                    age,
                </if>
                <if test="gender != null">
                    gender,
                </if>
            </trim>
            <trim prefix="values (" suffix=")" suffixOverrides=",">
                <if test="id != null">
                    #{id,jdbcType=INTEGER},
                </if>
                <if test="name != null">
                    #{name,jdbcType=VARCHAR},
                </if>
                <if test="age != null">
                    #{age,jdbcType=INTEGER},
                </if>
                <if test="gender != null">
                    #{gender,jdbcType=VARCHAR},
                </if>
            </trim>
        </insert>
    
        <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
            delete from test
            where id = #{id,jdbcType=INTEGER}
        </delete>
    
        <update id="updateByPrimaryKeySelective" parameterType="wen.druid.entity.CustomUser">
            update test
            <set>
                <if test="name != null">
                    name = #{name,jdbcType=VARCHAR},
                </if>
                <if test="age != null">
                    age = #{age,jdbcType=INTEGER},
                </if>
                <if test="gender != null">
                    gender = #{gender,jdbcType=VARCHAR},
                </if>
            </set>
            where id = #{id,jdbcType=INTEGER}
        </update>
    
        <update id="updateByPrimaryKey" parameterType="wen.druid.entity.CustomUser">
            update test
            set name = #{name,jdbcType=VARCHAR},
              age = #{age,jdbcType=INTEGER},
              gender = #{gender,jdbcType=VARCHAR}
            where id = #{id,jdbcType=INTEGER}
        </update>
    
        <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">
            select
            <include refid="BaseColumnList"/>
            from test
            where id = #{id,jdbcType=INTEGER}
        </select>
    
    </mapper>
    
  • mapper層
    package wen.druid.mapper;
    
    import wen.druid.entity.CustomUser;
    
    public interface CustomUserMapper {
    
        int deleteByPrimaryKey(Integer id);
    
        int insert(CustomUser customUser);
    
        int insertSelective(CustomUser customUser);
    
        CustomUser selectByPrimaryKey(Integer id);
    
        int updateByPrimaryKeySelective(CustomUser customUser);
    
        int updateByPrimaryKey(CustomUser customUser);
    
    }
    
    
    package wen.druid.mapper;
    
    import wen.druid.entity.MasterUser;
    
    public interface MasterUserMapper {
    
        int deleteByPrimaryKey(Integer id);
    
        int insert(MasterUser masterUser);
    
        int insertSelective(MasterUser masterUser);
    
        MasterUser selectByPrimaryKey(Integer id);
    
        int updateByPrimaryKeySelective(MasterUser masterUser);
    
        int updateByPrimaryKey(MasterUser masterUser);
    
    }
    
    
  • service層
    package wen.druid.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import wen.druid.entity.MasterUser;
    import wen.druid.mapper.MasterUserMapper;
    
    @Service
    public class MasterUserService {
    
        @Autowired
        private MasterUserMapper masterUserMapper;
    
        public MasterUser getMasterUser(Integer id) {
            return masterUserMapper.selectByPrimaryKey(id);
        }
    
        public int insert() {
    
            MasterUser masterUser = new MasterUser();
            masterUser.setName("your papa");
            masterUser.setAge(48);
            masterUser.setGender("M");
    
            return masterUserMapper.insert(masterUser);
        }
    }
    
    
    package wen.druid.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import wen.druid.entity.CustomUser;
    import wen.druid.mapper.CustomUserMapper;
    
    @Service
    public class CustomUserService {
    
        @Autowired
        private CustomUserMapper customUserMapper;
    
        public CustomUser getCustomUser(Integer id) {
            return customUserMapper.selectByPrimaryKey(id);
        }
    
        public int insert() {
    
            CustomUser customUser = new CustomUser();
            customUser.setName("mama");
            customUser.setAge(49);
            customUser.setGender("F");
    
            return customUserMapper.insert(customUser);
        }
    }
    
    
  • config配置層文件

    主數據源配置,需要加 @Primary:

    MasterDataSourceConfig.java

    package wen.druid.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    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 org.springframework.jdbc.datasource.DataSourceTransactionManager;
    
    import javax.sql.DataSource;
    
    @Configuration
    @MapperScan(basePackages = MasterDataSourceConfig.PACKAGES, sqlSessionFactoryRef = "masterSqlSessionFactory")
    public class MasterDataSourceConfig {
    
        static final String PACKAGES = "wen.druid.mapper";
    
        private static final String MAPPER_LOCAL = "classpath:mapper/*.xml";
    
        @Primary
        @Bean(name = "masterDataSource")
        @ConfigurationProperties("spring.datasource")
        public DruidDataSource druidDataSource() {
            return new DruidDataSource();
        }
    
    
        @Primary
        @Bean(name = "masterTransactionManager")
        public DataSourceTransactionManager masterTransactionManager() {
            return new DataSourceTransactionManager(druidDataSource());
        }
    
        @Primary
        @Bean(name = "masterSqlSessionFactory")
        public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
            final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
            sessionFactoryBean.setDataSource(dataSource);
            sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCAL));
            return sessionFactoryBean.getObject();
        }
    
    }
    

    CustomDataSourceConfig.java

    從數據源配置 不需要加@Primary:

    package wen.druid.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    
    @Configuration
    @MapperScan(basePackages = CustomDataSourceConfig.PACKAGES, sqlSessionFactoryRef = "customSqlSessionFactory")
    public class CustomDataSourceConfig {
    
        static final String PACKAGES = "wen.druid.mapper";
    
        private static final String MAPPER_LOCAL = "classpath:mapper/*.xml";
    
        @Bean(name = "customDataSource")
        @ConfigurationProperties("custom.datasource.ds1")
        public DruidDataSource druidDataSource() {
            return new DruidDataSource();
        }
    
        @Bean(name = "customTransactionManager")
        public DataSourceTransactionManager customTransactionManager() {
            return new DataSourceTransactionManager(druidDataSource());
        }
    
    
        @Bean(name = "customSqlSessionFactory")
        public SqlSessionFactory customSqlSessionFactory(@Qualifier("customDataSource") DruidDataSource dataSource) throws Exception {
            final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
            sessionFactoryBean.setDataSource(dataSource);
            sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCAL));
            return sessionFactoryBean.getObject();
        }
    
    }
    
    
  • servlet插件

    DruidStatViewServlet.java

    package wen.druid.config;
    
    import com.alibaba.druid.support.http.StatViewServlet;
    
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.annotation.WebServlet;
    
    @SuppressWarnings("serial")
    @WebServlet(urlPatterns = "/druid/*",
            initParams = {
                    @WebInitParam(name = "allow", value = "192.168.16.110,127.0.0.1"),// IP白名單 (沒有配置或者爲空,則允許所有訪問)
                    @WebInitParam(name = "deny", value = "192.168.16.111"),// IP黑名單 (存在共同時,deny優先於allow)
                    @WebInitParam(name = "loginUsername", value = "Stephanie"),// 用戶名
                    @WebInitParam(name = "loginPassword", value = "admin"),// 密碼
                    @WebInitParam(name = "resetEnable", value = "false")// 禁用HTML頁面上的“Reset All”功能
            })
    public class DruidStatViewServlet extends StatViewServlet {
    
    }
    

    上文中的druid控制端用戶名爲:Stephanie,密碼爲admin

  • 過濾器DruidStatFilter.java
    package wen.druid.config;
    
    import com.alibaba.druid.support.http.WebStatFilter;
    
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.annotation.WebInitParam;
    
    @WebFilter(filterName = "druidWebStatFilter", urlPatterns = "/*",
            initParams = {
                    @WebInitParam(name = "exclusions", value = "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*")// 忽略資源
            })
    public class DruidStatFilter extends WebStatFilter {
    
    }
    
  • 啓動類文件,需要加一個@ServletComponentScan,進行組件掃描
    package wen.druid;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    @SpringBootApplication
    @ServletComponentScan
    public class DruidApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(DruidApplication.class, args);
    	}
    
    }	
    
  • 運行調試
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章