JDBC03-批处理、连接池、DBUtils、事物、DAO通用方法

1. 封装 JDBCUtils 【关闭、得到连接】

1.1 说明

在这里插入图片描述
在这里插入图片描述

1.2 代码实现

工具类 JDBCUtils

package com.hspedu.jdbc.utils;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* 这是一个工具类,完成 mysql 的连接和关闭资源
*/
public class JDBCUtils {

	//定义相关的属性(4 个), 因为只需要一份,因此,我们做出 static
	private static String user; //用户名
	private static String password; //密码
	private static String url; //url
	private static String driver; //驱动名
	
	//在 static 代码块去初始化
	static {
		try {
			Properties properties = new Properties();
			properties.load(new FileInputStream("src\\mysql.properties"));
			//读取相关的属性值
			user = properties.getProperty("user");
			password = properties.getProperty("password");
			url = properties.getProperty("url");
			driver = properties.getProperty("driver");
		} catch (IOException e) {
			//在实际开发中,我们可以这样处理
			//1. 将编译异常转成 运行异常
			//2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便. 
			throw new RuntimeException(e);
		} 
	}
	
	//连接数据库, 返回 Connection
	public static Connection getConnection() {
		try {
			return DriverManager.getConnection(url, user, password);
		} catch (SQLException e) {
			//1. 将编译异常转成 运行异常
			//2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便. throw new RuntimeException(e);
		}
	}
	
	//关闭相关资源
	/*
		1. ResultSet 结果集
		2. Statement 或者 PreparedStatement
		3. Connection
		4. 如果需要关闭资源,就传入对象,否则传入 null
	*/
	public static void close(ResultSet set, Statement statement, Connection connection) {
		//判断是否为 null
		try {
			if (set != null) {
				set.close();
			}
			if (statement != null) {
				statement.close();
			}
			if (connection != null) {
				connection.close();
			}
		} catch (SQLException e) {
			//将编译异常转成运行异常抛出
			throw new RuntimeException(e);
		}
	}
}

测试类

package com.hspedu.jdbc.utils;

import org.junit.jupiter.api.Test;
import java.sql.*;

/**
* 该类演示如何使用 JDBCUtils 工具类,完成 dml 和 select
*/
public class JDBCUtils_Use {

