JDBC学习笔记——事务、DAO、数据库连接池、DBUtils

六、事务

概念及属性

  • 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。

  • 事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务**回滚(rollback)**到最初状态。

事务的ACID属性:

  1. 原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  2. 一致性(Consistency)
    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

  3. 隔离性(Isolation)
    事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

  4. 持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

数据库的并发问题:
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:

  • 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
  • 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。
  • 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。
package jdbc_2.transaction;

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

import org.junit.Test;

import jdbc_1.myutil.JDBCUtils;

public class TransactionTest {
	/*
	 * 在user_table表中,演示AA向BB转账100元
	 * update user_table set balance = balance - 100 where user = 'AA';
	 * update user_table set balance = balance + 100 where user = 'BB';
	 */
	@Test
	public void testTransaction_1() {
		/*
		 * 1. 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
		 * 逻辑操作单元:一个或多个DML操作
		 * 
		 * 2.事务处理原则:
		 * 保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,
		 * 要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,
		 * 整个事务回滚(rollback)到最初状态。
		 * 
		 * 3. 数据一旦提交,就不可以回滚
		 * 
		 * 4.哪些情况会导致数据的自动提交?
		 * (1)DDL一旦执行,就会被自动提交
		 * (2)DML默认情况下会自动提交,可以通过set autocommit = false; 取消DML的自动提交,但该操作对DDL不起作用
		 * (3)在关闭连接时,数据会自动提交
		 */
		String sql1 = "update user_table set balance = balance - 100 where user = ?";
		String sql2 = "update user_table set balance = balance + 100 where user = ?";
		CUD(sql1, "AA");
		CUD(sql2, "BB");
	}
	
	public int CUD(String sql, Object ...obj) {
		//sql中占位符的个数应该与可变形参的长度相同
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1.获取连接
			conn = JDBCUtils.getConnection();
			
			//2.预编译sql语句,获取PreparedStatement对象	
			ps = conn.prepareStatement(sql);

			//3.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
			}
					
			//4.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}
		//5.关闭资源
		JDBCUtils.closeResource(ps, conn);
		return 0;
	}
	
	@Test
	public void testTransaction_2() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			
			//1. 关闭自动提交
			conn.setAutoCommit(false);
			
			String sql1 = "update user_table set balance = balance - 100 where user = ?";
			CUDForTransaction(conn, sql1, "AA");
			
			String sql2 = "update user_table set balance = balance + 100 where user = ?";
			CUDForTransaction(conn, sql2, "BB");
			
			//2. 提交数据
			conn.commit();
			
		} catch (Exception e) {
			e.printStackTrace();
			//3. 如果发生异常,则回滚
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		} finally {
			try {
				//4. 修改回原状态(主要使用在数据库连接池中)
				conn.setAutoCommit(true);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			JDBCUtils.closeResource(null, conn);
		}
	}
	
	/*
	 * 考虑数据库事务的CUD操作
	 */
	public int CUDForTransaction(Connection conn, String sql, Object ...obj) {
		//sql中占位符的个数应该与可变形参的长度相同
		PreparedStatement ps = null;
		try {
			//1.预编译sql语句,获取PreparedStatement对象	
			ps = conn.prepareStatement(sql);

			//2.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
			}
					
			//3.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}
		//4.关闭资源
		JDBCUtils.closeResource(ps, null);//注意:不关闭连接
		return 0;
	}
	
}

四种隔离级别

数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。

在这里插入图片描述

package jdbc_2.transaction;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

import org.junit.Test;

import jdbc_1.bean.User;
import jdbc_1.myutil.JDBCUtils;

/**
 * 演示事务的隔离级别
 * @author MCC
 *
 */
public class IsolationTest {
	@Test
	public void testIsolationSelect() throws Exception {
		Connection conn = JDBCUtils.getConnection();
		//获取当前连接的隔离级别
		int isolation = conn.getTransactionIsolation();//四种隔离级别分别对应1, 2, 4, 8
		//设置数据库的隔离级别
		conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
		System.out.println(isolation);
		//取消自动提交
		conn.setAutoCommit(false);
		String sql = "select user, password, balance from user_table where user = ?";
		User user = generalOneSelectForTx(conn, User.class, sql, "CC");
		System.out.println(user);
	}
	
	@Test
	public void testIsolationUpdate() throws Exception {
		Connection conn = JDBCUtils.getConnection();
		//取消自动提交
		conn.setAutoCommit(false);
		String sql = "update user_table set balance = ? where user = ?";
		CUDForTransaction(conn, sql, 5000, "CC");
		
		//睡眠20s之后再提交数据
		Thread.sleep(20000);
		
		conn.commit();
	}
	
	//添加事务的通用查询操作
	public <T> T generalOneSelectForTx(Connection conn, Class<T> clazz, String sql, Object ...obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			//1.建立连接
//			conn = JDBCUtils.getConnection();
			
			//2.预编译sql语句
			ps = conn.prepareStatement(sql);
			
			//3.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);
			}
			
