JDBC的高阶应用之Druid及DButils

一、Druid连接池

1、连接池

连接池可以理解为存放多个连接的集合。是为了解决建立数据库连接耗费资源和时间很多的问题,提高性能。

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

2、常见连接池技术:

  • 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"的名称,光就是说明它十分快。

3、不同连接池对比

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

4、使用Druid优化工具类

1、需要添加一个druid的数据源jar包,例如druid-1.1.9.jar
2、修改工具类加入Druid数据源,代码编写:

  • 创建druid数据源
  • 给数据源赋值
  • 建立连接
  • 关闭连接
package com.gaj.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.alibaba.druid.pool.DruidDataSource;

/**
 * 加入Druid数据连接池的工具类
 * @author Jan
 *
 */
public class DruidJDBCUtil {
	// 连接数据库的驱动类
	private static String CONN_DIRVER = "com.mysql.jdbc.Driver";
	// 连接数据库的url
	private static String CONN_URL = "jdbc:mysql://localhost:3306/jdbcdb?characterEncoding=utf-8";
	// 连接数据库的用户名
	private static String CONN_USER = "root";
	// 连接数据库的密码
	private static String CONN_PASS = "root";
	
	// 创建数据源对象
	private static DruidDataSource dataSource = new DruidDataSource();
	
	// 静态块中赋值
	static{
		dataSource.setDriverClassName(CONN_DIRVER);
		dataSource.setUrl(CONN_URL);
		dataSource.setUsername(CONN_USER);
		dataSource.setPassword(CONN_PASS);
	}
	
	/**
	 * 从连接池里拿连接
	 * @return 连接
	 */
	public static Connection getConnction(){
		Connection conn = null;
		try {
			// 之前给过连接属性了,这里就可以不用给了
			conn = dataSource.getConnection();
		} catch (SQLException e) {
			System.out.println("连接数据库的四大属性可能不正确:" + e.getMessage());
			e.printStackTrace();
		}
		
		return conn;
	}
	
	/**
	 * 资源释放
	 * @param conn 连接
	 * @param ps 预编译操作对象
	 * @param rs 结果集
	 */
	public static void closeAllConnection(Connection conn, PreparedStatement ps, ResultSet rs){
		// 先开后关原则
		if(null != rs){
			try {
				rs.close();
			} catch (SQLException e) {
				System.out.println("关闭数据结果集出错:" + e.getMessage());
				e.printStackTrace();
			}
		}
		if(null != ps){
			try {
				ps.close();
			} catch (SQLException e) {
				System.out.println("关闭数据库操作对象出错:" + e.getMessage());
				e.printStackTrace();
			}
		}
		if(null != conn){
			try {
				conn.close();
			} catch (SQLException e) {
				System.out.println("关闭数据库连接出错:" + e.getMessage());
				e.printStackTrace();
			}
		}
	}
			
}

5、使用Druid数据源后进行CURD

  • CURD功能函数:
package com.gaj.function;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.gaj.entity.Person;
import com.gaj.utils.DruidJDBCUtil;

/**
 * 优化版 
 * JDBC连接数据库的CURD操作 
 * 加入了Druid数据源连接池(只优化了JDBCUtil工具类)
 * 这里JDBC代码没多大改动
 * @author Jan
 * @version 3.0
 * 
 */
public class DruidJDBCFunction {
	// 新增
	public static int insert(Person person) throws Exception{
		// 从DruidJDBCUtil获取连接
		Connection conn = DruidJDBCUtil.getConnction();
		// 编写sql 占位符占位
		String sql = "insert into person values(null,?,?,?,?)";
		// 预编译操作数据库对象
		PreparedStatement ps = conn.prepareStatement(sql);
		// 占位符赋值
		ps.setString(1, person.getPname());
		ps.setString(2, person.getSex());
		ps.setInt(3, person.getAge());
		ps.setString(4, person.getFrom());
		// 执行sql
		int count = ps.executeUpdate();
		// 释放
		DruidJDBCUtil.closeAllConnection(conn, ps, null);
		// 返回影响行数
		return count;
	}
	