	@Test
	public void testSelect() {
		//1. 得到连接
		Connection connection = null;
		
		//2. 组织一个 sql
		String sql = "select * from actor where id = ?";
		PreparedStatement preparedStatement = null;
		ResultSet set = null;
		
		//3. 创建 PreparedStatement 对象
		try {
			connection = JDBCUtils.getConnection();
			System.out.println(connection.getClass()); //com.mysql.jdbc.JDBC4Connection
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.setInt(1, 5);//给?号赋值
			//执行, 得到结果集
			set = preparedStatement.executeQuery();
			//遍历该结果集
			while (set.next()) {
				int id = set.getInt("id");
				String name = set.getString("name");
				String sex = set.getString("sex");
				Date borndate = set.getDate("borndate");
				String phone = set.getString("phone");
				System.out.println(id + "\t" + name + "\t" + sex + "\t" + borndate + "\t" + phone);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//关闭资源
			JDBCUtils.close(set, preparedStatement, connection);
		}
	}
	@Test
	public void testDML() {//insert , update, delete
		//1. 得到连接
		Connection connection = null;
		
		//2. 组织一个 sql
		String sql = "update actor set name = ? where id = ?";
		
		// 测试 delete 和 insert ,自己玩. PreparedStatement preparedStatement = null;
		//3. 创建 PreparedStatement 对象
		try {
			connection = JDBCUtils.getConnection();
			preparedStatement = connection.prepareStatement(sql);
			//给占位符赋值
			preparedStatement.setString(1, "周星驰");
			preparedStatement.setInt(2, 4);
			//执行
			preparedStatement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//关闭资源
			JDBCUtils.close(null, preparedStatement, connection);
		}
	}
}

2. 事务

2.1 基本介绍

在这里插入图片描述

2.2 应用实例

模拟经典的转账业务
在这里插入图片描述
在这里插入图片描述

2.3 不使用事务可能出现的问题模拟-模拟经典的转账业务

//没有使用事务. 
@Test
public void noTransaction() {
	//操作转账的业务
	//1. 得到连接
	Connection connection = null;
	
	//2. 组织一个 sql
	String sql = "update account set balance = balance - 100 where id = 1";
	String sql2 = "update account set balance = balance + 100 where id = 2";
	PreparedStatement preparedStatement = null;
	//3. 创建 PreparedStatement 对象
	try {
		connection = JDBCUtils.getConnection(); // 在默认情况下,connection 是默认自动提交
		preparedStatement = connection.prepareStatement(sql);
		preparedStatement.executeUpdate(); // 执行第 1 条 sql
		int i = 1 / 0; //抛出异常
		preparedStatement = connection.prepareStatement(sql2);
		preparedStatement.executeUpdate(); // 执行第 3 条 sql
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		//关闭资源
		JDBCUtils.close(null, preparedStatement, connection);
	}
}

2.4 使用事务解决上述问题-模拟经典的转账业务

//事务来解决
@Test
public void useTransaction() {
	//操作转账的业务
	//1. 得到连接
	Connection connection = null;
	
	//2. 组织一个 sql
	String sql = "update account set balance = balance - 100 where id = 1";
	String sql2 = "update account set balance = balance + 100 where id = 2";
	PreparedStatement preparedStatement = null;
	//3. 创建 PreparedStatement 对象
	try {
		connection = JDBCUtils.getConnection(); // 在默认情况下,connection 是默认自动提交
		//将 connection 设置为不自动提交
		connection.setAutoCommit(false); //开启了事务
		preparedStatement = connection.prepareStatement(sql);
		preparedStatement.executeUpdate(); // 执行第 1 条 sql
		int i = 1 / 0; //抛出异常
		preparedStatement = connection.prepareStatement(sql2);
		preparedStatement.executeUpdate(); // 执行第 3 条 sql
		
		//这里提交事务
		connection.commit();
	} catch (SQLException e) {
	
		//这里我们可以进行回滚,即撤销执行的 SQL
		//默认回滚到事务开始的状态. System.out.println("执行发生了异常,撤销执行的 sql");
		try {
			connection.rollback();
		} catch (SQLException throwables) {
			throwables.printStackTrace();
		}
		e.printStackTrace();
	} finally {
		//关闭资源
		JDBCUtils.close(null, preparedStatement, connection);
	}
}

3. 批处理

3.1 基本介绍

在这里插入图片描述

在这里插入图片描述

3.2 应用实例

在这里插入图片描述

特别注意:
1.注意修改配置文件
2.记得关闭事物自动提交(巨坑)

package com.hspedu.jdbc.batch_;

import com.hspedu.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
* 演示 java 的批处理
*/
public class Batch_ {
	//传统方法,添加 5000 条数据到 admin2
	@Test
	public void noBatch() throws Exception {
		Connection connection = JDBCUtils.getConnection();
		String sql = "insert into admin2 values(null, ?, ?)";
		PreparedStatement preparedStatement = connection.prepareStatement(sql);
		System.out.println("开始执行");
		long start = System.currentTimeMillis();//开始时间
		for (int i = 0; i < 5000; i++) {  //5000 执行
			preparedStatement.setString(1, "jack" + i);
			preparedStatement.setString(2, "666");
			preparedStatement.executeUpdate();
		}
		long end = System.currentTimeMillis();
		System.out.println("传统的方式 耗时=" + (end - start));//传统的方式 耗时=10702
		
		//关闭连接
		JDBCUtils.close(null, preparedStatement, connection);
	}