			//4.执行sql,获取结果集对象以及结果集元数据对象
			rs = ps.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			if(rs.next()) {//如果有数据,则取出数据
				T t = clazz.getConstructor().newInstance();
				//根据数据的字段数,判断取出次数
				int columnCount = rsmd.getColumnCount();
				for(int i=0; i<columnCount; i++) {
					//取出数值以及列名
					/*
					 * 获取列的列名:rsmd.getColumnName()——不推荐使用
					 * 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
					 */
					Object columnValue = rs.getObject(i+1);
					String columnLabel = rsmd.getColumnLabel(i+1);
					//通过反射,将该值赋值给t对象的对应属性
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columnValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(rs, ps, null);
		}
		return null;	
	}
	
	//考虑事务的CUD
	public int CUDForTransaction(Connection conn, String sql, Object ...obj) {
		//sql中占位符的个数应该与可变形参的长度相同
		PreparedStatement ps = null;
		try {
			//1.预编译sql语句,获取PreparedStatement对象	
			ps = conn.prepareStatement(sql);

			//2.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
			}
					
			//3.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}
		//4.关闭资源
		JDBCUtils.closeResource(ps, null);//注意:不关闭连接
		return 0;
	}
	
}

七、DAO

  1. 将每个表封装成一个类,给出每个类的规范(即接口)
  2. 定义BaseDAO类,提供所有基本功能
  3. 定义Implement类,继承BaseDAO类并实现各自规范,即为一个DAO

版本1

BaseDAO类

package jdbc_2.DAO;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import jdbc_1.myutil.JDBCUtils;

/**
 * 封装了关于数据表的通用操作
 * DAO:Data(base) Access Object
 * @author MCC
 *
 */
public abstract class BaseDAO {
	
	//添加事务的CUD
	public int CUDForTx(Connection conn, String sql, Object ...obj) {
		//sql中占位符的个数应该与可变形参的长度相同
		PreparedStatement ps = null;
		try {
			//1.预编译sql语句,获取PreparedStatement对象	
			ps = conn.prepareStatement(sql);

			//2.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
			}
					
			//3.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}
		//4.关闭资源
		JDBCUtils.closeResource(ps, null);//注意:不关闭连接
		return 0;
	}
	
	//添加事务的通用查询操作
	public <T> T generalOneSelectForTx(Connection conn, Class<T> clazz, String sql, Object ...obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			//1.建立连接
//				conn = JDBCUtils.getConnection();
			
			//2.预编译sql语句
			ps = conn.prepareStatement(sql);
			
			//3.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);
			}
			
			//4.执行sql,获取结果集对象以及结果集元数据对象
			rs = ps.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			if(rs.next()) {//如果有数据,则取出数据
				T t = clazz.getConstructor().newInstance();
				//根据数据的字段数,判断取出次数
				int columnCount = rsmd.getColumnCount();
				for(int i=0; i<columnCount; i++) {
					//取出数值以及列名
					/*
					 * 获取列的列名:rsmd.getColumnName()——不推荐使用
					 * 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
					 */
					Object columnValue = rs.getObject(i+1);
					String columnLabel = rsmd.getColumnLabel(i+1);
					//通过反射,将该值赋值给t对象的对应属性
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columnValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(rs, ps, null);
		}
		return null;	
	}
		
	//添加事务的查询
	public <T> List<T> generalMoreSelectForTx(Connection conn, Class<T> clazz, String sql, Object ...obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
//			//1.建立连接
//			conn = JDBCUtils.getConnection();
			
			//2.预编译sql语句
			ps = conn.prepareStatement(sql);
			
			//3.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);
			}
			
			//4.执行sql,获取结果集对象以及结果集元数据对象
			rs = ps.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			
			//创建集合对象
			List<T> list = new ArrayList<T>();
			
			while(rs.next()) {//如果有数据,则取出数据
				T t = clazz.getConstructor().newInstance();
				//根据数据的字段数,判断取出次数
				int columnCount = rsmd.getColumnCount();
				//给t对象指定属性赋值
				for(int i=0; i<columnCount; i++) {
					//取出数值以及列名
					/*
					 * 获取列的列名:rsmd.getColumnName()——不推荐使用
					 * 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
					 */
					Object columnValue = rs.getObject(i+1);
					String columnLabel = rsmd.getColumnLabel(i+1);
					//通过反射,将该值赋值给t对象的对应属性
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columnValue);
				}
				//将t对象添加到集合中
				list.add(t);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(rs, ps, null);
		}
		return null;	
	}
	
	//针对于分组函数的查询操作
	public <T> T getGroupValues(Connection conn, String sql, Object ...obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = conn.prepareStatement(sql);
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);
			}
			rs = ps.executeQuery();
			if(rs.next()) {
				Object value = rs.getObject(1);
				return (T)value;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(rs, ps, null);
		}
		return null;
	}
}