	// 删除
	public static int delete(Person person) throws Exception{
		// 获取连接
		Connection conn = DruidJDBCUtil.getConnction();
		// 编写sql
		String sql = "delete from person where id = ?";
		// 预编译 执行对象
		PreparedStatement ps = conn.prepareStatement(sql);
		// 占位符赋值
		ps.setInt(1, person.getPid());
		// 执行
		int count = ps.executeUpdate();
		// 释放
		DruidJDBCUtil.closeAllConnection(conn, ps, null);
		// 返回
		return count;
	}

	// 修改
	public static int update(Person person) throws Exception{
		// 获取连接
		Connection conn = DruidJDBCUtil.getConnction();
		// 编写sql
		String sql = "update person set name=?,sex=?,age=?,`from`=? where id=?";
		// 预编译 操作对象
		PreparedStatement ps = conn.prepareStatement(sql);
		// 占位符赋值
		ps.setString(1, person.getPname());
		ps.setString(2, person.getSex());
		ps.setInt(3, person.getAge());
		ps.setString(4, person.getFrom());
		ps.setInt(5, person.getPid());
		// 执行
		int count = ps.executeUpdate();
		// 释放
		DruidJDBCUtil.closeAllConnection(conn, ps, null);
		// 返回
		return count;
	}
		
	// 查询全部
	public static List<Person> queryAll() throws Exception{
		List<Person> list = new ArrayList<>();
		Person person = null;
		// 获取连接
		Connection conn = DruidJDBCUtil.getConnction();
		// 编写sql 数据库的字段名与类的属性不一致时,使用as进行重命名 获取得时候用的是重命名后的字段
		String sql = "select `id` pid, `name` pname, `sex`, `age`, `from` from person";
		// 预编译
		PreparedStatement ps = conn.prepareStatement(sql);
		// 执行sql
		ResultSet rs = ps.executeQuery();
		// 解析结果
		while(rs.next()){
			// 创建person对象
			person = new Person();
			// 给person对象赋值
			person.setPid(rs.getInt("pid"));
			person.setPname(rs.getString("pname"));
			person.setSex(rs.getString("sex"));
			person.setAge(rs.getInt("age"));
			person.setFrom(rs.getString("from"));
			// 将person放入list
			list.add(person);
		}
		// 关闭连接
		DruidJDBCUtil.closeAllConnection(conn, ps, rs);
		// 返回集合
		return list;
	}

	// 按ID查询
	public static Person findPersonById(Integer id) throws Exception{
		Person person = null;
		// 获取连接
		Connection conn = DruidJDBCUtil.getConnction();
		// 编写sql
		String sql = "select * from person where id = ?";
		// 预编译 操作对象
		PreparedStatement ps = conn.prepareStatement(sql);
		// 占位符赋值
		ps.setInt(1, id);
		// 执行
		ResultSet rs = ps.executeQuery();
		// 解析结果集
		if(rs.next()){
			// 创建person对象
			person = new Person();
			// 给person对象赋值
			person.setPid(rs.getInt("id"));
			person.setPname(rs.getString("name"));
			person.setSex(rs.getString("sex"));
			person.setAge(rs.getInt("age"));
			person.setFrom(rs.getString("from"));
		}
		// 释放
		DruidJDBCUtil.closeAllConnection(conn, ps, rs);
		// 返回
		return person;
	}

}

  • CURD测试类:
package com.gaj.test;

import java.util.List;

import org.junit.Test;

import com.gaj.entity.Person;
import com.gaj.function.DruidJDBCFunction;

/**
 * 测试类 测试DruidJDBCFunction的CURD操作
 * @author Jan
 *
 */
public class Test3 {
	// 新增测试
	@Test
	public void insertTest() throws Exception{
		Person person = new Person();
		person.setPname("王五");
		person.setSex("男");
		person.setAge(23);
		person.setFrom("广东省");
		
		int count = DruidJDBCFunction.insert(person);
		System.out.println(count > 0 ? "Insert Successed!" : "Insert Failed!");
	}
	