	//使用批量方式添加数据
	@Test
	public void batch() throws Exception {
		Connection connection = JDBCUtils.getConnection();
		connection.setAutoCommit(false);
		String sql = "insert into admin2 values(null, ?, ?)";
		PreparedStatement preparedStatement = connection.prepareStatement(sql);
		System.out.println("开始执行");
		long start = System.currentTimeMillis();//开始时间
		for (int i = 0; i < 5000; i++) {//5000 执行
			preparedStatement.setString(1, "jack" + i);
			preparedStatement.setString(2, "666");
		//将 sql 语句加入到批处理包中 -> 看源码
		/*
			//1. //第一就创建 ArrayList - elementData => Object[]
			//2. elementData => Object[] 就会存放我们预处理的 sql 语句
			//3. 当 elementData 满后,就按照 1.5 扩容
			//4. 当添加到指定的值后,就 executeBatch
			//5. 批量处理会减少我们发送 sql 语句的网络开销,而且减少编译次数,因此效率提高
			
			public void addBatch() throws SQLException {
				synchronized(this.checkClosed().getConnectionMutex()) {
					if (this.batchedArgs == null) {
						this.batchedArgs = new ArrayList();
					}
					for(int i = 0; i < this.parameterValues.length; ++i) {
						this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
					}
					this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
				}
			}
		*/
			preparedStatement.addBatch();
		
			//当有 1000 条记录时,在批量执行
			if((i + 1) % 1000 == 0) {//满 1000 条 sql
				preparedStatement.executeBatch();
				//清空一把
				preparedStatement.clearBatch();
			}
		}
		long end = System.currentTimeMillis();
		connection.commit();
		System.out.println("批量方式 耗时=" + (end - start));//批量方式 耗时=108
		//关闭连接
		JDBCUtils.close(null, preparedStatement, connection);
	}
}

4. 数据库连接池

4.1 五k 次连接数据库问题

在这里插入图片描述

package com.hspedu.jdbc.datasource;

import com.hspedu.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.sql.Connection;


public class ConQuestion {

	//代码 连接 mysql 5000 次
	@Test
	public void testCon() {
		//看看连接-关闭 connection 会耗用多久
		long start = System.currentTimeMillis();
		System.out.println("开始连接.....");
		
		for (int i = 0; i < 5000; i++) {
			//使用传统的 jdbc 方式,得到连接
			Connection connection = JDBCUtils.getConnection();
			
			//做一些工作,比如得到 PreparedStatement ,发送 sql
			//.......... //关闭
			JDBCUtils.close(null, null, connection);
		}
		long end = System.currentTimeMillis();
		System.out.println("传统方式 5000 次 耗时=" + (end - start));//传统方式 5000 次 耗时=7099
	}
}

4.2 传统获取 Connection 问题分析

在这里插入图片描述

连接示意图
在这里插入图片描述

在这里插入图片描述
连接池示意图
在这里插入图片描述

4.3 数据库连接池种类

在这里插入图片描述

4.3.1 C3P0

继承关系图
在这里插入图片描述

厂家必须实现该接口

在这里插入图片描述
最大连接数和初始化连接数的区别
在这里插入图片描述

前期准备:先导入jar包
在这里插入图片描述

应用实例
在这里插入图片描述
1)方式1

package com.hspedu.jdbc.datasource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
* 演示 c3p0 的使用
*/

