连接池
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集合了开源和商业数据库连接池的优秀特性,并结合阿里巴巴大规模苛刻生产环境的使用经验进行优化。
- 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
-
可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
-
数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。
-
SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。
-
扩展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,并指定了泛型为对应的Dept。DeptDao包含继承的方法,如:
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