	// 删除测试
	@Test
	public void deleteTest() throws Exception{
		Person person = DruidJDBCFunction.findPersonById(10);
		if(person != null){
			int count = DruidJDBCFunction.delete(person);
			System.out.println(count > 0 ? "Delete Successed!" : "Delete Failed!");
		}else{
			System.out.println("The object is not exists!");
		}
	}
	
	// 修改测试
	@Test
	public void updateTest() throws Exception{
		Person person = DruidJDBCFunction.findPersonById(11);
		if(person != null){
			person.setPname("赵六");
			person.setSex("女");
			person.setAge(19);
			person.setFrom("湖北省");
			int count = DruidJDBCFunction.update(person);
			System.out.println(count > 0 ? "Update Successed!" : "Update Failed!");
		}else{
			System.out.println("The object is not exists!");
		}
	}
	
	// 查询全部测试
	@Test
	public void queryAll() throws Exception{
		List<Person> list = DruidJDBCFunction.queryAll();
		for (Person person : list) {
			System.out.println(person);
		}
	}
	
	// 按ID查询测试
	@Test
	public void findPersonById() throws Exception{
		Person person = DruidJDBCFunction.findPersonById(19);
		System.out.println(person);
	}
}

二、DButils工具类

DBUtils是java编程中的数据库操作实用工具,小巧简单实用,用于简化JDBC代码的开发,位于apache commons组件下的一个成员,但需要数据源支持。

1、Dbutils功能介绍

  • QueryRunner中提供对sql语句操作的API
  • ResultSetHandler接口,用于定义查询操作后,封装不同的结果集
  • DBUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法

2、QueryRunner类构造

QueryRunner核心类用户执行sql语句
QueryRunner类的构造,这里主要用到public QueryRunner(DataSource ds)需要一个数据源,例如druid就可以。

/**
 * Executes SQL queries with pluggable strategies for handling
 * <code>ResultSet</code>s.  This class is thread safe.
 *
 * @see ResultSetHandler
 */
public class QueryRunner extends AbstractQueryRunner {

    /**
     * Constructor for QueryRunner.
     */
    public QueryRunner() {
        super();
    }

    /**
     * Constructor for QueryRunner that controls the use of <code>ParameterMetaData</code>.
     *
     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
     * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
     * and if it breaks, we'll remember not to use it again.
     */
    public QueryRunner(boolean pmdKnownBroken) {
        super(pmdKnownBroken);
    }

    /**
     * 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);
    }

    /**
     * Constructor for QueryRunner that takes a <code>DataSource</code> and controls the use of <code>ParameterMetaData</code>.
     * 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.
     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
     * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
     * and if it breaks, we'll remember not to use it again.
     */
    public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
        super(ds, pmdKnownBroken);
    }
}

3、QueryRunner的主要方法

  • update:执行更新数据,用于DML操作(增删改);
    /**
     * Executes the given INSERT, UPDATE, or DELETE SQL statement.  The
     * <code>Connection</code> is retrieved from the <code>DataSource</code>
     * set in the constructor.  This <code>Connection</code> must be in
     * auto-commit mode or the update will not be saved.
     *
     * @param sql The SQL statement to execute.
     * @param params Initializes the PreparedStatement's IN (i.e. '?')
     * parameters.
     * @throws SQLException if a database access error occurs
     * @return The number of rows updated.
     */
    public int update(String sql, Object... params) throws SQLException {
        Connection conn = this.prepareConnection();

        return this.update(conn, true, sql, params);
    }
  • query:查询数据,用于DQL操作。
    /**
     * Executes the given SELECT SQL query and returns a result object.
     * The <code>Connection</code> is retrieved from the
     * <code>DataSource</code> set in the constructor.
     * @param <T> The type of object that the handler returns
     * @param sql The SQL statement to execute.
     * @param rsh The handler used to create the result object from
     * the <code>ResultSet</code>.
     * @param params Initialize the PreparedStatement's IN parameters with
     * this array.
     * @return An object generated by the handler.
     * @throws SQLException if a database access error occurs
     */
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        Connection conn = this.prepareConnection();

        return this.<T>query(conn, true, sql, rsh, params);
    }

4、ResultSetHandler

  • ResultSetHandler是数据集处理类,是一个接口。

