2019-7-31 [Java_MySQL_JDBC] 数据源技术性能优化 JavaBean组件 druid数据库连接池 DBUtils[CRUD案例] 结果集处理类 分页效果

13 篇文章 0 订阅
12 篇文章 0 订阅

2.使用数据源技术提升JDBC性能

2.1 连接池原理:

理解为存放多个连接的集合
在这里插入图片描述
目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。

2.2 编写标准的数据源(规范)

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

常见的连接池技术

DBCP(DataBase Connection Pool)数据库连接池,是java数据库连接池的一种,由Apache开发,通过数据库连接池,可以让程序自动管理数据库连接的释放和断开。

Druid 是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。

Tomcat-JDBC是Spring Boot中自动配置优先级最高的连接池方案,它的出现是用来替代Apache早期的连接池产品——DBCP 1.x。

HikariCP同样是一个十分快速、简单、可靠的及十分轻量级的连接池,只有130KB,在GitHub上看到的是"光HikariCP"的名称,光就是说明它十分快。
在这里插入图片描述
性能测试
环境配置:在这里插入图片描述
1:获取关闭连接性能测试
测试说明:

打开关闭次数为: 100w次
测试用例和mysql在同一台机器上面,尽量避免io的影响

mysql性能数据 (单位:ms)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试结果:

性能表现:hikariCP>druid>tomcat-jdbc>dbcp>c3p0。
hikariCP 的性能及其优异。hikariCP号称java平台最快的数据库连接池。
hikariCP在并发较高的情况下,性能基本上没有下降。
c3p0连接池的性能很差,不建议使用该数据库连接池。

2:查询一条语句性能测试

测试说明:

查询的次数为10w次,查询的语句为 1:打开连接 2:执行 :select 1 3:关闭连接
测试用例和mysql在同一台机器上面,尽量避免io的影响

测试数据:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试结果:

在并发比较少的情况下,每个连接池的响应时间差不多。是由于并发少,基本上没有资源竞争。
在并发较高的情况下,随着并发的升高,hikariCP响应时间基本上没有变动。
c3p0随着并发的提高,性能急剧下降。

结论 : 根据几种数据源的对比 hikari 无疑性能最优秀的,但是因为是最新技术可能存在潜在的bug,所以我们要使用目前比较稳定的阿里的druid数据源;

2.3 常用的数据源配置

2.3.1 druid数据库连接池

Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
使用步骤:
1、添加jar包在这里插入图片描述
2、修改工具类:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
import com.alibaba.druid.pool.DruidDataSource;

/**
 * 数据库连接工具类
 * @author My
 *
 */
public class JdbcUtil {

	// 创建数据库的连接对象
	private static final String CONN_DRIVER = "com.mysql.jdbc.Driver";
	private static final String CONN_URL = "jdbc:mysql://127.0.0.1:3306/jdbcdb?characterEncoding=UTF-8";
	private static final String CONN_USER = "root";
	private static final String CONN_PASSWORD = "root";
	
	// 创建数据源对象
	private static DruidDataSource dataSource = new DruidDataSource();
	
	// 赋值
	static{
		dataSource.setDriverClassName(CONN_DRIVER);
		dataSource.setUrl(CONN_URL);
		dataSource.setUsername(CONN_USER);
		dataSource.setPassword(CONN_PASSWORD);
	}
		
	// 编写数据库打开方法
	public static Connection getConn(){
		System.out.println("[系统日志]开始获取数据库连接,请稍后!");
		// 创建方法的返回值
		Connection conn = null;
		try {
			conn = dataSource.getConnection();
			System.out.println("[系统日志]已获取数据库连接!" + conn);
		} catch (SQLException e) {
			System.err.println("[系统日志]你URL或者用户名密码写错了!");
			e.printStackTrace();
		}
		// 返回
		return conn;
	}
		
	// 编写释放资源方法
	public static void closeAll(Connection conn,PreparedStatement stat,ResultSet set){
		// 顺序关闭 第一个关 set 第二个关 stat  第三个关 conn
		try {
			if(null != set){
				set.close();
				System.out.println("[系统日志]对象 " + set + " 已释放!");
			}
			if(null != stat){
				stat.close();
				System.out.println("[系统日志]对象 " + stat + " 已释放!");
			}
			if(null != conn){
				conn.close();
				System.out.println("[系统日志]对象 " + conn + " 已释放!");
			}
		} catch (SQLException e) {
			System.err.println("[系统日志]关闭个连接都能报错?嗯???");
			e.printStackTrace();
		}
	}
}

