文章目录
- 一、JDBC
- 1.1 概述
- 1.2 快速入门
- 二、获取数据库连接的5种方式
- 三、ResultSet
- 四、Statement与PreparedStatement
- 4.1 Statement存在SQL注入
- 4.2 PreparedStatement
- 五、JDBC API
- 六、事务
- 七、批处理
- 八、数据库的连接方式
- 8.1 传统的连接方式
- 8.2 数据库的连接池技术
- 8.2.1 C3P0
- 8.2.2 德鲁伊连接池
- 九、自定义连接数据库的工具类
- 9.1 自定义JDBCUtils工具类
- 9.2 使用Druid实现JDBCUtils工具类
- 十、使用开源的DBUtils工具类操作数据库
- 10.1 问题引入
- 10.2 使用DBUtils+数据连接池(Driud)方式,操作数据库
- 十一、【最终版】BasicDao+Druid
一、JDBC
1.1 概述
- 为访问不同的数据库提供了统一的接口
- Java程序员使用jdbc,可以连接任何提供了jdbc驱动程序的数据库系统
1.2 快速入门
JDBC编写步骤:
- 注册驱动 - 加载Driver类
- 获取连接 - 得到Connection
- 执行增删改查 - 发送SQL命令给到mysql执行
- 释放资源 - 关闭相关的连接
第一步:新建一个libs文件夹,将mysql的jdbc的jar包放入其中,然后右击add as library将jar包导入项目
第二步:向数据库mydb的表actor插入一条数据
package JDBC_Exercise;
import com.mysql.cj.jdbc.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author 神代言
* @version 1.0
*/
public class Jdbc01 {
public static void main(String[] args) throws SQLException {
//将mysql.jar 拷贝到该目录下,加载为库
//1. 注册驱动
// 创建一个driver对象:new com.cj.mysql.jdbc.Driver 8.0版本需要.cj
Driver driver = new Driver();
//2. 得到连接
//解读:jdbc:mysql:// 规定好的协议,通过jdbc的方式来连接mysql
//(2)localhost 主机,可以是IP地址
//(3)3306 表示mysql监听的端口
//(4)jdbc 表示连接到mysql dbms 的哪个数据库
//(5)mysql的连接本质:就是socket连接
//(6)serverTimezone=UTC需要设置时区
String url = "jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC";
//将 用户名和密码放入到Properties 对象
Properties properties = new Properties();
//说明:user 和 password 是规定好的,后边的值根据实际情况来写
properties.setProperty("user", "root");//用户
properties.setProperty("password", "123456");//密码
//根据url连接数据库
Connection connect = driver.connect(url, properties);
//3. 执行sql语句
String sql = "insert into actor values(null,'吴彦祖','男','2023-09-14','1235')";
//用于执行静态sql语句,并返回结果
Statement statement = connect.createStatement();
int i = statement.executeUpdate(sql);//如果是 dml 语句,返回的就是影响行数
System.out.println(i > 0 ? "成功" : "失败");
//4. 关闭连接资源
statement.close();
connect.close();
}
}
二、获取数据库连接的5种方式
package JDBC_Exercise;
import com.mysql.cj.jdbc.Driver;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author 神代言
* @version 1.0
*/
public class Jdbc01 {
public static void main(String[] args) {
}
@Test
public void jdbcConnect01() throws SQLException{
//方式一:静态加载
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC";
Properties properties = new Properties();
properties.setProperty("user", "root");//用户
properties.setProperty("password", "123456");//密码
Connection connect = driver.connect(url, properties);
System.out.println("方式1 " + connect);
connect.close();
}
@Test
public void jdbcConnect02() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
//方式二:使用反射加载Diver类 动态加载,更加灵活
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC";
Properties properties = new Properties();
properties.setProperty("user", "root");//用户
properties.setProperty("password", "123456");//密码
Connection connect = driver.connect(url, properties);
System.out.println("方式2 " + connect);
connect.close();
}
@Test
public void jdbcConnect03()throws Exception{
//方式三:使用DriverManager替代Manager进行 统一管理
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC";
String user = "root";
String password = "123456";
DriverManager.registerDriver(driver);//注册Driver驱动
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println("方式3 " + connection);
connection.close();
}
@Test
public void jdbcConnect04()throws Exception{
//方式四:使用Class.forName自动完成注册驱动,简化代码
// 在加载Driver类时,自动完成了注册
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");//可以省略,但是建议写上
String url = "jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println("方式4 " + connection);
connection.close();
}
@Test
public void jdbcConnect05()throws Exception{
//方式五:在第四种上优化,设置配置文件,更加灵活
// ------使用的最多------
// 从配置文件拿到信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
Class<?> aClass = Class.forName(driver);//可以省略,但是建议写上
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println("方式5 " + connection);
connection.close();
}
}
三、ResultSet
- 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成;
- ResultSet 对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前;
- next 方法将光标移动到下一行,并且由于在 ResultSet 对象中没有更多行时返回 false ,因此可以在 while 循环中使用循环来遍历结果集。
package JDBC_Exercise;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Date;
import java.util.Properties;
/**
* @author 神代言
* @version 1.0
*/
public class ResultSet_ {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
// 1.注册驱动
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
// 2.得到连接
Class<?> aClass = Class.forName(driver);//可以省略,但是建议写上
Connection connection = DriverManager.getConnection(url, user, password);
// 3.得到statement
Statement statement = connection.createStatement();
// 4.组织SQL语句
String sql = "select id,name,sex,borndate,phone from actor";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String sex = resultSet.getString(3);
Date date = resultSet.getDate(4);
String phone = resultSet.getString(5);
System.out.println(id + "\t" + name + "\t" + sex
+ "\t" + date + "\t" + phone);
}
// 5.关闭
resultSet.close();
statement.close();
connection.close();
}
}
四、Statement与PreparedStatement
4.1 Statement存在SQL注入
演示SQL注入:
package JDBC_Exercise;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
public class Statement_ {
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
System.out.print("请输入名字:");
/* next 接收到 空格就结束
如果希望看到sql注入,则这里需要使用nextLine */
String admin_name = scanner.nextLine();
System.out.print("请输入电话:");
String admin_pwd = scanner.nextLine();
//通过properties获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//1.注册驱动
Class.forName(driver);
//2.得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//3.得到statement
Statement statement = connection.createStatement();
//4.组织一个sql语句
String sql = "select name,phone from actor " +
"where name ='" + admin_name + "' and pwd = '" + admin_pwd + "'";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) { //查询到一条记录,则说明存在
System.out.println("成功");
} else {
System.out.println("失败");
}
resultSet.close();
statement.close();
connection.close();
}
}
结果分析:名字和电话显然是错的,但是这条语句还能成功,显然存在SQL、注入。
请输入名字:1' or
请输入电话:or '1'= '1
成功
4.2 PreparedStatement
- 不再使用 + 拼接SQL语句,减少语法错误
- 有效的解决了sql注入问题
- 大大减少了编译次数,效率较高
package JDBC_Exercise;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.Scanner;
/**
* @author 神代言
* @version 1.0
*/
public class PreparedStatement_ {
public static void main(String[] args) throws Exception{
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
System.out.print("请输入管理员的名字:");//next 接收到 空格就结束
String admin_name = scanner.nextLine();//如果希望看到sql注入,则这里需要使用nextLine
System.out.print("请输入管理员的密码:");
String admin_pwd = scanner.nextLine();
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
//3.得到PreparedStatement
// 3.1 组织一个sql语句 ?:相当于占位符
String sql = "select name ,pwd from admin where NAME =? and pwd =?";
// 3.2 preparedStatement 实现了 PreparedStatement 接口的一个实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 3.3 给? 赋值
preparedStatement.setString(1,admin_name);
preparedStatement.setString(2,admin_pwd);
//4.执行
// 这里执行 executeQuery ,不要写sql,之前已经赋值
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){ //查询到一条记录,则说明该管理存在
System.out.println("成功");
}else {
System.out.println("失败");
}
//5.关闭
resultSet.close();
preparedStatement.close();
connection.close();
}
}
五、JDBC API
六、事务
不使用事务
@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.创建PrepareStatement 对象
try {
connection = JDBCUtils.getConnection();//在默认情况下,connection对象是默认自动提交
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();//执行第一条sql,并且自动提交commit
int i = 1/0; // 会抛出异常,会导致前一条语句执行成功,后一条语句不执行
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();//执行第二条sql,并且自动提交commit
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//关闭资源
JDBCUtils.close(null,preparedStatement,connection);
}
}
使用事务
@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.创建PrepareStatement 对象
try {
connection = JDBCUtils.getConnection();//在默认情况下,connection对象是默认自动提交
//将connection 设置为不自动提交
connection.setAutoCommit(false);//相当于开启了事务
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();//执行第一条sql
int i = 1/0; // 抛出异常,则回滚
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();//执行第二条sql
//若无异常,直接提交事务
connection.commit();
} catch (SQLException e) {
//这里我们可以进行回滚,撤销即将执行的sql
//默认回滚到事务开始的状态
System.out.println("执行发生了异常,撤销执行的sql");
try {
connection.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException(e);
} finally {
//关闭资源
JDBCUtils.close(null,preparedStatement,connection);
}
}
七、批处理
- 当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率
- JDBC的批量处理语句包括下面方法:
JDBC连接MySQL时,如果要使用批处理功能,请再url中加参数?rewriteBatchedStatements=true
addBatch():添加需要批量处理的SQL语句或参数;
executeBatch():执行批量处理语句;
clearBatch():清空批处理包的语句; - 批处理往往和PreparedStatement一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高
代码演示
package JDBC_Exercise;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
/**
* @author 神代言
* @version 1.0
*/
public class Batch_ {
@Test
public void noBatch() throws Exception {//3000ms
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++) {
preparedStatement.setString(1,"jack" + i);
preparedStatement.setString(2,"666");
preparedStatement.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("传统的方式 耗时=" + (end - start));
JDBCUtils.close(null,preparedStatement,connection);
}
//使用批量处理添加数据
@Test
public void batch() 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++) {
preparedStatement.setObject(1,"jack" + i);
preparedStatement.setObject(2,"666");
//将sql语句加入到批处理包中 -> 源码
preparedStatement.addBatch();
//当有1000条记录时,再批量执行
if ((i + 1) % 1000 == 0){
preparedStatement.executeBatch();
//清空
preparedStatement.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("batch的方式 耗时=" + (end - start));
JDBCUtils.close(null,preparedStatement,connection);
}
}
八、数据库的连接方式
8.1 传统的连接方式
8.2 数据库的连接池技术
这里懒得写了,参考了这篇博客
8.2.1 C3P0
前期工作:配置文件
将C3P0的jar包按照上述jdbc的方式导入项目中,并且书写c3p0-config.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--配置连接池mysql-->
<named-config name="c3p0_mysql"> <!--数据源名称代表连接池 这里等会连接时候使用-->
<property name="driverClass">com.mysql.jdbc.Driver</property> <!--驱动类-->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mysql?characterEncoding=utf-8&serverTimezone=UTC</property> <!--url-->
<property name="user">root</property> <!--用户名-->
<property name="password"></property> <!--密码-->
<property name="initialPoolSize">10</property> <!--初始化的连接数-->
<property name="acquireIncrement">5</property> <!--每次增长的连接数-->
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">60</property> <!--最大的连接数-->
<property name="minPoolSize">10</property> <!--最小的连接数-->
</named-config>
<!--配置连接池2,可以配置多个-->
</c3p0-config>
方式一:
@Test
public void testC3P0_01() throws Exception{
//1.创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2.通过配置文件mysql.propertie获取相关的信息
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 5000ci操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
//这个方法是从 DataSource 接口实现的
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 连接5000次mysql 耗时=" + (end-start));
}
方式二:
//方式二:使用配置文件模板来完成
//1. 将c3p0 提供的 xml 文件拷贝到src目录下
//2. 该文件指定了连接数据库和连接池的相关参数
@Test
public void testC3P0_02() throws SQLException {
ComboPooledDataSource com = new ComboPooledDataSource("c3p0_mysql");
//测试5000次连接MySQL
long start = System.currentTimeMillis();
System.out.println("开始执行");
for (int i = 0; i < 5000; i++) {
Connection connection = com.getConnection();
//System.out.println("连接成功---");
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c390的第二种方式 耗时=" + (end-start));
}
8.2.2 德鲁伊连接池
前期工作:配置文件
将driud的jar包按照上述jdbc的方式导入项目中,并且书写druid.properties文件
演示
@Test
public void testDruid() throws Exception {
//1. 加入jar 包
//2. 加入配置文件druid.properties
//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.close();
}
//druid连接池 操作5000次 耗时268
long end = System.currentTimeMillis();
System.out.println("druid连接池 耗时" + (end - start));//耗时332
}
九、自定义连接数据库的工具类
9.1 自定义JDBCUtils工具类
JDBCUtils
package JDBC_Exercise;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author 神代言
* @version 1.0
* JDBC连接数据库的工具类
*/
public class JDBCUtils {
//定义相关的属性(4个),因为因为只需要一次,因此,我们做成static
private static String user;//用户名
private static String password;//密码
private static String url;//url
private static String driver;//驱动名字
//在static代码块中去初始化
static {
Properties properties = new Properties();
try {
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 或者 PrepareStatement
3.Connection
4.如果需要关闭资源,就传入对象否则传入null
*/
public static void close(ResultSet resultSet, Statement statement, Connection connection){
//判断是否为null
try {
if (resultSet!=null){
resultSet.close();
}
if (statement!=null){
statement.close();
}
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
//将编译异常转成运行异常抛出
throw new RuntimeException(e);
}
}
}
测试JDBCUtils
package JDBC_Exercise;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
/**
* @author 神代言
* @version 1.0
*/
public class JDBCUtils_Use {
@Test
public void testDML(){
//1.得到连接
Connection connection = null;
//2.组织sql语句
String sql = "update actor set name = ? where id = ?";
//delete 与 insert
PreparedStatement preparedStatement = null;
//3.创建PrepareStatement 对象
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给占位符赋值
preparedStatement.setObject(1,"周星驰");
preparedStatement.setObject(2,1);
//执行
preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//关闭资源
JDBCUtils.close(null,preparedStatement,connection);
}
}
@Test
public void testSelect(){
//1.得到连接
Connection connection = null;
//2.组织sql语句
String sql = "select * from actor";
//delete 与 insert
PreparedStatement preparedStatement = null;
ResultSet set = null;
//3.创建PrepareStatement 对象
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
//执行
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) {
throw new RuntimeException(e);
} finally {
//关闭资源
JDBCUtils.close(set,preparedStatement,connection);
}
}
}
9.2 使用Druid实现JDBCUtils工具类
编写工具类
package JDBC_Exercise;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author 神代言
* @version 1.0
*/
public class JDBCUtilsByDriud {
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();
}
// 关闭连接
// 注意:Driud连接池技术在这里不是断掉连接,而是把连接放回连接池
/*
1.ResultSet 结果集
2.Statement 或者 PrepareStatement
3.Connection
4.如果需要关闭资源,就传入对象否则传入null
*/
public static void close(ResultSet resultSet, Statement statement, Connection connection){
//判断是否为null
try {
if (resultSet!=null){
resultSet.close();
}
if (statement!=null){
statement.close();
}
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
//将编译异常转成运行异常抛出
throw new RuntimeException(e);
}
}
}
测试工具类
package JDBC_Exercise;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
/**
* @author 神代言
* @version 1.0
* 测试JDBCUtilsByDriud工具类
*/
public class JDBCUtilsByDriud_Use {
@Test
public void testDML(){
//1.得到连接
Connection connection = null;
//2.组织sql语句
String sql = "update actor set name = ? where id = ?";
//delete 与 insert
PreparedStatement preparedStatement = null;
//3.创建PrepareStatement 对象
try {
connection = JDBCUtilsByDriud.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给占位符赋值
preparedStatement.setObject(1,"周星驰");
preparedStatement.setObject(2,1);
//执行
preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//关闭资源
JDBCUtilsByDriud.close(null,preparedStatement,connection);
}
}
@Test
public void testSelect(){
//1.得到连接
Connection connection = null;
//2.组织sql语句
String sql = "select * from actor";
//delete 与 insert
PreparedStatement preparedStatement = null;
ResultSet set = null;
//3.创建PrepareStatement 对象
try {
connection = JDBCUtilsByDriud.getConnection();
preparedStatement = connection.prepareStatement(sql);
//执行
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) {
throw new RuntimeException(e);
} finally {
//关闭资源
JDBCUtilsByDriud.close(set,preparedStatement,connection);
}
}
}
十、使用开源的DBUtils工具类操作数据库
10.1 问题引入
问题引入
- resultSet与connection是关联的,关闭connection后,resultSet结果集无法使用;
- resultSet不利于数据管理。
如何解决上述问题?使用原始的方法,创建一个Actor实体类,将resultSet结果集放入一个Actor数组中。
@Test
public ArrayList<Actor> testSelectToArrayList(){
System.out.println("我们使用Druid方式完成");
//1.得到连接
Connection connection = null;
//2.组织sql语句
String sql = "select * from actor";
//delete 与 insert
PreparedStatement preparedStatement = null;
ResultSet set = null;
ArrayList<Actor> list = new ArrayList<Actor>();
//3.创建PrepareStatement 对象
try {
connection = JDBCUtilsByDruid.getConnection();
System.out.println(connection.getClass());//运行类型
preparedStatement = connection.prepareStatement(sql);
//执行
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");
//把得到的result 的记录,封装到 Actor 对象,放入到list集合
list.add(new Actor(id,name,sex,borndate,phone));
}
System.out.println("list集合数据=" + list);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//关闭资源
JDBCUtilsByDruid.close(set,preparedStatement,connection);
}
return list;
}
但是,显然这样还是很麻烦,因此,接下来介绍Apache提供的开源工具类-DBUtils。
10.2 使用DBUtils+数据连接池(Driud)方式,操作数据库
前期工作:配置文件
将DBUtils和driud的jar包按照上述jdbc的方式导入项目中,并且书写druid.properties文件
Actor实体类
package JDBC_Exercise;
import java.util.Date;
/**
* @author 神代言
* @version 1.0
*/
public class Actor {
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;
}
@Override
public String toString() {
return "Actor{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", borndate=" + borndate +
", 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;
}
}
演示DBUtils+数据连接池(Driud)
package JDBC_Exercise;
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.ScalarHandler;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* @author 神代言
* @version 1.0
*/
public class DBUtils_Use {
// 使用apache-DBUtils工具类+driud连接池方式,完成对数据库的操作
@Test
// 返回结果是多行多列的情况 BeanListHandler
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 >= ?";
//解读 query 方法就是执行一个sql 语句,得到resultset --封装--> ArrayList 集合中,然后返回集合
// query 底层会将resultset和PreparedStatement关闭
// 1)connection:连接
// 2)sql:执行的sql语句
// 3)new BeanListHandler<>(actor.class):将resultset -> actor对象 -> 封装到 ArrayList
// 底层使用反射机制 去获取actor类的属性,然后进行封装
// 4)1 就是给sql语句中?赋值的,可以有多个值,因为是可变参数
List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
System.out.println("输出集合的的信息");
for(Actor actor : list){
System.out.println(actor);
}
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
}
@Test
// 返回结果是单行多列的情况 BeanHandler
public void testQuerySingle() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "select * from actor where id = ?";
// 单行所以使用的是BeanHandler
Actor query = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 4);
System.out.println(query);
JDBCUtilsByDruid.close(null,null,connection);
}
@Test
// 返回结果是单行单列的情况 ScalarHandler
public void testScalar() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "select name from actor where id = ?";
//
Object query = queryRunner.query(connection, sql, new ScalarHandler(), 1);
System.out.println(query);
JDBCUtilsByDruid.close(null,null,connection);
}
@Test
// 完成DML(update,insert,delete)
public void testDML() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "update actor set name = ? where id = ?";
// 返回结果是受影响的行数
int affectedRow = queryRunner.update(connection, sql, "张三丰", 1);
System.out.println(affectedRow);
JDBCUtilsByDruid.close(null,null,connection);
}
}
十一、【最终版】BasicDao+Druid
上述apache-DBUtils+Druid 简化了JDBC开发,但还有不足:
- SQL 语句是固定,不能通过参数传入,通用性不好,需要进行改进,更方便执行 增删改查;
- 对于select 操作,如果有返回值,返回类型不能固定,需要使用泛型;
- 将来的表很多,业务需求复杂,不可能只靠一个Java类完成
框架结构:
- com.utils:编写工具类JDBCUtilsByDruid(用于连接/关闭数据库)
- com.domain:java bean实体类
- com.dao:存放***Dao和BasicDao(其他DAO的父类)(DAO数据访问对象,用于操作数据库的增删改查)
- com.test:编写测试类
Actor实体类和JDBCUtilsByDruid工具类上面有,这里就不演示了
BasicDao
package JDBC_Exercise.dao;
import JDBC_Exercise.utils.JDBCUtilsByDruid;
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.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* @author 神代言
* @version 1.0
* 其他Dao的父类,是Dao的共性
*/
public class BasicDao<T> { //泛型
private QueryRunner qr = new QueryRunner();
// 更新
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 classz 传入一个类的class对象,例如:Actor.class
* @param parameters sql语句中的?对应的参数
* @return
*/
public List<T> queryMultiply(String sql, Class<T> classz, Object... parameters) {
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new BeanListHandler<T>(classz), 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
package JDBC_Exercise.dao;
import JDBC_Exercise.domain.Actor;
/**
* @author 神代言
* @version 1.0
* 继承BasicDao,并将自己的class类型传入实现泛型
*/
public class ActorDao extends BasicDao<Actor> {
// 继承BasicDao的方法
// 也可以编写自己的dao方法
}
测试Dao
package JDBC_Exercise.test;
import JDBC_Exercise.dao.ActorDao;
import JDBC_Exercise.domain.Actor;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* @author 神代言
* @version 1.0
*/
public class TestDao {
@Test
public void testActorDao(){
//1.查询多行记录
ActorDao actorDao = new ActorDao();
List<Actor> actors = actorDao.queryMultiply("select * from actor where id >= ?", Actor.class, 1);
for (Actor actor : actors) {
System.out.println(actor);
}
//2.查询单行记录
Actor actor = actorDao.querySingle("select * from actor where id = ?", Actor.class, 1);
System.out.println("查询单行结果");
System.out.println(actor);
//3.查询单行单列
Object o = actorDao.queryScalar("select name from actor where id =?", 3);
System.out.println("====查询单行单列值=======");
System.out.println(o);
//4.dml 操作
int update = actorDao.update("insert into actor values(null,?,?,?,?)", "岳小琳", "女", "1999-06-01", "13266");
System.out.println(update > 0?"执行成功":"执行没有影响");
}
}
特别说明
本文章是个人整理的学习笔记,参考b站韩顺平老师的课程(【零基础 快速学Java】韩顺平 零基础30天学会Java)。老师讲的非常好,有兴趣的可以去看一下。