/**
 * Implementations of this interface convert ResultSets into other objects.
 *
 * @param <T> the target type the input ResultSet will be converted to.
 */
public interface ResultSetHandler<T> {

    /**
     * Turn the <code>ResultSet</code> into an Object.
     *
     * @param rs The <code>ResultSet</code> to handle.  It has not been touched
     * before being passed to this method.
     *
     * @return An Object initialized with <code>ResultSet</code> data. It is
     * legal for implementations to return <code>null</code> if the
     * <code>ResultSet</code> contained 0 rows.
     *
     * @throws SQLException if a database access error occurs
     */
    T handle(ResultSet rs) throws SQLException;

}
  • ResultSetHandler的子类
    在这里插入图片描述
ResultSetHandler子类说明
ArrayHandler将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
ArrayListHandler将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。
BeanHandler将结果集中第一条记录封装到一个指定的javaBean中。
BeanListHandler将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中
ColumnListHandler将结果集中指定的列的字段值,封装到一个List集合中
KeyedHandler将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个Map的value,另一个Map集合的key是指定的字段的值。
MapHandler将结果集中第一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值
MapListHandler将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中。
ScalarHandler它是用于单个数据。例如select count(*) from 表操作。

5、使用DButils优化工具类

1、需要添加一个dbutils的jar包,例如commons-dbutils.jar
2、修改工具类加入Druid数据源,代码编写:

  • 创建druid数据源
  • 给数据源赋值
  • 由于DButils的QueryRunenr执行类需要一个DataSource,因此只需要返回druid的数据源即可
package com.gaj.utils;

import com.alibaba.druid.pool.DruidDataSource;

/**
 * 在加入Druid连接池的基础上增加了dbutils-1.6
 * dbutils也只能获取连接池的连接
 * @author Jan
 *
 */
public class DruidDBUtil {
	// 四大连接属性
	// Driver类
	private static final String CONN_DRIVER = "com.mysql.jdbc.Driver";
	// 数据库地址 url
	private static final String CONN_URL = "jdbc:mysql://localhost:3306/jdbcdb?characterEncoding=utf-8";
	// 用户名
	private static final String CONN_USER = "root";
	// 密码
	private static final String CONN_PASS = "root";

	// 创建数据源对象
	public static DruidDataSource dataSource = new DruidDataSource();
	
	// 静态块赋值给druid数据源
	static {
		dataSource.setDriverClassName(CONN_DRIVER);
		dataSource.setUrl(CONN_URL);
		dataSource.setUsername(CONN_USER);
		dataSource.setPassword(CONN_PASS);
	}
	
	/**
	 * DBUtils执行SQL语句需要传入一个DataSource
	 * 根据这一特性将自己的DruidJDBCUtil加以改造
	 * DBUtils有自己的连接和释放方式
	 * 这里只需要返回一个数据源就可以了
	 * 添加获取数据源的方法
	 * @return
	 */
	public static DruidDataSource getDataSource(){
		return dataSource;
	}
}

6、加入DButils后进行JDBC

  • CURD功能函数:
package com.gaj.function;

import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.gaj.entity.Person;
import com.gaj.utils.DruidDBUtil;
/**
 * 最终版 
 * JDBC连接数据库的CURD操作 
 * 在加入了Druid数据源连接池的基础上并使用了dbutils工具类
 * 不需要手动写代码连接和释放了
 * @author Jan
 * @version 4.0
 * 
 */

public class DButilsJDBCFunction {
	
	// 创建SQL执行对象
	private static QueryRunner qr = new QueryRunner(DruidDBUtil.getDataSource());
	
	// 新增
	public static int insert(Person person) throws Exception{
		// 1.编写sql语句
		String sql = "insert into person values(null,?,?,?,?)";
		// 2.占位符赋值
		Object [] params = {person.getPname(), person.getSex(), person.getAge(), person.getFrom()};
		// 3.执行sql
		int count = qr.update(sql, params);
		// 4.返回影响行数
		return count;
	}
	
	// 删除
	public static int delete(Person person) throws Exception{
		// 1.编写sql
		String sql = "delete from person where id = ?";
		// 2.占位符赋值
		Object[] params = {person.getPid()};
		// 3.执行sql
		int count = qr.update(sql, params);
		// 4.返回
		return count;
	}
	