druid数据源还有很多功能,我们后续会为大家介绍,现在我们只用最基本的获取数据库连接的方案;

3.2 案例相关知识

3.2.1 JavaBean组件 --> (实体Bean)

JavaBean就是一个类,在开发中常用于封装数据。具有如下特性

1.需要实现接口:java.io.Serializable ,通常偷懒省略了。
2.提供私有字段:private 类型字段名;
3.提供getter/setter方法:
4.提供无参构造

JavaBean是我们对OOP中封装特性的一种实现
实体类

/**
 * PERSON表的对应类 Person
 * @author My
 *
 */
public class Person {

	// 创建属性(与列一一对应的 类型对应 名称可以不对应)
	private Integer id;	
	private String name;
	private String sex;
	private Integer age;
	private String from;
	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;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getFrom() {
		return from;
	}
	public void setFrom(String from) {
		this.from = from;
	}
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", from=" + from + "]";
	}
}

3.3 DBUtils完成CRUD

3.3.1概述

DBUtils是java编程中的数据库操作实用工具,小巧简单实用。
DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。
Dbutils三个核心功能介绍

1.QueryRunner中提供对sql语句操作的API.
2.ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
3.DbUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法

3.3.2 QueryRunner核心类(用于执行SQL语句的类)

有参构造:
QueryRunner(DataSource ds) ,提供数据源(连接池),DBUtils底层自动维护连接connection

    /**
     * Constructor for QueryRunner that takes a <code>DataSource</code> to use.
     *
     * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
     * <code>DataSource</code>.
     *
     * @param ds The <code>DataSource</code> to retrieve connections from.
     */
    public QueryRunner(DataSource ds) {
        super(ds);
    }

主要方法:

update(String sql, Object… params) ,执行更新数据 insert update delete

query(String sql, ResultSetHandler rsh, Object… params) ,执行查询数据 select

3.3.3 ResultSetHandler结果集处理类(重要)

在这里插入图片描述

3.3.4 CURD案例

开发步骤:
1、创建项目,并导入jar包
在这里插入图片描述
2、优化JdbcUtil
因为Dbutils执行SQL语句需要传入一个DataSourc,所以我们根据这一特性将我们的JdbcUtil加以改造,得出以下优化结论

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
import com.alibaba.druid.pool.DruidDataSource;

/**
 * 数据库连接工具类
 * @author My
 *
 */
public class JdbcUtil {

	// 创建数据库的连接对象
	private static final String CONN_DRIVER = "com.mysql.jdbc.Driver";
	private static final String CONN_URL = "jdbc:mysql://127.0.0.1:3306/jdbcdb?characterEncoding=UTF-8";
	private static final String CONN_USER = "root";
	private static final String CONN_PASSWORD = "root";
		
	// 创建数据源对象
	private static DruidDataSource dataSource = new DruidDataSource();
	
	// 赋值
	static{
		dataSource.setDriverClassName(CONN_DRIVER);
		dataSource.setUrl(CONN_URL);
		dataSource.setUsername(CONN_USER);
		dataSource.setPassword(CONN_PASSWORD);
	}
	
	/**
	 * 添加获取数据源的方法
	 * @return
	 */
	public static DruidDataSource getDataSource() {
		return dataSource;
	}	
}

3、完成各功能并测试

3.3.4.1 创建SQL执行对象

在写CURD的类中先创建一个CURD共享的SQL执行对象QueryRunner

	// 创建SQL执行对象
	private QueryRunner qr = new QueryRunner(JdbcUtil.getDataSource());
3.3.4.2 添加

代码 :

	/**
	 * 添加人员信息
	 * @param person
	 * @return
	 * @throws Exception
	 */
	public int insertPerson(Person person)throws Exception{
		// 创建方法的返回值
		int count = 0;
		// 编写SQL语句
		String sql = "insert into person values (null,?,?,?,?)";
		// 占位符赋值
		Object[] params = {person.getName(),person.getSex(),person.getAge(),person.getFrom()};
		// 执行
		count = qr.update(sql,params);
		// 返回
		return count;
	}