//方式 1: 相关参数,在程序中指定 user, url , password 等
@Test
public void testC3P0_01() throws Exception {
	//1. 创建一个数据源对象(相当于连接池)
	ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
	
	//2. 通过配置文件 mysql.properties 获取相关连接的信息
	Properties properties = new Properties();
	properties.load(new FileInputStream("src\\mysql.properties"));
	//读取相关的属性值
	String user = properties.getProperty("user");
	String password = properties.getProperty("password");
	String url = properties.getProperty("url");
	String driver = properties.getProperty("driver");
	
	//给数据源 comboPooledDataSource 设置相关的参数
	//注意:连接管理是由 comboPooledDataSource 来管理
	comboPooledDataSource.setDriverClass(driver);
	comboPooledDataSource.setJdbcUrl(url);
	comboPooledDataSource.setUser(user);
	comboPooledDataSource.setPassword(password);
	//设置初始化连接数
	comboPooledDataSource.setInitialPoolSize(10);
	//最大连接数  区别如上图
	comboPooledDataSource.setMaxPoolSize(50);
	
	//测试连接池的效率, 测试对 mysql 5000 次操作
	long start = System.currentTimeMillis();
	for (int i = 0; i < 5000; i++) {
		Connection connection = comboPooledDataSource.getConnection(); //这个方法就是从 DataSource 接口实现的
		//System.out.println("连接 OK");
		connection.close();
	}
	long end = System.currentTimeMillis();
	//c3p0 5000 连接 mysql 耗时=391
	System.out.println("c3p0 5000 连接 mysql 耗时=" + (end - start));
}

2)方式2
需要先添加配置文件:c3p0-config.xml

<c3p0-config>
<!-- 定义名字 -->
  <named-config name="my_c3p0"> 
<!-- 驱动类 -->
  <property name="driverClass">com.mysql.jdbc.Driver</property>
  <!-- url-->
  	<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/t1</property>
  <!-- 用户名 -->
  		<property name="user">root</property>
  		<!-- 密码 -->
  	<property name="password">root</property>
  	<!-- 每次增长的连接数-->
    <property name="acquireIncrement">5</property>
    <!-- 初始的连接数 -->
    <property name="initialPoolSize">10</property>
    <!-- 最小连接数 -->
    <property name="minPoolSize">5</property>
   <!-- 最大连接数 -->
    <property name="maxPoolSize">50</property>

	<!-- 可连接的最多的命令对象数 -->
    <property name="maxStatements">5</property> 
    
    <!-- 每个连接对象可连接的最多的命令对象数 -->
    <property name="maxStatementsPerConnection">2</property>
  </named-config>
</c3p0-config>

说明:
在这里插入图片描述

//第二种方式 使用配置文件模板来完成
//1. 将 c3p0 提供的 c3p0.config.xml 拷贝到 src 目录下
//2. 该文件指定了连接数据库和连接池的相关参数

@Test
public void testC3P0_02() throws SQLException {

	ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("hsp_edu");
	
	//测试 5000 次连接 mysql
	long start = System.currentTimeMillis();
	
	System.out.println("开始执行....");
	for (int i = 0; i < 500000; i++) {
		Connection connection = comboPooledDataSource.getConnection();
		//System.out.println("连接 OK~");
		connection.close();
	}
	
	long end = System.currentTimeMillis();
	
	//c3p0 的第二种方式 耗时=413
	System.out.println("c3p0 的第二种方式(500000) 耗时=" + (end - start));//1917
}

4.3.2 Druid(德鲁伊)

在这里插入图片描述
先引入jar包和配置文件
druid.properties

#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/t1?rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/girls
username=root
password=root
#initial connection Size
initialSize=10
#min idle connecton size  类似最小连接数
minIdle=5
#max active connection size
maxActive=20
#max wait time (5000 mil seconds)  等待最大时长为5秒
maxWait=5000
@Test
public void testDruid() throws Exception {
	//1. 加入 Druid jar 包
	//2. 加入 配置文件 druid.properties , 将该文件拷贝项目的 src 目录
	//3. 创建 Properties 对象, 读取配置文件
	Properties properties = new Properties();
	properties.load(new FileInputStream("src\\druid.properties"));
	
	//4. 创建一个指定参数的数据库连接池, Druid 连接池
	DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
	long start = System.currentTimeMillis();
	for (int i = 0; i < 500000; i++) {
		Connection connection = dataSource.getConnection();
		System.out.println(connection.getClass());
		//System.out.println("连接成功!");
		connection.close();
	}
	long end = System.currentTimeMillis();
	//druid 连接池 操作 5000 耗时=412
	System.out.println("druid 连接池 操作 500000 耗时=" + (end - start));//539
}