	// 修改
	public static int update(Person person) throws Exception{
		// 1.编写sql
		String sql = "update person set name=?,sex=?,age=?,`from`=? where id=?";
		// 2.占位符赋值
		Object[] params = {person.getPname(), person.getSex(), person.getAge(), person.getFrom(), person.getPid()};
		// 3.执行sql
		int count = qr.update(sql, params);
		// 4.返回
		return count;
	}
	
	// 查询全部
	public static List<Person> queryAll() throws Exception{
		// 1.编写sql
		String sql = "select id pid, name pname, sex, age, `from` from person;";
		// 2.执行sql
		List<Person> list = qr.query(sql, new BeanListHandler<Person>(Person.class)); 
		// 3.返回
		return list;
	}
	
	// 按id查询
	public static Person findPersonById(Integer id) throws Exception{
		// 1.编写sql
		String sql = "select id pid, name pname, sex, age, `from` from person where id=?;";
		// 2.占位符赋值
		Object [] params = {id};
		// 3.执行sql
		Person person = qr.query(sql, new BeanHandler<Person>(Person.class), params);
		// 4.返回
		return person;
	}
}

知识点:BeanHandler的设置是分两步完成

  1. BeanHandler<Person>通过泛型设置了BeanHandler的类型
  2. 然后Person.class指定了这个BeanHandler应该返回Person类的对象
    Person.class是获取Person的class对象。
  • CURD测试类:
package com.gaj.test;

import java.util.List;

import org.junit.Test;

import com.gaj.entity.Person;
import com.gaj.function.DButilsJDBCFunction;

/**
 * 测试类 测试DButilsJDBCFunction的CURD操作
 * @author Jan
 *
 */
public class Test4 {
	
	// 新增
	@Test
	public void insertTest() throws Exception{
		Person person = new Person();
		person.setPname("赵闪闪");
		person.setSex("男");
		person.setAge(20);
		person.setFrom("山西省");
		int count = DButilsJDBCFunction.insert(person);
		System.out.println(count > 0 ? "Insert Successed!" : "Insert Failed!");
	}
	
	// 删除
	@Test
	public void deleteTest() throws Exception{
		Person person = DButilsJDBCFunction.findPersonById(12);
		if(person != null){
			int count = DButilsJDBCFunction.delete(person);
			System.out.println(count > 0 ? "Delete Successed!" : "Delete Failed!");
		}else{
			System.out.println("The object is not exists!");
		}
	}
	// 修改
	@Test
	public void updateTest() throws Exception{
		Person person = DButilsJDBCFunction.findPersonById(13);
		if(person != null){
			person.setPname("张亮亮");
			person.setSex("男");
			person.setAge(21);
			person.setFrom("陕西省");
			int count = DButilsJDBCFunction.update(person);
			System.out.println(count > 0 ? "Update Successed!" : "Update Failed!");
		}else{
			System.out.println("The object is not exists!");
		}
	}
	
	// 查询全部
	@Test
	public void queryAllTest() throws Exception{
		List<Person> list = DButilsJDBCFunction.queryAll();
		for (Person person : list) {
			System.out.println(person);
		}
	}
	
	// 按ID查询
	@Test
	public void findPersonByIdTest() throws Exception{
		Person person = DButilsJDBCFunction.findPersonById(11);
		System.out.println(person);
	}
}

三、分页查询

  • 分页功能函数:
package com.gaj.function;

import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import com.gaj.entity.Person;
import com.gaj.utils.DruidDBUtil;

/**
 * 分页功能需要的函数
 * @author Jan
 *
 */
public class PagingJDBCFunction {
	// 创建sql执行对象
	private static QueryRunner qr = new QueryRunner(DruidDBUtil.getDataSource());
	
	// 添加
	public static int insert(Person person) throws Exception{
		// 1.编写sql
		String sql = "insert into person values(null,?,?,?,?);";
		// 2.占位符赋值
		Object[] params = {person.getPname(), person.getSex(), person.getAge(),person.getFrom()};
		// 3.执行sql
		int count = qr.update(sql, params);
		// 4.返回
		return count;
	}
	