测试:

	@Test
	public void test()throws Exception{
		// 创建要添加的数据
		Person person = new Person();
		person.setSex("女");
		person.setAge(18);
		person.setFrom("湖南省");
		person.setName("王小花");
		int count = this.insertPerson(person);
		String msg = count>0?"添加成功":"添加失败";
		System.out.println(msg);
	}

效果:在这里插入图片描述

3.3.4.3 按id查询

代码 :

	/**
	 * 根据ID查询数据
	 * @param id
	 * @return
	 * @throws Exception
	 */
	public Person findPersonByID(Integer id)throws Exception{
		// 创建方法的返回值
		Person person = null;
		// 编写SQL语句
		String sql = "select * from person where `id` = ?";
		// 占位符赋值
		Object[] params = {id};
		// 执行
		person = qr.query(sql, new BeanHandler<Person>(Person.class),params);
		// 返回
		return person;
	}

说明 :

此处我们在查询的时候应用了query(sql语句,返回值设置,sql语句的占位符);
方法编码中 new BeanHandler<Person>(Person.class) 是设置本次SQL执行返回一个BeanHandler

BeanHandler是啥呢?
BeanHandler设置后表示这条SQL返回一个JavaBean对象,
但是到底返回哪个JavaBean的对象需要自己设置
此处我们就是把BeanHandler转换为Person对象进行返回的,
对于BeanHandler的设置是分两步完成

  1. BeanHandler<Person>通过泛型设置了BeanHandler的类型
  2. 然后Person.class指定了这个BeanHandler应该返回Person类的对象
    Person.class是获取Person的class对象。

下面给你说几种获取class对象的方法

  1. 所有的引用数据类型(类-类型)的类名、基本数据类型都可以通过.class方式获取其 Class对象
    (对于基本数据类型的封装类还可以通过.TYPE 的方式获取其 Class 对象。
    但要注意,TYPE 实际上获取的封装类对应的基本类型的 Class 对象的引用,
    那么你可以判断出
    int.class==Integer.TYPE 返回 true,
    int.class==Integer.class 返回 false!)
    通过这种方式不会初始化静态域,使用.class、.TYPE 的方式获取 Class对象叫做类的字面常量;

  2. Class 的 forName(String name)传入一个类的完整类路径也可以获得 Class 对象,
    但由于使用的是字符串,必须强制转换才可以获取泛型的Class<T>的 Class对象,
    并且你必须获取这个方法可能抛出的ClassNotFoundException异常。
    这种方法可以初始化静态域。

  3. 还可通过类的对象实例下的getClass()方法来获取Class对象,即 实例名.getClass()
    测试:

@Test
public void test()throws Exception{
	Person person = this.findPersonByID(5);
	System.out.println(person);
}

效果:
在这里插入图片描述

3.3.4.4 更新

代码 :

/**
 * 更新人员信息
 * @param person
 * @return
 * @throws Exception
 */
public int updatePerson(Person person)throws Exception{
	// 创建方法的返回值
	int count = 0;
	// 编写SQL语句
	String sql = "update person set `name`=?,`sex`=?,`age`=?,`from`=? where `id`=?";
	// 占位符赋值
	Object[] params = {person.getName(),person.getSex(),person.getAge(),person.getFrom(),person.getId()};
	// 执行
	count = qr.update(sql,params);
	// 返回
	return count;
}

测试:

@Test
public void test()throws Exception{
	// 更新测试
	Person person = this.findPersonByID(5);
	person.setName("李建军");
	person.setSex("男");
	person.setAge(35);
	person.setFrom("广东省");
	int count = this.updatePerson(person);
	String msg = count>0?"更新成功":"更新失败";
	System.out.println(msg);
}

效果:
在这里插入图片描述
3.3.4.5 删除

代码 :

/**
 * 删除人员信息
 * @param person
 * @return
 * @throws Exception
 */
public int deletePerson(Person person)throws Exception{
	// 创建方法的返回值
	int count = 0;
	// 编写SQL语句
	String sql = "delete from person where `id` = ?";
	// 占位符赋值
	Object[] params = {person.getId()};
	// 执行
	count = qr.update(sql,params);
	// 返回
	return count;
}

测试:

@Test
public void test()throws Exception{
	// 更新测试
	Person person = this.findPersonByID(5);
	int count = this.deletePerson(person);
	String msg = count>0?"删除成功":"删除失败";
	System.out.println(msg);
}

效果:
在这里插入图片描述

3.3.4.6 查询所有

代码 :

/**
 * 查询所有人员信息
 * @return
 * @throws Exception
 */
public List<Person> findAllPersons()throws Exception{
	// 创建方法的返回值
	List<Person> list = null;
	// 编写SQL语句
	String sql = "select * from person";
	// 占位符赋值
	// 执行
	list = qr.query(sql, new BeanListHandler<Person>(Person.class));
	// 返回
	return list;
}

测试:

@Test
public void test()throws Exception{
	List<Person> list = this.findAllPersons();
	// 遍历
	for (Person person : list) {
		System.out.println(person);
	}
}

效果:
在这里插入图片描述
到此基本的CURD案例完成

3.3.5 分页效果实现

3.3.5.1 添加测试数据

向数据库中添加300条人员信息

	@Test
public void test()throws Exception{
	Person p = null;
	Random r = new Random();
	for (int i = 1; i <= 300; i++) {
		p = new Person();
		p.setName("普通用户"+i);
		// 随机添加男 或 女
		if(r.nextInt(2) == 0){
			p.setSex("女");
		}else{
			p.setSex("男");
		}
		p.setAge(r.nextInt(100)==0?1:r.nextInt(100));
		p.setFrom("山东省");
		System.out.println(this.insertPerson(p));
	}
}

效果:在这里插入图片描述

3.3.5.2 总记录数

代码 :

/**
 * 获取总记录数
 * @return
 * @throws Exception
 */
public int findPersonsByPageCount()throws Exception{
	// 创建方法的返回值
	int totalCount = 0;
	// 编写SQL语句
	String sql = "select count(*) from person";
	// 占位符赋值
	// 执行
	totalCount = qr.query(sql, new ScalarHandler<Integer>());		
	// 返回
	return totalCount;
}

测试

@Test
public void test()throws Exception{
	int totalCount = this.findPersonsByPageCount();
	System.out.println(totalCount);
}

效果:在这里插入图片描述
报错!报错信息提示你
Long类型的返回值不能转换为Integer
也就是说select count(*) from person 这条SQL语句 dbutils 把这个数当做Long类型处理
,所以我们需要修改下查询全部的方法

/**
 * 获取总记录数
 * @return
 * @throws Exception
 */
public int findPersonsByPageCount()throws Exception{
	// 创建方法的返回值
	int totalCount = 0;
	// 编写SQL语句
	String sql = "select count(*) from person";
	// 占位符赋值
	// 执行
	Number num = qr.query(sql, new ScalarHandler<Number>());
	// 处理返回值
	totalCount = num.intValue();
	// 返回
	return totalCount;
}

重新执行单元测试效果
在这里插入图片描述

拓展学习 java.lang.Number

java.lang.Number这个基础类,看似貌不惊人,
其实在java数字类型生态系统中很重要。上图看下他的子类家族
在这里插入图片描述

3.3.5.3 分页展示数据

代码 :

/**
 * 分页操作
 * @param start
 * @param size
 * @return
 * @throws Exception
 */
public List<Person> findPersonsByPage(Integer start,Integer size)throws Exception{
	// 创建方法的返回值
	List<Person> persons = null;
	// 编写SQL语句
	String sql = "select * from person limit ?,?";
	// 占位符赋值
	Object[] params = {start,size};
	// 执行
	persons = qr.query(sql, new BeanListHandler<Person>(Person.class),params);
	// 返回
	return persons;
}

测试:

@Test
public void test()throws Exception{
	int pageIndex = 1;	// 页码
	int pageSize = 10;	// 页容量
	// 总条数
	int totalCount = this.findPersonsByPageCount();
	// 总页数
	int totalPage = totalCount%pageSize==0?totalCount/pageSize:totalCount/pageSize+1;
	// 输出
	System.out.println("总条数"+totalCount+"条,每页展示"+pageSize+"条,一共分"+totalPage+"页,当前为第"+pageIndex+"页;");
	// 获取分页集合
	List<Person> list = this.findPersonsByPage((pageIndex-1)*pageSize, pageSize);
	// 遍历循环
	for (Person person : list) {
		System.out.println(person);
	}
}

效果:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值