4.4 将 JDBCUtils 工具类改成 Druid(德鲁伊)实现

在这里插入图片描述

/**
* 基于 druid 数据库连接池的工具类
*/
public class JDBCUtilsByDruid {

	private static DataSource ds;
	
	//在静态代码块完成 ds 初始化
	static {
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream("src\\druid.properties"));
			ds = DruidDataSourceFactory.createDataSource(properties);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//编写 getConnection 方法
	public static Connection getConnection() throws SQLException {
		return ds.getConnection();
	}
	
	//关闭连接, 老师再次强调: 在数据库连接池技术中,close 不是真的断掉连接
	//而是把使用的 Connection 对象放回连接池
	public static void close(ResultSet resultSet, Statement statement, Connection connection) {
		try {
			if (resultSet != null) {
				resultSet.close();
			}
			if (statement != null) {
				statement.close();
			}
			if (connection != null) {
				connection.close();
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
}

测试类

@SuppressWarnings({"all"})
public class JDBCUtilsByDruid_USE {
	@Test
	public void testSelect() {
	
		System.out.println("使用 druid 方式完成");
		//1. 得到连接
		Connection connection = null;
		
		//2. 组织一个 sql
		String sql = "select * from actor where id >= ?";
		PreparedStatement preparedStatement = null;
		ResultSet set = null;
		
		//3. 创建 PreparedStatement 对象
		try {
			connection = JDBCUtilsByDruid.getConnection();
			System.out.println(connection.getClass());//运行类型 com.alibaba.druid.pool.DruidPooledConnection
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.setInt(1, 1);//给?号赋值
			
			//执行, 得到结果集
			set = preparedStatement.executeQuery();
			
			//遍历该结果集
			while (set.next()) {
				int id = set.getInt("id");
				String name = set.getString("name");//getName()
				String sex = set.getString("sex");//getSex()
				Date borndate = set.getDate("borndate");
				String phone = set.getString("phone");
				System.out.println(id + "\t" + name + "\t" + sex + "\t" + borndate + "\t" + phone);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//关闭资源
			JDBCUtilsByDruid.close(set, preparedStatement, connection);
		}
	}
}

5. Apache—DBUtils

5.1 先分析一个问题

在这里插入图片描述

5.2 解决方案

在这里插入图片描述

5.3 用自己的土方法来解决

1)先创建一个类,用于映射

package com.pojo;

/**
 * 该类用于和数据库数据进行绑定
 */
public class Actor {
    private int id;
    private String name;
    private String sex;
    private String borndate;
    private String phone;

    public Actor() {//一定要提供,将来反射要用
    }

    public Actor(int id, String name, String sex, String borndate, String phone) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.borndate = borndate;
        this.phone = phone;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public String getBorndate() {
        return borndate;
    }

    public String getPhone() {
        return phone;
    }

    @Override
    public String toString() {
        return "Actor{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", borndate='" + borndate + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setBorndate(String borndate) {
        this.borndate = borndate;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

注意事项:
1.必须提供无参构造和set方法,DBUtil框架使用反射时需要使用

2)测试方法

//使用老师的土方法来解决 ResultSet =封装=> Arraylist
@Test
public ArrayList<Actor> testSelectToArrayList() {
	System.out.println("使用 druid 方式完成");
	
	//1. 得到连接
	Connection connection = null;
	//2. 组织一个 sql
	String sql = "select * from actor where id >= ?";
	PreparedStatement preparedStatement = null;
	ResultSet set = null;
	ArrayList<Actor> list = new ArrayList<>();//创建 ArrayList 对象,存放 actor 对象
	
	//3. 创建 PreparedStatement 对象
	try {
		connection = JDBCUtilsByDruid.getConnection();
		System.out.println(connection.getClass());//运行类型 com.alibaba.druid.pool.DruidPooledConnection
		preparedStatement = connection.prepareStatement(sql);
		preparedStatement.setInt(1, 1);//给?号赋值
		
		//执行, 得到结果集
		set = preparedStatement.executeQuery();
		
		//遍历该结果集
		while (set.next()) {
			int id = set.getInt("id");
			String name = set.getString("name");//getName()
			String sex = set.getString("sex");//getSex()
			Date borndate = set.getDate("borndate");
			String phone = set.getString("phone");
			//把得到的 resultset 的记录,封装到 Actor 对象,放入到 list 集合
			list.add(new Actor(id, name, sex, borndate, phone));
		}
		System.out.println("list 集合数据=" + list);
		
		for(Actor actor : list) {
			System.out.println("id=" + actor.getId() + "\t" + actor.getName());
		}
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		//关闭资源
		JDBCUtilsByDruid.close(set, preparedStatement, connection);
	}
	//因为 ArrayList 和 connection 没有任何关联,所以该集合可以复用. 
	return list;
}

5.4 DBUtils引入和基本介绍

在这里插入图片描述

5.4.1 应用实例

在这里插入图片描述
入门案例:

//使用 apache-DBUtils 工具类 + druid 完成对表的 crud 操作
@Test
public void testQueryMany() throws SQLException { //返回结果是多行的情况
	//1. 得到 连接 (druid)
	Connection connection = JDBCUtilsByDruid.getConnection();
	
	//2. 使用 DBUtils 类和接口 , 先引入 DBUtils 相关的 jar , 加入到本 Project
	
	//3. 创建 QueryRunner
	QueryRunner queryRunner = new QueryRunner();
	
	//4. 就可以执行相关的方法,返回 ArrayList 结果集
	//String sql = "select * from actor where id >= ?";
	// 注意: sql 语句也可以查询部分列
	String sql = "select id, name from actor where id >= ?";
	
	// 解读
	//(1) query 方法就是执行 sql 语句,得到 resultset ---封装到 --> ArrayList 集合中
	//(2) 返回集合
	//(3) connection: 连接
	//(4) sql : 执行的 sql 语句
	//(5) new BeanListHandler<>(Actor.class): 在将 resultset -> Actor 对象 -> 封装到 ArrayList
	// 底层使用反射机制 去获取 Actor 类的属性,然后进行封装
	//(6) 1 就是给 sql 语句中的? 赋值,可以有多个值,因为是可变参数 Object... params
	//(7) 底层得到的 resultset ,会在 query 关闭, 关闭 PreparedStatment
	/**
	* 分析 queryRunner.query 方法:
	* public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
	* 	PreparedStatement stmt = null;//定义 PreparedStatement
	* 	ResultSet rs = null;//接收返回的 ResultSet
	* 	Object result = null;//返回 ArrayList
	*
	* 	try {
	* 		stmt = this.prepareStatement(conn, sql);//创建 PreparedStatement
	* 		this.fillStatement(stmt, params);//对 sql 进行 ? 赋值
	* 		rs = this.wrap(stmt.executeQuery());//执行 sql,返回 resultset
	* 		result = rsh.handle(rs);//返回的 resultset --> arrayList[result] [使用到反射,对传入 class 对象处理]
	* 	} catch (SQLException var33) {
	* 		this.rethrow(var33, sql, params);
	* 	} finally {
	* 		try {
	* 			this.close(rs);//关闭 resultset
	*   	} finally {
	* 			this.close((Statement)stmt);//关闭 preparedstatement 对象
	* 		}
	* 	}
	* 	return result;
	* }
	*/
	List<Actor> list =
	queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
	System.out.println("输出集合的信息");
	for (Actor actor : list) {
	System.out.print(actor);
	}
	//释放资源
	JDBCUtilsByDruid.close(null, null, connection);
}

5.4.2 ResultSetHandler的种类

1)返回的结果是单行记录(单个对象)

//演示 apache-dbutils + druid 
//完成 返回的结果是单行记录(单个对象)
@Test
public void testQuerySingle() throws SQLException {

	//1. 得到 连接 (druid)
	Connection connection = JDBCUtilsByDruid.getConnection();
	//2. 使用 DBUtils 类和接口 , 先引入 DBUtils 相关的 jar , 加入到本 Project
	//3. 创建 QueryRunner
	QueryRunner queryRunner = new QueryRunner();
	//4. 就可以执行相关的方法,返回单个对象
	String sql = "select * from actor where id = ?";
	// 解读
	// 因为我们返回的单行记录<--->单个对象 , 使用的 Hander 是 BeanHandler
	Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 10);
	System.out.println(actor);
	// 释放资源
	JDBCUtilsByDruid.close(null, null, connection);
}

2)完成查询结果是单行单列-返回的就是 object

//演示 apache-dbutils + druid 
//完成查询结果是单行单列-返回的就是 object
@Test
public void testScalar() throws SQLException {
	//1. 得到 连接 (druid)
	Connection connection = JDBCUtilsByDruid.getConnection();
	//2. 使用 DBUtils 类和接口 , 先引入 DBUtils 相关的 jar , 加入到本 Project
	//3. 创建 QueryRunner
	QueryRunner queryRunner = new QueryRunner();
	//4. 就可以执行相关的方法,返回单行单列 , 返回的就是 Object
	String sql = "select name from actor where id = ?";
	//解读: 因为返回的是一个对象, 使用的 handler 就是 ScalarHandler
	Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 4);
	System.out.println(obj);
	// 释放资源
	JDBCUtilsByDruid.close(null, null, connection);
}