	// 删除
	public static int delete(String name) throws Exception{
		// 1.编写sql
		String sql = "delete from person where name like ?";
		// 2.占位符赋值
		Object [] params = {name};
		// 3.执行sql
		int count = qr.update(sql, params);
		// 4.返回
		return count;
	}
	
	
	// 分页查询
	public static List<Person> findPersonByPage(Integer startIndex, Integer pageSize) throws Exception{
		// 1.编写sql
		String sql = "select id pid, name pname, sex, age, `from` from person limit ?,?";
		// 2.占位符赋值
		Object[] params = {startIndex, pageSize};
		// 3.执行sql
		List<Person> list = qr.query(sql, new BeanListHandler<Person>(Person.class), params); 
		// 4.返回
		return list;
	}
	
	// 总条数
	public static int countPersonSum() throws Exception{
		// 1.编写sql
		String sql = "select count(1) from person;";
		// 2.执行sql
		// 错误:Long cannot be cast to Integer
//		int count = qr.query(sql, new ScalarHandler<Integer>());
		Number count = qr.query(sql, new ScalarHandler<Number>());

		// 3.返回结果
		return count.intValue();
	}

}

知识点:new ScalarHandler/(),这里如果用Integer会报错,这里可以用Long的父类Number接收,再返回的时候转换成Integer即可。

  • 分页的测试类:
package com.gaj.test;

import java.util.List;

import org.junit.Test;

import com.gaj.entity.Person;
import com.gaj.function.PagingJDBCFunction;

/**
 * 分页查询
 * @author Jan
 *
 */
public class Test5 {

	// 插入300条分页数据
	@Test
	public void InsertPagingData() throws Exception{
		int count = 0;
		Person person = null;
		for (int i = 1; i <= 300; i++) {
			person =new Person();
			person.setPname("测试数据" + i);
			person.setSex(i % 2 == 0 ?"男":"女");
			person.setAge(i % 31 + 20);
			person.setFrom("-");
			count = PagingJDBCFunction.insert(person);
			System.out.println("数据" + i + (count > 0 ? "Insert Succeed!": "Insert Failed!"));
		}
	}
	
	// 删除测试数据
	@Test
	public void deleteBeatch() throws Exception{
		int count = PagingJDBCFunction.delete("测试数据%");
		System.out.println("被删除的记录数量:" + count);
	}
	
	/**
	 * 测试分页 每页10条数据
	 * 
	 * 总条数		totalNum
	 * 总页数		totalPage
	 * 页容量		pageSize
	 * 起始位置	pageIndex:(行索引 - 1 ) * pageSize
	 * @throws Exception
	 */
	@Test
	public void PagingTest() throws Exception{

		int pageSize = 10;
		int totalNum = PagingJDBCFunction.countPersonSum();
		int totalPage = (totalNum % pageSize == 0) ? (totalNum / pageSize) : (totalNum / pageSize + 1);
		int pageIndex;
		List<Person> list = null;
		for(int i = 1; i <= totalPage; i++){
			pageIndex = (i - 1) * pageSize;
			list = PagingJDBCFunction.findPersonByPage(pageIndex, pageSize);
			System.out.println("总条数:" + totalNum + "\t总页数:" + totalPage + "\t页容量:" + pageSize + "\t当前页:" + i);
			for (Person person : list) {
				System.out.println(person);
			}
		}
	}
}

四、事务

1、事务的概念

事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败

2、一个关于事务的例子

转账功能函数:

package com.gaj.function;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

import com.gaj.utils.DruidDBUtil;

/**
 * 事务处理
 * @author Jan
 *
 */
public class TransactionJDBCFunction {
	// 创建执行对象
	private static QueryRunner qr = new QueryRunner(DruidDBUtil.getDataSource());
	