CustomerDAO接口

package jdbc_2.DAO;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

import jdbc_1.bean.Customer;

/**
 * 此接口用于规范针对于customers表的CRUD操作
 * @author MCC
 *
 */
public interface CustomerDAO {
	/**
	 * 将cust对象添加到数据库中
	 * @param conn
	 * @param cust
	 */
	void insert(Connection conn, Customer cust);
	/**
	 * 根据指定的ID删除表中的记录
	 * @param conn
	 * @param id
	 */
	void deleteByID(Connection conn, int id);
	/**
	 * 根据cust对象的id修改数据库中对应id的信息为cust的信息
	 * @param conn
	 * @param cust
	 */
	void updateByID(Connection conn, Customer cust);
	/**
	 * 根据指定的id查询对应的Customer对象
	 * @param conn
	 * @param id
	 */
	Customer getCustomerByID(Connection conn, int id);
	/**
	 * 查询满足条件的所有Customer对象
	 * @param conn
	 * @return
	 */
	List<Customer> getAllCustomerByID(Connection conn);
	/**
	 * 获取表中记录的总数
	 * @param conn
	 * @return
	 */
	Long getCount(Connection conn);
	/**
	 * 获取表中的最大生日
	 * @param conn
	 * @return
	 */
	Date getMaxBirth(Connection conn);
	
}

CustomerDAOImpl类

package jdbc_2.DAO;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

import jdbc_1.bean.Customer;

public class CustomerDAOImpl extends BaseDAO implements CustomerDAO {

	@Override
	public void insert(Connection conn, Customer cust) {
		String sql = "insert into customers (name, email, birth) values (?, ?, ?)";
		CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth());
	}

	@Override
	public void deleteByID(Connection conn, int id) {
		String sql = "delete from customers where id = ?";
		CUDForTx(conn, sql, id);
	}

	@Override
	public void updateByID(Connection conn, Customer cust) {
		String sql = "update customers set name = ?, email = ?, birth = ? where id = ? ";
		CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth(), cust.getId());
	}

	@Override
	public Customer getCustomerByID(Connection conn, int id) {
		String sql = "select id, name, email, birth from customers where id = ?";
		Customer customer = generalOneSelectForTx(conn, Customer.class, sql, id);
		return customer;
	}

	@Override
	public List<Customer> getAllCustomerByID(Connection conn) {
		String sql = "select id, name, email, birth from customers";
		List<Customer> list = generalMoreSelectForTx(conn, Customer.class, sql);
		return list;
	}

	@Override
	public Long getCount(Connection conn) {
		String sql = "select count(*) from customers";
		Object values = getGroupValues(conn, sql);
		return (Long)values;
	}

	@Override
	public Date getMaxBirth(Connection conn) {
		String sql = "select max(birth) from customers";
		Object values = getGroupValues(conn, sql);
		return (Date)values;
	}
	
}

测试类

package jdbc_2.DAO.junit;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

import org.junit.Test;

import jdbc_1.bean.Customer;
import jdbc_1.myutil.JDBCUtils;
/**
 * 测试CustomerDAOImplTest
 */
import jdbc_2.DAO.CustomerDAOImpl;

public class CustomerDAOImplTest {
	//每个方法都要用,定义为成员属性
	private CustomerDAOImpl dao = new CustomerDAOImpl();