5.4.3 DButils的dml操作

//演示 apache-dbutils + druid 
//完成 dml (update, insert ,delete)
@Test
public void testDML() throws SQLException {
	//1. 得到 连接 (druid)
	Connection connection = JDBCUtilsByDruid.getConnection();
	//2. 使用 DBUtils 类和接口 , 先引入 DBUtils 相关的 jar , 加入到本 Project
	//3. 创建 QueryRunner
	QueryRunner queryRunner = new QueryRunner();
	
	//4. 这里组织 sql 完成 update, insert delete
	//String sql = "update actor set name = ? where id = ?";
	//String sql = "insert into actor values(null, ?, ?, ?, ?)";
	String sql = "delete from actor where id = ?";
	
	//解读
	//(1) 执行 dml 操作是 queryRunner.update()
	//(2) 返回的值是受影响的行数 (affected: 受影响)
	//int affectedRow = queryRunner.update(connection, sql, "林青霞", "女", "1966-10-10", "116");
	int affectedRow = queryRunner.update(connection, sql, 1000 );
	System.out.println(affectedRow > 0 ? "执行成功" : "执行没有影响到表");
	
	// 释放资源
	JDBCUtilsByDruid.close(null, null, connection);
}

5.5 表和 JavaBean 的类型映射关系

