Skip to content

连接池

00-企业级开发过程中通用后端处理

1.常见连接池介绍

HiKariCP和Druid

我们所熟知的C3P0,DBCP,Druid, HiKariCP为我们所常用的数据库连接池,

其中C3P0已经很久没有更新了。DBCP更新速度很慢,基本处于不活跃状态,而Druid和HikariCP处于活跃状态的更新中,这就是我们说的二代产品了。

2.HiKariCP连接池
2.1 特点
  • 字节码精简 :优化代码,直到编译后的字节码最少,这样,CPU缓存可以加载更多的程序代码;

  • 优化代理和拦截器 :减少代码,例如HikariCP的Statement proxy只有100行代码,只有BoneCP的十分之一;

  • 自定义数组类型(FastStatementList)代替ArrayList :避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描;

  • 自定义集合类型(ConcurrentBag :提高并发读写的效率;

  • 其他针对BoneCP缺陷的优化。

注意:HiKari在springboot2.0上默认使用无需配置

3.Druid连接池
3.1Druid 相对于其他数据库连接池的优点

Druid提供性能卓越的连接池功能外,还集成了SQL监控,黑名单拦截等功能,

强大的监控特性,通过Druid提供的监控功能,可以清楚知道连接池和SQL的工作情况。

a. 监控SQL的执行时间、ResultSet持有时间、返回行数、更新行数、错误次数、错误堆栈信息;

b. SQL执行的耗时区间分布。什么是耗时区间分布呢?比如说,某个SQL执行了1000次,其中0~1毫秒区间50次,1~10毫秒800次,10~100毫秒100次,100~1000毫秒30次,1~10秒15次,10秒以上5次。通过耗时区间分布,能够非常清楚知道SQL的执行耗时情况;

c. 监控连接池的物理连接创建和销毁次数、逻辑连接的申请和关闭次数、非空等待次数、PSCache命中率等。

方便扩展。Druid提供了Filter-Chain模式的扩展API,可以自己编写Filter拦截JDBC中的任何方法,可以在上面做任何事情,比如说性能监控、SQL审计、用户名密码加密、日志等等。

3.2 Druid集合了开源和商业数据库连接池的优秀特性,并结合阿里巴巴大规模苛刻生产环境的使用经验进行优化。
  1. 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
  2. 可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。

  3. 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。

  4. SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。

  5. 扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。

4.Spring Boot集成MyBatis实现通用Mapper
4.1 MyBatis

关于MyBatis,大部分人都很熟悉。MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

不管是DDD(Domain Driven Design,领域驱动建模)还是分层架构的风格,都会涉及到对数据库持久层的操作,在此会讲解Spring Boot集成MyBatis如何实现通用Mapper。

4.2 Spring Boot集成MyBatis

引入依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </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>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--<scope>runtime</scope>-->
            <version>5.1.32</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

4.3 通用Mapper的使用

引入依赖

<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>

4.3xx 环境配置

spring.datasource.hikari.connection-test-query=select 1
spring.datasource.hikari.minimum-idle=1
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.pool-name=poolx
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.url=jdbc:mysql://localhost:3306/myrbac?characterEncoding=utf-8
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
mybatis.configuration.map-underscore-to-camel-case=true

配置通用Mapper

在配置中,设定了指定路径下的dao,并指定了通用dao。

需要注意的是,MapperScannerConfigurer来自于tk.mybatis.spring.mapper包下。

4.3.x 配置通用mapper接口扫描器

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        mapperScannerConfigurer.setBasePackage("com.jeffrey.mapper");
        Properties properties = new Properties();
        properties.setProperty("mappers","com.jeffrey.config.BaseDao");//通用dao
        properties.setProperty("notEmpty","false");
        properties.setProperty("IDENTITY","MYSQL");

        mapperScannerConfigurer.setProperties(properties);
        return mapperScannerConfigurer;

    }
4.4 BaseDao
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;

public interface BaseDao<T> extends Mapper<T>,MySqlMapper<T>{

}

通用Mapper接口,其他接口继承该接口即可。

4.5 创建实体

我们需要添加dept表对应的实体。

package com.jeffrey.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.*;

/**
 * Created by jeffrey on 2019/11/17.
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Table(name="DEPT")
public class Dept {
    @Id
    @Column(name="deptno")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer deptno;
    private String dname;
    private String loc;

}

其中,@Table(name = "DEPT")注解指定了该实体对应的数据库表名。

配置文件

mybatis.configuration.map-underscore-to-camel-case=true
4.6 DeptDao编写
package com.jeffrey.mapper;

import com.fasterxml.jackson.databind.ser.Serializers;
import com.jeffrey.config.BaseDao;
import com.jeffrey.model.Dept;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.mybatis.spring.annotation.MapperScan;

import java.util.List;

/**
 * Created by jeffrey on 2019/11/17.
 */

public interface DeptDao extends BaseDao<Dept> {
    @Select("select * from dept")
    public List<Dept> findAll();
    @Insert("insert into dept values(#{deptno},#{dname},#{loc})")
    public int  addDept(Dept dept);
}

DeptDao继承自BaseDao,并指定了泛型为对应的DeptDeptDao包含继承的方法,如:

int deleteByPrimaryKey(Integer deptno);
int insert(Dept dept);
int insertSelective(Dept dept);
Dept selectByPrimaryKey(Integer deptno);
int updateByPrimaryKeySelective(Dept dept);
int updateByPrimaryKey(Dept dept);
。。。。
4.7 测试结果
package com.jeffrey;

import com.jeffrey.mapper.DeptDao;
import com.jeffrey.model.Dept;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class JeffreySpcMapperApplicationTests {
    @Autowired
    private DeptDao deptMapper;
    @Test
    public void contextLoads() {
        List<Dept> all = deptMapper.findAll();
        for (Dept dept: all          ) {
            log.info(dept.toString());
        }
    }
    @Test
    public void testAdd(){
        Dept dept = new Dept();
        dept.setDname("zhangsn");
        dept.setLoc("xxx");
        int i =deptMapper.addDept(dept);
        log.info(dept.toString()+" "+i);

    }

}

5. 集成PageHelper实现分页
5.1依赖包
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.5</version>
        </dependency>
5.2添加PageHelper配置类

在前文配置类中添加以下代码:

//注册mybatis分页插件PageHelper
    @Bean
    public PageHelper pageHelper(){
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("offsetAsPageNum","true");
        properties.setProperty("rowBoundsWithCount","true");
        properties.setProperty("reasonable","true");
        pageHelper.setProperties(properties);
        return pageHelper;
    }

这时就可以使用PageHelp插件了,在service中直接使用。

5.3 service层分页实现

``` package com.jeffrey.service.impl;

import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.jeffrey.mapper.DeptDao; import com.jeffrey.model.Dept; import com.jeffrey.service.DeptService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.s