	public static void transfer(String from, String to, Double money) throws Exception{
		// 1.编写sql  转出
		String sql1 = "update account set money = money - ? where name = ?";
		// 2.占位符赋值
		Object[] params1 = {money, from};
		// 看一下是否为同一个连接
		System.out.println(DruidDBUtil.getDataSource().getConnection());
		// 3.执行sql
		int count1 = qr.update(sql1, params1);
		
		// 故意引发一个错误
//		int i = 1/0;
		
		// 4.编写sql 转入
		String sql2 = "update account set money = money + ? where name = ?";
		// 5.占位符赋值
		Object[] params2 = {money, to};
		// 看一下是否为同一个连接
		System.out.println(DruidDBUtil.getDataSource().getConnection());
		// 6.执行sql
		int count2 = qr.update(sql2, params2);
		
		// 7.判断
		if(count1 > 0 && count2 > 0){
			System.out.println("转账成功!");
		}else{
			System.out.println("转账失败!");
		}
	}

}
  • 转账问题测试类:
package com.gaj.test;

import org.junit.Test;

import com.gaj.function.TransactionJDBCFunction;
/**
 * 事务测试类
 * @author Jan
 *
 */
public class Test6 {

	@Test
	public void transferTest() throws Exception{
		// 李四 转100元给张三
//		TransactionJDBCFunction.transfer("李四", "张三", 100D);
		TransactionJDBCFunction.transfer0("李四", "张三", 100D);
	}
	
	@Test
	public void show() throws Exception{
		TransactionJDBCFunction.QueryAll();
	}
}

不使用事务如果在两个sql语句中间报错,会由于使用两个不同的连接,一个成功一个失败:
在这里插入图片描述

  • 使用事务:
Connection对象的方法名描述
conn.setAutoCommit(false)开启事务
conn.commit()提交事务
conn.rollback()回滚事务
  • 编写事务的步骤:
  1. 从数据库获取了一个数据源
  2. 将数据源的事务自动提交设置为false
  3. 每次执行SQL都用同一个Connection连接
  4. 在最后一条执行sql语句后使用conn.commit()提交,保证所有sql语句都执行成功
  5. 在catch块中使用conn.rollback()回滚
	public static void transfer0(String from, String to, Double money) throws Exception {
		// 从数据源获取一个连接
		Connection conn = DruidDBUtil.getDataSource().getConnection();
		// 设置事务的手动提交
		conn.setAutoCommit(false);
		
		try {
			// 1.编写sql  转出
			String sql1 = "update account set money = money - ? where name = ?";
			// 2.占位符赋值
			Object[] params1 = {money, from};
			// 3.执行sql
			int count1 = qr.update(conn, sql1, params1);
			
			// 故意引发一个错误
			int i = 1/0;
			
			// 4.编写sql 转入
			String sql2 = "update account set money = money + ? where name = ?";
			// 5.占位符赋值
			Object[] params2 = {money, to};
			// 6.执行sql
			int count2 = qr.update(conn, sql2, params2);
			// 没有报错 提交
			conn.commit();
			
			// 7.判断
			if(count1 > 0 && count2 > 0){
				System.out.println("转账成功!");
			}else{
				System.out.println("转账失败!");
			}
		} catch (Exception e) {
			// 报错 回滚
			conn.rollback();
			conn.close();
			e.printStackTrace();
		}
	}
	
	public static void QueryAll() throws Exception{
		String sql = "select * from account";
		qr.query(sql, new ResultSetHandler<Object>() {
			@Override
			public Object handle(ResultSet rs) throws SQLException {
				while(rs.next()){
					System.out.println(rs.getInt(1) +"\t"+ rs.getString(2) +"\t"+ rs.getDouble(3));
				}
				return null;
			}
		});
	}
package druidJDBCUtils; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class DruidJDBCUtils { //定义成员变量 private static DataSource ds; //静态代码块加载配置文件 static { try { Properties prop = new Properties(); InputStream is = DruidJDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"); prop.load(is); ds = DruidDataSourceFactory.createDataSource(prop); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取数据库连接对象 */ public static Connection getConnection() throws SQLException { return ds.getConnection(); } /** * 获取连接池方法 */ public static DataSource getDataSource(){ return ds; } /** * 关闭资源方法 * close()查询sql方法 */ public static void close(ResultSet resultSet, Statement statement, Connection connection) { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 关闭资源方法 * close()增删改sql方法 */ public static void close(Statement statement, Connection connection) {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值