在这里插入图片描述

6. DAO 和增删改查通用方法-BasicDao

6.1 问题引入说明

在这里插入图片描述
架构图
在这里插入图片描述

6.2 DAO说明

在这里插入图片描述

6.3 应用实例

在这里插入图片描述
javaBean

/**
* Actor 对象和 actor 表的记录对应
*/
public class Actor { //Javabean, POJO, Domain 对象
	private Integer id;
	private String name;
	private String sex;
	private Date borndate;
	private String phone;
	
	public Actor() { //一定要给一个无参构造器[反射需要]
	}
	
	public Actor(Integer id, String name, String sex, Date borndate, String phone) {
		this.id = id;
		this.name = name;
		this.sex = sex;
		this.borndate = borndate;
		this.phone = phone;
	}
	
	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 Date getBorndate() {
		return borndate;
	}
	public void setBorndate(Date borndate) {
		this.borndate = borndate;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	@Override
	public String toString() {
		return "\nActor{" +
		"id=" + id +
		", name='" + name + '\'' +
		", sex='" + sex + '\'' +
		", borndate=" + borndate +
		", phone='" + phone + '\'' +
		'}';
	}
}

utils:工具类

/**
* 基于 druid 数据库连接池的工具类
*/
public class JDBCUtilsByDruid {