	@Test
	public void testInsert() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Customer customer = new Customer(0, "宇智波鼬", "yuzhiboyou@126.com", new Date(19930607));
			dao.insert(conn, customer);
			System.out.println("添加成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testDeleteByID() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			dao.deleteByID(conn, 13);
			System.out.println("删除成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testUpdateByID() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Customer customer = new Customer(18, "贝多芬", "beiduofen@126.com", new Date(19930607L));
			dao.updateByID(conn, customer);
			System.out.println("修改成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetCustomerByID() {
		Connection conn = null;
		try {
			//单独演示使用c3p0获取链接
			conn = jdbc_2.myutil.JDBCUtils.getConnection_c3p0();
			Customer customer = dao.getCustomerByID(conn, 19);
			System.out.println(customer);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetAllCustomerByID() {
		Connection conn = null;
		try {
			conn = jdbc_2.myutil.JDBCUtils.getConnection_DBCP();
			List<Customer> list = dao.getAllCustomerByID(conn);
			list.forEach(System.out::println);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetCount() {
		Connection conn = null;
		try {
			conn = jdbc_2.myutil.JDBCUtils.getConnection_druid();
			Long count = dao.getCount(conn);
			System.out.println(count);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetMaxBirth() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Date maxBirth = dao.getMaxBirth(conn);
			System.out.println(maxBirth);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

}

版本2

BaseDAO类

package jdbc_2.DAO_pro;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import jdbc_1.myutil.JDBCUtils;

/**
 * 封装了关于数据表的通用操作
 * DAO:Data(base) Access Object
 * @author MCC
 *
 */
public abstract class BaseDAO<T> {
	private Class<T> clazz = null;
	/*
	 * 所有继承BaseDAO的子类在使用BaseDAO中的方法时,都会先执行代码块,
	 * 代码块中的操作是获取子类所继承的父类的泛型参数,之后将该参数(实际上是一个类)赋值给clazz,
	 * 而clazz被声明成成员属性,因此可以被每个成员方法调用,这样就不需要在函数中传入Class<T> clazz参数了。
	 * 例如CustomerDAOImpl extends BaseDAO implements CustomerDAO,因为父类BaseDAO有泛型,所以声明为
	 * CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO
	 * 代码块中的方法,可以获取子类CustomerDAOImpl中带泛型的父类BaseDAO<Customer>中的泛型参数Customer,
	 * 之后将其赋值给clazz,因此,成员方法中的所有clazz在被CustomerDAOImpl调用时不需要传入clazz = Customer.class了
	 */
	{
		//获取当前正在调用BaseDAO的子类所继承的父类的泛型
		//获取该子类所继承的泛型父类
		Type genericSuperclass = this.getClass().getGenericSuperclass();
		//类型转换
		ParameterizedType paramType = (ParameterizedType) genericSuperclass;
		//获取父类的泛型参数
		Type[] typeArguments = paramType.getActualTypeArguments();
		//因为只有一个泛型,所以取出数组中的第一个参数即可
		Type type = typeArguments[0];
		//赋值给clazz
		clazz = (Class<T>) type;
	}
	
	//添加事务的CUD
	public int CUDForTx(Connection conn, String sql, Object ...obj) {
		//sql中占位符的个数应该与可变形参的长度相同
		PreparedStatement ps = null;
		try {
			//1.预编译sql语句,获取PreparedStatement对象	
			ps = conn.prepareStatement(sql);

			//2.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
			}
					
			//3.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}
		//4.关闭资源
		JDBCUtils.closeResource(ps, null);//注意:不关闭连接
		return 0;
	}
	
	//添加事务的通用查询操作
	public T generalOneSelectForTx(Connection conn, String sql, Object ...obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			//1.建立连接
//				conn = JDBCUtils.getConnection();
			
			//2.预编译sql语句
			ps = conn.prepareStatement(sql);
			
			//3.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);
			}
			
			//4.执行sql,获取结果集对象以及结果集元数据对象
			rs = ps.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			if(rs.next()) {//如果有数据,则取出数据
				T t = clazz.getConstructor().newInstance();
				//根据数据的字段数,判断取出次数
				int columnCount = rsmd.getColumnCount();
				for(int i=0; i<columnCount; i++) {
					//取出数值以及列名
					/*
					 * 获取列的列名:rsmd.getColumnName()——不推荐使用
					 * 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
					 */
					Object columnValue = rs.getObject(i+1);
					String columnLabel = rsmd.getColumnLabel(i+1);
					//通过反射,将该值赋值给t对象的对应属性
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columnValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(rs, ps, null);
		}
		return null;	
	}
		
	//添加事务的查询
	public List<T> generalMoreSelectForTx(Connection conn, String sql, Object ...obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
//			//1.建立连接
//			conn = JDBCUtils.getConnection();
			
			//2.预编译sql语句
			ps = conn.prepareStatement(sql);
			
			//3.填充占位符
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);
			}
			
			//4.执行sql,获取结果集对象以及结果集元数据对象
			rs = ps.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			
			//创建集合对象
			List<T> list = new ArrayList<T>();
			
			while(rs.next()) {//如果有数据,则取出数据
				T t = clazz.getConstructor().newInstance();
				//根据数据的字段数,判断取出次数
				int columnCount = rsmd.getColumnCount();
				//给t对象指定属性赋值
				for(int i=0; i<columnCount; i++) {
					//取出数值以及列名
					/*
					 * 获取列的列名:rsmd.getColumnName()——不推荐使用
					 * 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
					 */
					Object columnValue = rs.getObject(i+1);
					String columnLabel = rsmd.getColumnLabel(i+1);
					//通过反射,将该值赋值给t对象的对应属性
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columnValue);
				}
				//将t对象添加到集合中
				list.add(t);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(rs, ps, null);
		}
		return null;	
	}
	
	//针对于分组函数的查询操作
	//需要声明为泛型方法,返回值类型与泛型类T不一定相同
	public <E> E getGroupValues(Connection conn, String sql, Object ...obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = conn.prepareStatement(sql);
			for(int i=0; i<obj.length; i++) {
				ps.setObject(i+1, obj[i]);
			}
			rs = ps.executeQuery();
			if(rs.next()) {
				Object value = rs.getObject(1);
				return (E)value;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(rs, ps, null);
		}
		return null;
	}
}

CustomerDAO接口

package jdbc_2.DAO_pro;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

import jdbc_1.bean.Customer;

/**
 * 此接口用于规范针对于customers表的CRUD操作
 * @author MCC
 *
 */
public interface CustomerDAO {
	/**
	 * 将cust对象添加到数据库中
	 * @param conn
	 * @param cust
	 */
	void insert(Connection conn, Customer cust);
	/**
	 * 根据指定的ID删除表中的记录
	 * @param conn
	 * @param id
	 */
	void deleteByID(Connection conn, int id);
	/**
	 * 根据cust对象的id修改数据库中对应id的信息为cust的信息
	 * @param conn
	 * @param cust
	 */
	void updateByID(Connection conn, Customer cust);
	/**
	 * 根据指定的id查询对应的Customer对象
	 * @param conn
	 * @param id
	 */
	Customer getCustomerByID(Connection conn, int id);
	/**
	 * 查询满足条件的所有Customer对象
	 * @param conn
	 * @return
	 */
	List<Customer> getAllCustomerByID(Connection conn);
	/**
	 * 获取表中记录的总数
	 * @param conn
	 * @return
	 */
	Long getCount(Connection conn);
	/**
	 * 获取表中的最大生日
	 * @param conn
	 * @return
	 */
	Date getMaxBirth(Connection conn);
	
}

CustomerDAOImpl类

package jdbc_2.DAO_pro;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

import jdbc_1.bean.Customer;

public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO {

	@Override
	public void insert(Connection conn, Customer cust) {
		String sql = "insert into customers (name, email, birth) values (?, ?, ?)";
		CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth());
	}

	@Override
	public void deleteByID(Connection conn, int id) {
		String sql = "delete from customers where id = ?";
		CUDForTx(conn, sql, id);
	}

	@Override
	public void updateByID(Connection conn, Customer cust) {
		String sql = "update customers set name = ?, email = ?, birth = ? where id = ? ";
		CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth(), cust.getId());
	}

	@Override
	public Customer getCustomerByID(Connection conn, int id) {
		String sql = "select id, name, email, birth from customers where id = ?";
		Customer customer = generalOneSelectForTx(conn, sql, id);
		return customer;
	}

	@Override
	public List<Customer> getAllCustomerByID(Connection conn) {
		String sql = "select id, name, email, birth from customers";
		List<Customer> list = generalMoreSelectForTx(conn, sql);
		return list;
	}

	@Override
	public Long getCount(Connection conn) {
		String sql = "select count(*) from customers";
		Object values = getGroupValues(conn, sql);
		return (Long)values;
	}

	@Override
	public Date getMaxBirth(Connection conn) {
		String sql = "select max(birth) from customers";
		Object values = getGroupValues(conn, sql);
		return (Date)values;
	}
	
}

测试类

package jdbc_2.DAO_pro.junit;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

import org.junit.Test;

import jdbc_1.bean.Customer;
import jdbc_1.myutil.JDBCUtils;
import jdbc_2.DAO_pro.CustomerDAOImpl;

public class CustomerDAOImplTest {

	//每个方法都要用,定义为成员属性
	private CustomerDAOImpl dao = new CustomerDAOImpl();

	@Test
	public void testInsert() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Customer customer = new Customer(0, "宇智波鼬", "yuzhiboyou@126.com", new Date(19930607));
			dao.insert(conn, customer);
			System.out.println("添加成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testDeleteByID() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			dao.deleteByID(conn, 13);
			System.out.println("删除成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testUpdateByID() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Customer customer = new Customer(18, "贝多芬", "beiduofen@126.com", new Date(19930607L));
			dao.updateByID(conn, customer);
			System.out.println("修改成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetCustomerByID() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Customer customer = dao.getCustomerByID(conn, 19);
			System.out.println(customer);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetAllCustomerByID() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			List<Customer> list = dao.getAllCustomerByID(conn);
			list.forEach(System.out::println);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetCount() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Long count = dao.getCount(conn);
			System.out.println(count);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

	@Test
	public void testGetMaxBirth() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Date maxBirth = dao.getMaxBirth(conn);
			System.out.println(maxBirth);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}

}

八、数据库连接池

  • JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
    • DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
    • C3P0 是一个开源组织提供的一个数据库连接池,**速度相对较慢,稳定性还可以。**hibernate官方推荐使用
    • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
    • BoneCP 是一个开源组织提供的数据库连接池,速度快
    • Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快
  • DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
  • DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
  • 特别注意:
    • 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
    • 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。

在这里插入图片描述

C3P0

配置文件:文件名必须为c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

	<named-config name="c3p0Config">
		<!-- 提供获取连接的4个基本信息 -->
		<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/test?serverTimezone=UTC&amp;rewriteBatchedStatements=true</property>
		<property name="user">root</property>
		<property name="password">macheng_0213</property>
		
		<!-- 数据库连接池管理的基本信息 -->
		<!-- 当数据库连接池中的连接不够时,c3p0可以向数据库服务器申请的额外连接数 -->
		<property name="acquireIncrement">5</property>
		<!-- c3p0初始时的连接数 -->
		<property name="initialPoolSize">10</property>
		<!-- c3p0维护的最小连接数 -->
		<property name="minPoolSize">10</property>
		<!-- c3p0维护的最大连接数 -->
		<property name="maxPoolSize">100</property>
		<!-- c3p0维护的最大Statement的个数 -->
		<property name="maxStatements">50</property>
		<!-- 每个连接可以使用的最大Statement个数 -->
		<property name="maxStatementsPerConnection">2</property>

	</named-config>
</c3p0-config>
package jdbc_2.connectionpool;

import java.sql.Connection;

import org.junit.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
 * 演示C3P0数据库连接池技术
 * @author MCC
 *
 */
public class C3P0Test {
	/*
	 * 方式1:
	 */
	@Test
	public void testGetConnection_1() throws Exception {
		//获取c3p0数据库连接池
		ComboPooledDataSource cpds = new ComboPooledDataSource();
		cpds.setDriverClass("com.mysql.cj.jdbc.Driver"); //loads the jdbc driver            
		cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&rewriteBatchedStatements=true");
		cpds.setUser("root");                                  
		cpds.setPassword("macheng_0213");
		
		//设置数据库连接池的属性,初始连接数,最大连接数等
		cpds.setInitialPoolSize(10);//初始时池中有10个连接
		
		//获取连接
		Connection conn = cpds.getConnection();
		
	}
	
	/*
	 * 方式2:使用xml文件
	 */
	@Test
	public void testGetConnection_2() throws Exception {
		//创建数据库连接池的同时,加载指定xml文件
		ComboPooledDataSource cpds = new ComboPooledDataSource("c3p0Config");  
		Connection conn = cpds.getConnection();
		System.out.println(conn);
	}
	
}

DBCP

dbcp连接池常用基本配置属性:

  • initialSize:连接池启动时创建的初始化连接数量(默认值为0)

  • maxActive:连接池中可同时连接的最大的连接数(默认值为8,调整为20,高峰单机器在20并发左右,自己根据应用场景定)

  • maxIdle:连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认为8个,maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数 上升超过maxIdle,而造成频繁的连接销毁和创建,类似于jvm参数中的Xmx设置)

  • minIdle:连接池中最小的空闲的连接数,低于这个数量会被创建新的连接(默认为0,调整为5,该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于minidle个数的连接,类似于jvm参数中的Xmn设置)

  • maxWait:最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为无限,调整为60000ms,避免因线程池不够用,而导致请求被无限制挂起)

  • poolPreparedStatements:开启池的prepared(默认是false,未调整,经过测试,开启后的性能没有关闭的好。)

  • maxOpenPreparedStatements:开启池的prepared 后的同时最大连接数(默认无限制,同上,未配置)

  • minEvictableIdleTimeMillis:连接池中连接,在时间段内一直空闲, 被逐出连接池的时间

  • removeAbandonedTimeout:超过时间限制,回收没有用(废弃)的连接(默认为 300秒,调整为180)

  • removeAbandoned:超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收(默认为false,调整为true)

在这里插入图片描述

package jdbc_2.connectionpool;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;

/**
 * 演示DBCP数据库连接池技术
 * @author MCC
 *
 */
public class DBCPTest {
	/*
	 * 方式1:
	 */
	@Test
	public void testGetConnection1() throws SQLException {
		//创建DBCP数据库连接池
		BasicDataSource ds = new BasicDataSource();
		
		//设置基本信息
		ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&rewriteBatchedStatements=true");
		ds.setUsername("root");
		ds.setPassword("macheng_0213");
		
		//设置涉及DBCP管理的属性
		ds.setInitialSize(10);
		ds.setMaxActive(8);
		
		//获取连接
		Connection conn = ds.getConnection();
		System.out.println(conn);
		
	}
	
	/**
	 * 方式2:使用配置文件
	 * @throws Exception 
	 */
	@Test
	public void testGetConnection2() throws Exception {
		Properties prop = new Properties();
		InputStream in = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
		prop.load(in);
		DataSource ds = BasicDataSourceFactory.createDataSource(prop);
		
		//获取连接
		Connection conn = ds.getConnection();
		System.out.println(conn);
		
	}

}

Druid

在这里插入图片描述

package jdbc_2.connectionpool;

import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import org.junit.Test;

import com.alibaba.druid.pool.DruidDataSourceFactory;

/**
 * 演示druid数据库连接池技术
 * @author MCC
 *
 */
public class DruidTest {
	@Test
	public void getConnection() throws Exception {
		//与DBCP创建对象方式类似
		Properties prop = new Properties();
		InputStream in = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
		prop.load(in);
		DataSource ds = DruidDataSourceFactory.createDataSource(prop);
		Connection conn = ds.getConnection();
		System.out.println(conn);
		
	}
}

使用数据库连接池的工具类

package jdbc_2.myutil;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

import jdbc_2.connectionpool.DBCPTest;
import jdbc_2.connectionpool.DruidTest;

/**
 * 使用数据库连接池获取连接
 * @author MCC
 *
 */
public class JDBCUtils {
	/**
	 * 使用c3p0数据库连接池获取链接
	 * @return Connection conn
	 * @throws SQLException
	 */
	//声明为静态成员属性,保证c3p0只有一个,所有对象共享一个数据库连接池
	private static ComboPooledDataSource cpds = new ComboPooledDataSource("c3p0Config");  
	public static Connection getConnection_c3p0() throws SQLException {
		Connection conn = cpds.getConnection();
		return conn;
	} 
	
	/**
	 * 使用DBCP数据库连接池获取链接
	 * @return Connection conn
	 * @throws SQLException
	 */
	//声明静态代码块,使静态成员属性在类加载时就被赋值,且只赋值一次
	private static DataSource ds;
	static {
		try {
			Properties prop = new Properties();
			InputStream in = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
			prop.load(in);
			ds = BasicDataSourceFactory.createDataSource(prop);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static Connection getConnection_DBCP() throws SQLException {
		//获取连接
		Connection conn = ds.getConnection();
		return conn;
	} 
	
	/**
	 * 使用druid数据库连接池获取链接
	 * @return Connection conn
	 * @throws SQLException
	 */
	//声明静态代码块,使静态成员属性在类加载时就被赋值,且只赋值一次
	private static DataSource dso;
	static {
		try {
			Properties prop = new Properties();
			InputStream in = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
			prop.load(in);
			dso = DruidDataSourceFactory.createDataSource(prop);
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
	public static Connection getConnection_druid() throws SQLException {
		//与DBCP创建对象方式类似
		Connection conn = dso.getConnection();
		return conn;
	}
	
	/**
	 * 关闭statement和Connection
	 * @param conn
	 * @param s
	 */
	public static void closeResource(Statement s, Connection conn) {
		try {
			if(s != null) {
				s.close();
			}
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		try {
			if(conn != null) {
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	//重载
	public static void closeResource(ResultSet rs, Statement s, Connection conn) {
		try {
			if(rs != null) {
				rs.close();
			}
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		try {
			if(s != null) {
				s.close();
			}
		} catch(SQLException e2) {
			e2.printStackTrace();
		}
		try {
			if(conn != null) {
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
}

九、Apache-DBUtils

  • commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。

  • API介绍:

    • org.apache.commons.dbutils.QueryRunner
    • org.apache.commons.dbutils.ResultSetHandler
    • 工具类:org.apache.commons.dbutils.DbUtils

QueryRunner

使用QueryRunner对象进行增删改查
update:增删改
query:查

package jdbc_2.Apache_DBUtils;

import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import jdbc_1.bean.Customer;
import jdbc_2.myutil.JDBCUtils;

/*
 * commons-dbutils是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,
 * 并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
 */
/**
 * 使用QueryRunner进行插入
 * @author MCC
 *
 */
public class QueryRunnerTest {
	@Test
	public void testInsert() {
		Connection conn = null;
		try {
			QueryRunner qr = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "insert into customers (name, email, birth) values (?, ?, ?)";
			int updateCount = qr.update(conn, sql, "赤砂之蝎", "xie@qq.com", "1999-2-1");
			System.out.println("添加成功 " + updateCount + " 条数据!");
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
	/**
	 * BeanHandler:ResultSetHandler接口的实现类,接收查询结果是一个对象的数据
	 * @throws Exception
	 */
	@Test
	public void testSelect_bean() {
		Connection conn = null;
		try {
			QueryRunner qr = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "select id, name, email, birth from customers where id = ?";
			BeanHandler<Customer> bh = new BeanHandler<Customer>(Customer.class);
			Customer customer = qr.query(conn, sql, bh, 24);
			System.out.println(customer);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
	/**
	 * BeanListHandler:接收查询结果是多个对象的数据
	 * @throws Exception
	 */
	@Test
	public void testSelect_beanList() {
		Connection conn = null;
		try {
			QueryRunner qr = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "select id, name, email, birth from customers where id > ?";
			BeanListHandler<Customer> blh = new BeanListHandler<Customer>(Customer.class);
			List<Customer> customerList = qr.query(conn, sql, blh, 19);
			customerList.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
	/**
	 * MapHandler:将查询结果(只有一条)保存为map,键为列名,值为列值
	 * @throws Exception
	 */
	@Test
	public void testSelect_map() {
		Connection conn = null;
		try {
			QueryRunner qr = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "select id, name, email, birth from customers where id = ?";
			MapHandler mh = new MapHandler();
			Map<String, Object> map = qr.query(conn, sql, mh, 25);
			Set<Entry<String, Object>> es = map.entrySet();
			Iterator<Entry<String, Object>> iter = es.iterator();
			while(iter.hasNext()) {
				System.out.println(iter.next());
				/*
				 *  id=25
					name=赤砂之蝎
					email=xie@qq.com
					birth=1999-02-01
				 */
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
	/**
	 * MapListHandler:将查询结果(多条)保存为map,键为列名,值为列值
	 * @throws Exception
	 */
	@Test
	public void testSelect_mapList() {
		Connection conn = null;
		try {
			QueryRunner qr = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "select id, name, email, birth from customers where id > ?";
			MapListHandler mlh = new MapListHandler();
			List<Map<String, Object>> customerList = qr.query(conn, sql, mlh, 19);
			customerList.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
	/**
	 * SaclarHandler:保存分组查询的数据
	 * 示例1:查询记录总数
	 * @throws Exception
	 */
	@Test
	public void testSelect_scalar1() {
		Connection conn = null;
		try {
			QueryRunner qr = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "select count(*) from customers";
			//分组查询的结果类型不确定,要指定泛型
			ScalarHandler<Long> sh = new ScalarHandler<Long>();
			Long count = qr.query(conn, sql, sh);
			System.out.println(count);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
	/**
	 * ScalarHandler:保存分组查询的数据,即保存特殊值的数据
	 * 示例2:查询最大生日
	 * @throws Exception
	 */
	@Test
	public void testSelect_scalar2() {
		Connection conn = null;
		try {
			QueryRunner qr = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "select max(birth) from customers";
			//分组查询的结果类型不确定,要指定泛型
			ScalarHandler<Date> sh = new ScalarHandler<Date>();
			Date date = qr.query(conn, sql, sh);
			System.out.println(date);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
}

ResetSetHandler

  • 该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。

  • ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)。

  • 接口的主要实现类:

    • ArrayHandler:把结果集中的第一行数据转成对象数组。
    • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
    • BeanHandler: 将结果集中的第一行数据封装到一个对应的JavaBean实例中。
    • BeanListHandler: 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
    • ColumnListHandler:将结果集中某一列的数据存放到List中。
    • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
    • MapHandler: 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
    • MapListHandler: 将结果集中的每一行数据都封装到一个Map里,然后再存放到List
    • ScalarHandler: 查询单个值对象

复现针对于customer表的BeanHandler

package jdbc_2.Apache_DBUtils;

import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

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

import jdbc_1.bean.Customer;
import jdbc_2.myutil.JDBCUtils;

public class MyResultSetHandler {
	@Test
	public void testResultSetHandler() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection_druid();
			String sql = "select id, name, email, birth from customers where id > ?";
			/*
			 * 通过匿名内部类实现ResultSetHandler接口,以返回多条记录为例
			 */
			ResultSetHandler<List<Customer>> handler = new ResultSetHandler<List<Customer>>() {	
				@Override
				public List<Customer> handle(ResultSet rs) throws SQLException {
					List<Customer> list = new ArrayList<Customer>();
					while(rs.next()) {
						int id = rs.getInt("id");
						String name = rs.getString("name");
						String email = rs.getString("email");
						Date birth = rs.getDate("birth");
						Customer customer = new Customer(id, name, email, birth);
						list.add(customer);
					}
					return list;
				}
				
			};
			List<Customer> list = runner.query(conn, sql, handler, 20);
			list.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, conn);
		}
	}
	
}

DbUtils

DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:

  • public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
  • public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
  • public static void commitAndClose(Connection conn)throws SQLException: 用来提交连接的事务,然后关闭连接
  • public static void commitAndCloseQuietly(Connection conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。
  • public static void rollback(Connection conn)throws SQLException:允许conn为null,因为方法内部做了判断
  • public static void rollbackAndClose(Connection conn)throws SQLException
  • rollbackAndCloseQuietly(Connection)
  • public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。
package jdbc_2.Apache_DBUtils;

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

import org.apache.commons.dbutils.DbUtils;

/**
 * 演示DbUtils工具类提供的方法
 * @author MCC
 *
 */
public class DbUtilsTest {
	public void closeResource(Connection conn, Statement s, ResultSet rs) {
//		try {
//			DbUtils.close(conn);
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		try {
//			DbUtils.close(s);
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		try {
//			DbUtils.close(rs);
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
		DbUtils.closeQuietly(conn);
		DbUtils.closeQuietly(s);
		DbUtils.closeQuietly(rs);
	}
}

十、总结

  1. 使用数据库连接池(druid)获取链接
  2. 使用Apache-DBUtils下的QueryRunner进行增删改查
  3. (可选)使用Apache-DBUtils下的DbUtils关闭连接
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值