	private static DataSource ds;
	
	//在静态代码块完成 ds 初始化
	static {
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream("src\\druid.properties"));
			ds = DruidDataSourceFactory.createDataSource(properties);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//编写 getConnection 方法
	public static Connection getConnection() throws SQLException {
		return ds.getConnection();
	}
	
	//关闭连接, 老师再次强调: 在数据库连接池技术中,close 不是真的断掉连接
	//而是把使用的 Connection 对象放回连接池
	public static void close(ResultSet resultSet, Statement statement, Connection connection) {
		try {
			if (resultSet != null) {
				resultSet.close();
			}
			if (statement != null) {
				statement.close();
			}
			if (connection != null) {
				connection.close();
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
}

BasicDAO

/**
* 开发 BasicDAO , 是其他 DAO 的父类, 使用到 apache-dbutils
*/
public class BasicDAO<T> { //泛型指定具体类型
	private QueryRunner qr = new QueryRunner();
	
	//开发通用的 dml 方法, 针对任意的表
	public int update(String sql, Object... parameters) {
		Connection connection = null;
		try {
			connection = JDBCUtilsByDruid.getConnection();
			int update = qr.update(connection, sql, parameters);
			return update;
		} catch (SQLException e) {
			throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
		} finally {
			JDBCUtilsByDruid.close(null, null, connection);
		}
	}
	
	//返回多个对象(即查询的结果是多行), 针对任意表
	/**
	*
	* @param sql sql 语句,可以有 ?
	* @param clazz 传入一个类的 Class 对象 比如 Actor.class
	* @param parameters 传入 ? 的具体的值,可以是多个
	* @return 根据 Actor.class 返回对应的 ArrayList 集合
	*/
	public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters) {
		Connection connection = null;
		try {
			connection = JDBCUtilsByDruid.getConnection();
			return qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);
		} catch (SQLException e) {
			throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
		} finally {
			JDBCUtilsByDruid.close(null, null, connection);
		}
	}


	//查询单行结果 的通用方法
	public T querySingle(String sql, Class<T> clazz, Object... parameters) {
		Connection connection = null;
		try {
			connection = JDBCUtilsByDruid.getConnection();
			return qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);
		} catch (SQLException e) {
			throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
		} finally {
			JDBCUtilsByDruid.close(null, null, connection);
		}
	}
	
	
	//查询单行单列的方法,即返回单值的方法
	public Object queryScalar(String sql, Object... parameters) {
		Connection connection = null;
		try {
			connection = JDBCUtilsByDruid.getConnection();
			return qr.query(connection, sql, new ScalarHandler(), parameters);
		} catch (SQLException e) {
			throw new RuntimeException(e); //将编译异常->运行异常 ,抛出
		} finally {
			JDBCUtilsByDruid.close(null, null, connection);
		}
	}
}

ActorDAO

public class ActorDAO extends BasicDAO<Actor> {
	//1. 就有 BasicDAO 的方法
	//2. 根据业务需求,可以编写特有的方法. 
}

测试类

//测试 ActorDAO 对 actor 表 crud 操作
@Test
public void testActorDAO() {

	ActorDAO actorDAO = new ActorDAO();
	//1. 查询
	List<Actor> actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 1);
	System.out.println("===查询结果===");
	for (Actor actor : actors) {
		System.out.println(actor);
	}
	
	//2. 查询单行记录
	Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 6);
	System.out.println("====查询单行结果====");
	System.out.println(actor);
	
	//3. 查询单行单列
	Object o = actorDAO.queryScalar("select name from actor where id = ?", 6);
	System.out.println("====查询单行单列值===");
	System.out.println(o);
	
	//4. dml 操作 insert ,update, delete
	int update = actorDAO.update("insert into actor values(null, ?, ?, ?, ?)", "张无忌", "男", "2000-11-11", "999");
	System.out.println(update > 0 ? "执行成功" : "执行没有影响表");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值