少女祈祷中 . . .|

园龄:粉丝:关注:

2023-02-09 17:00阅读: 36评论: 0推荐: 0

JDBC和数据库连接池

一、概述


JDBC( 为访问不同的数据库提供了统一的接口 , 为使用者屏蔽了细节问题 。
Java 程序员使用 JDBC, 可以连接任何提供了JDBC( 驱动程序的数据库系统 , 从而完成对数据库的各种操作 。

1.JDBC的基本原理图 [重要]

image-20230209092327351

JDBC接口:

/**
* @Author: XIYAN
* @Date: 2023/2/9 9:27
* @注释:规定的jdbc接口[模拟]
*/
public interface JdbcInterface {
/**
* 连接
*
* @return
*/
Object getConnection();
/**
* 数据的增删改查
*/
void crud();
/**
* 关闭连接
*/
void close();
}

实现类:

/**
* @Author: XIYAN
* @Date: 2023/2/9 9:32
* @注释:Mysql实现jdbc接口
*/
public class MysqlJdbcImpl implements JdbcInterface {
/**
* 连接
*
* @return
*/
@Override
public Object getConnection() {
return "得到Mysql连接";
}
/**
* 数据的增删改查
*/
@Override
public void crud() {
System.out.println("实现数据的增删改查");
}
/**
* 关闭连接
*/
@Override
public void close() {
System.out.println("关闭连接");
}
}

测试:

public class Test {
public static void main(String[] args) {
//创建jdbc对象(通过接口调用实现类,动态绑定[多态])
JdbcInterface jdbcInterface = new MysqlJdbcImpl();
//创建Mysql连接
jdbcInterface.getConnection();
//对数据进行操作
jdbcInterface.crud();
//关闭Mysql连接
jdbcInterface.close();
}
}

2.JDBC带来的好处

JDBC是Java提供一套用于数据库操作的接口 API,Java程序员只需要面向这套接口编程即可。 不同的数据库厂商 , 需要针对这套接口 , 提供不同实现 。

3.JDBC API

image-20230209113339548

image-20230211094323974

二、JDBC快速入门


1.使用步骤

# 注册驱动 - 加载Driver 类
# 获取连接 - 得到 Connection
# 执行增删改查 - 发送SQL给mysql 执行
# 释放资源 - 关闭相关连接

2.模拟 JDBC

image-20230209104938970

完整代码:

/**
* @Author: XIYAN
* @Date: 2023/2/9 10:10
* @注释:第一个Jdbc程序,完成简单的操作
*/
public class Test {
public static void main(String[] args) throws SQLException {
/*
前置操作:
创建一个文件夹(名字随意)
将jar包拷贝到该目录下,右键添加到库
*/
//1.注册驱动(创建Driver对象)
Driver driver = new Driver();
//2.得到连接(本质:socket连接)
//"jdbc连接协议://主机或IP地址:端口号/数据库名"
String url = "jdbc:mysql://localhost:3306/hsp_jdbc";
//3.将用户名和密码放入Properties对象中
Properties properties = new Properties();
/*
说明:
user和password是规定好的不能修改
*/
//设置用户
properties.setProperty("user", "root");
//设置密码
properties.setProperty("password", "123456");
//4.连接数据库
Connection connection = driver.connect(url, properties);
//5.操作数据库
//创建sql语句
String sql = "insert into actor values(null,'张三','男','2000-09-07','110')";
//执行sql语句并返回结果对象
Statement statement = connection.createStatement();
//返回受影响的行数(返回大于0的数字即执行成功,返回0则执行失败)
int rows = statement.executeUpdate(sql);
System.out.println(rows > 0 ? "sql执行成功" : "sql执行失败");
//6.关闭连接
statement.close();
connection.close();
}
}

3.获取数据库连接的 5 种方式

3-1.方式一,使用Driver对象
/**
* @Author: XIYAN
* @Date: 2023/2/9 11:43
* @注释:方式一
*/
public class Manner1 {
public static void main(String[] args) throws SQLException {
//获取Driver实现类对象
Driver driver = new Driver();
//定义jdbc连接地址
String url="jdbc:mysql://localhost:3306/hsp_jdbc";
//创建Properties对象
Properties properties = new Properties();
//设置用户名
properties.setProperty("user","root");
//设置密码
properties.setProperty("password","123456");
//连接数据库
Connection connect = driver.connect(url, properties);
}
}

缺点:

  • 会直接使用com.mysql.jdbc.Driver(),属于静态加载,灵活性差,依赖强
3-2.方式二,减少依赖增强灵活性
/**
* @Author: XIYAN
* @Date: 2023/2/9 11:51
* @注释:方式二
*/
public class Manner2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用反射加载Driver类(动态加载,更加的灵活,减少依赖性)
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) aClass.newInstance();
//定义jdbc连接地址
String url = "jdbc:mysql://localhost:3306/hsp_jdbc";
//创建Properties对象
Properties properties = new Properties();
//设置用户名
properties.setProperty("user", "root");
//设置密码
properties.setProperty("password", "123456");
//连接数据库
Connection connect = driver.connect(url, properties);
}
}
3-3.方式三,使用DriverManager替代Driver进行统一管理
/**
* @Author: XIYAN
* @Date: 2023/2/9 11:51
* @注释:方式三
*/
public class Manner3 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用反射加载Driver类(动态加载,更加的灵活,减少依赖性)
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) aClass.newInstance();
//定义jdbc连接地址、用户名和密码
String url = "jdbc:mysql://localhost:3306/hsp_jdbc";
String user = "root";
String password = "123456";
//注册Driver驱动
DriverManager.registerDriver(driver);
//连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
}
}

缺点:

  • 需手动注册Driver驱动
3-4.使用Class.forName自动完成注册驱动,简化代码(推荐使用
/**
* @Author: XIYAN
* @Date: 2023/2/9 11:51
* @注释:方式四
*/
public class Manner4 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用反射加载Driver类并在加载时自动完成Driver驱动的注册
Class.forName("com.mysql.jdbc.Driver");
//定义jdbc连接地址、用户名和密码
String url = "jdbc:mysql://localhost:3306/hsp_jdbc";
String user = "root";
String password = "123456";
//连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
}
}

补充:

- mysqL 驱动5.1.6以后可以无需 CLass.forName("com.mysql.jdbc.Driver");
- 从 jdk1.5以后使用了 jdbc4,不再需要显示调用 class.forName() 注册驱动而是自动调用驱动jar包下 META-INF\services\java.sql.Driver 文本中的类名称去注册

# 建议还是写上 CLass.forName("com.mysql.jdbc.Driver"), 更加明确

3-5.使用配置文件,连接数据库更灵活

jdbc.properties:

url=jdbc:mysql://localhost:3306/hsp_jdbc
user=root
password=123456
driver=com.mysql.jdbc.Driver

Manner5:

/**
* @Author: XIYAN
* @Date: 2023/2/9 14:53
* @注释:方式五
*/
public class Manner5 {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
//通过Properties对象获取配置文件信息
Properties properties = new Properties();
//读取文件
properties.load(new FileInputStream("src/com/hspedu/myjdbc/demo3/resource/jdbc.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);
}
}

4.创建、插入、修改、删除(练习)

题目:

image-20230209153723801

/**
* @Author: XIYAN
* @Date: 2023/2/9 14:53
* @注释:使用方式五完成数据表的创建、插入、修改、删除
*/
public class Manner5 {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
//通过Properties对象获取配置文件信息
Properties properties = new Properties();
//读取文件
properties.load(new FileInputStream("src/com/hspedu/myjdbc/demo3/resource/jdbc.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);
//操作数据库
//创建news表
String createSql = "create table news (id int not null primary key auto_increment," +
"content varchar(100)" +
")";
//向news表插入数据
String insertSql = "insert into news values(null,'dyt')," +
"(null,'det')," +
"(null,'dst')," +
"(null,'dsit')," +
"(null,'dwt')";
//更新news表里id为1的信息内容
String updateSql = "update news set content='news' where id=1";
//删除news表里id为3的记录
String deleteSql = "delete from news where id=3";
//创建Statement对象
Statement statement = connection.createStatement();
//执行sql语句
statement.executeUpdate(createSql);
int insert = statement.executeUpdate(insertSql);
System.out.println(insert > 0 ? "sql执行成功" : "sql执行失败");
int update = statement.executeUpdate(updateSql);
System.out.println(update > 0 ? "sql执行成功" : "sql执行失败");
int delete = statement.executeUpdate(deleteSql);
System.out.println(delete > 0 ? "sql执行成功" : "sql执行失败");
//关闭连接
statement.close();
connection.close();
}
}

三、ResultSet[结果集]


1.基本介绍

表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
ResultSet 对象保持一个光标指向其当前的数据行 。 最初,光标位于第一行之前
next() 方法将光标移动到下一行 , 并且由于在 ResultSet 对象中没有更多行时返回false ,因此可以在 while 循环中使用循环来遍历结果集

image-20230211094907675

image-20230209163845428

2.应用实例:

image-20230209165220391

/**
* @Author: XIYAN
* @Date: 2023/2/9 16:06
* @注释:eclect语句返回ResultSet并取出结果集
*/
public class Result {
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
//1.通过Properties对象获取配置文件信息
Properties properties = new Properties();
//读取文件
properties.load(new FileInputStream("src/com/hspedu/myjdbc/demo3/resource/jdbc.properties"));
//获取用户名
String user = properties.getProperty("user");
//获取密码
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//2.注册驱动(可省略)
Class.forName(driver);
//3.连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
//4.创建Statement对象
Statement statement = connection.createStatement();
//5.操作数据库
//查询actor表
String selectSql = "select id,name,sex,borndate from actor";
//6.执行sql语句并返回单个ResultSet对象
ResultSet resultSet = statement.executeQuery(selectSql);
//7.使用while循环取出数据 next--让光标下移,当没有数据行时返回false
while (resultSet.next()) {
//获取该行的第一列数据~~~
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String sex = resultSet.getString(3);
Date borndate = resultSet.getDate(4);
System.out.println(id + "\t" + name + "\t" + sex + "\t" + borndate);
}
//8.关闭连接
resultSet.close();
statement.close();
connection.close();
}
}

四、Statement


1.基本介绍

Statement 对象用于执行静态 SQL 语句并返回其生成的结果的对象
在连接建立后 , 需要对数据库进行访问 , 执行 命名或是 SQL 语句,可以通过

- Statement [ 存在 SQL 注入 ]
- PreparedStatement [ 预处理 ]
- CaIIabIeStatement [ 存储过程 ]

Statement 对象执行 SQL 语句 ,存在 SQL 注入风险

image-20230210101759654

例:

select * from admin where `name`='1' or 'and pwd=' or '1'='1';

SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查 , 而在用户输
入数据中注入非法的 SQL 语句段或命令 , 恶意攻击数据库 。
要防范 SQL 注入 , 只要用 PreparedStatement( 从 Statement 扩展而来 ) 取
代 Statement 就可以了

2.java程序模拟SQL注入:

/**
* @Author: XIYAN
* @Date: 2023/2/10 9:20
* @注释:java演示SQL注入
*/
public class Statement {
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
Scanner input = new Scanner(System.in);
System.out.print("请输入用户名:");
//使用next()时当收到空格或者'时自动结束
//如果希望看到SQL注入,这里需要使用nextLine()
String name = input.nextLine();
System.out.print("请输入密码:");
String pwd = input.nextLine();
//1.通过Properties对象获取配置文件信息
Properties properties = new Properties();
//读取文件
properties.load(new FileInputStream("src/com/hspedu/myjdbc/demo3/resource/jdbc.properties"));
//获取用户名
String user = properties.getProperty("user");
//获取密码
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//2.注册驱动(可省略)
Class.forName(driver);
//3.连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
//4.创建Statement对象
java.sql.Statement statement = connection.createStatement();
//5.操作数据库
//查询actor表
String selectSql = "select name,pwd from admin where name='" + name + "'and pwd='" + pwd + "'";
//6.执行sql语句并返回单个ResultSet对象
ResultSet resultSet = statement.executeQuery(selectSql);
if (resultSet.next()) {
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
//8.关闭连接
resultSet.close();
statement.close();
connection.close();
}
}

终端:

请输入用户名:1' or
请输入密码: or '1'='1

五、PreparedStatement


1.基本介绍

PreparedStatement 执行的 SQL 语句中的参数用问号 (?)来表示 ,调用PreparedStatement 对象的 setXxx()方法来设置这些参数.setXxx()方法有两个参数 ,第一个参数是要设置的 SQL 语句中的参数的索引( 从 1 开始 ), 第二个是设置的 SQL 语句中的参数的值
调用 executeQuery() , 返回 ResuItSet 对象
调用 executeUpdate() :执行更新 ,包括增 、删 、修改

2.预处理的好处

* 不再使用 + 拼接 sql 语句 , 减少语法错误
* 有效的解决了 sql 注入问题 !
* 大大减少了编译次数 , 效率较高

3.应用实例:

/**
* @Author: XIYAN
* @Date: 2023/2/10 9:20
* @注释:java演示SQL注入
*/
public class PerparStatement {
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
Scanner input = new Scanner(System.in);
System.out.print("请输入用户名:");
//使用next()时当收到空格或者'时自动结束
//如果希望看到SQL注入,这里需要使用nextLine()
String name = input.nextLine();
System.out.print("请输入密码:");
String pwd = input.nextLine();
//1.通过Properties对象获取配置文件信息
Properties properties = new Properties();
//读取文件
properties.load(new FileInputStream("src/com/hspedu/myjdbc/demo3/resource/jdbc.properties"));
//获取用户名
String user = properties.getProperty("user");
//获取密码
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//2.注册驱动(可省略)
Class.forName(driver);
//3.连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
//4.操作数据库
//查询actor表(sql语句里的?相当于占位符)
String selectSql = "select name,pwd from admin where name=? and pwd=?";
//5.创建PerparedStatement对象(PerparedStatement对象实现了PerparedStatement接口的实现类的对象)
PreparedStatement preparedStatement = connection.prepareStatement(selectSql);
//6.设置占位符(?)的位置及获取的字段名
preparedStatement.setString(1, name);
preparedStatement.setString(2, pwd);
//6.执行sql语句并返回单个ResultSet对象
/*
执行select语句使用executeQuery()
执行update、insert、delete语句使用executeUpdate()
*/
//executeQuery()里的参数无需再处理
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
//8.关闭连接
resultSet.close();
preparedStatement.close();
connection.close();
}
}

4.使用PerparedStatement创建、插入、修改、删除、查询(练习)

题目:

image-20230210150524400

/**
* @Author: XIYAN
* @Date: 2023/2/10 15:08
* @注释:使用PerparedStatement创建、插入、修改、删除、查询
*/
public class PerparedStatementDml {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Scanner scanner = new Scanner(System.in);
//创建Properties对象并获取配置文件信息
Properties properties = new Properties();
properties.load(new FileInputStream("src/com/hspedu/myjdbc/demo3/resource/jdbc.properties"));
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
//注册Driver驱动
Class.forName(driver);
//连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
//操作数据库
//创建admin表
String createSql = "create table admin (id int not null primary key auto_increment,username varchar(10),pwd varchar(10))";
int createTable = connection.prepareStatement(createSql).executeUpdate();
//向admin表插入数据
String insertSql = "insert into admin values(null,?,?)";
System.out.print("请输入用户名:");
String username = scanner.nextLine();
System.out.print("请输入密码:");
String pwd = scanner.nextLine();
PreparedStatement insPs = connection.prepareStatement(insertSql);
insPs.setString(1, username);
insPs.setString(2, pwd);
int insertData = insPs.executeUpdate();
System.out.println(insertData > 0 ? "插入数据成功" : "执行失败!");
//修改admin表中username为tom的记录
String updateSql = "update admin set username=? where username=?";
System.out.print("请输入需要修改的用户名:");
String oldname = scanner.nextLine();
System.out.print("请输入新的用户名:");
String newname = scanner.nextLine();
PreparedStatement updPs = connection.prepareStatement(updateSql);
updPs.setString(1, newname);
updPs.setString(2, oldname);
int updateData = updPs.executeUpdate();
System.out.println(updateData > 0 ? "更新数据成功" : "执行失败!");
//删除admin中的一条记录
String deleteSql = "delete from admin where id=?";
System.out.print("请输入需要删除的id:");
int id = scanner.nextInt();
PreparedStatement delPs = connection.prepareStatement(deleteSql);
delPs.setInt(1, id);
int deleteData = delPs.executeUpdate();
System.out.println(deleteData > 0 ? "删除记录成功" : "执行失败!");
//查询admin表中的所有记录
String selectSql = "select id,username,pwd from admin";
ResultSet resultSet = connection.prepareStatement(selectSql).executeQuery();
while (resultSet.next()) {
int ids = resultSet.getInt(1);
String usernames = resultSet.getString(2);
String pwds = resultSet.getString(3);
System.out.println(ids + "\t" + usernames + "\t" + pwds);
}
}
}

六、封装 JDBCUtils


1.说明

在jdbc操作中 ,获取连接和释放资源是经常使用到 ,可以将其封装成JDBC连接数据库的工具类 (JdbcUtiIs)

image-20230211104002602

2.实际使用使用工具类 JDBCUtils

2-1.JdbcUtils
/**
* @Author: XIYAN
* @Date: 2023/2/11 10:07
* @注释:完成mysql数据库的连接与关闭
*/
public class JdbcUtils {
//1.定义属性(需定义成static静态属性)
//用户名
private static String user;
//密码
private static String password;
//数据库连接点
private static String url;
//驱动
private static String driver;
//2.初始化(static)
static {
//创建Properties对象
Properties properties = new Properties();
try {
//读取配置文件信息
properties.load(new FileInputStream("src/com/hspedu/resource/jdbc.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);
}
}
//3.连接数据库,返回一个Connection连接对象
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//4.关闭连接(如果需要关闭连接,就传入需要关闭的对象,否则传入null)
/*
可能需要关闭的对象:
1.关闭ResultSet结果集
2.关闭Sataement或PreparedStatement
3.关闭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);
}
}
}
2-2.更新记录
/**
* 更新记录
*/
public static void upDate() {
//1.创建Connection对象
Connection connection = null;
//2.创建PeoparedStatement对象
PreparedStatement preparedStatement = null;
//3.数据库操作
String updateSql = "update actor set name=? where id=?";
try {
//4.得到连接
connection = JdbcUtils.getConnection();
//5.预处理SQL语句
preparedStatement = connection.prepareStatement(updateSql);
//6.处理占位符
preparedStatement.setString(1, "志昂");
preparedStatement.setInt(2, 2);
//执行SQL语句并返回影响行数
int update = preparedStatement.executeUpdate();
System.out.println(update > 0 ? "更新记录成功" : "执行失败");
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//7.关闭连接
JdbcUtils.close(null, preparedStatement, connection);
}
}
2-3.插入记录
/**
* 插入记录
*/
public static void inSert() {
Scanner scanner = new Scanner(System.in);
Connection connection = null;
PreparedStatement preparedStatement = null;
String insertSql = "insert into admin values(null,?,?)";
try {
connection = JdbcUtils.getConnection();
preparedStatement = connection.prepareStatement(insertSql);
boolean flag = true;
while (flag) {
System.out.print("请输入用户名:");
String user = scanner.nextLine();
preparedStatement.setString(1, user);
System.out.print("请输入密码");
String pwd = scanner.nextLine();
preparedStatement.setString(2, pwd);
int insert = preparedStatement.executeUpdate();
System.out.println(insert > 0 ? "插入记录成功" : "执行失败");
System.out.print("是否继续添加记录(y/n):");
String w = scanner.next();
if (!"y".equals(w)) {
flag = false;
}
String dubug = scanner.nextLine();
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, preparedStatement, connection);
}
}
2-4.删除记录
/**
* 删除记录
*/
public static void delEte() {
Scanner scanner = new Scanner(System.in);
Connection connection = null;
PreparedStatement preparedStatement = null;
String deleteSql = "delete from admin where username=?";
try {
connection = JdbcUtils.getConnection();
preparedStatement = connection.prepareStatement(deleteSql);
System.out.print("请输入需要删除的用户名:");
String user = scanner.nextLine();
preparedStatement.setString(1, user);
int delete = preparedStatement.executeUpdate();
System.out.println(delete > 0 ? "删除记录成功" : "执行失败");
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, preparedStatement, connection);
}
}
2-5.查询记录
/**
* 查询记录
*/
public static void selEct() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String selectSql = "select id,username,pwd from admin";
try {
connection = JdbcUtils.getConnection();
preparedStatement = connection.prepareStatement(selectSql);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt(1);
String username = resultSet.getString(2);
String pwd = resultSet.getString(3);
System.out.println(id + "\t" + username + "\t" + pwd);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(resultSet, preparedStatement, connection);
}
}

七、数据库连接池


1.传统获取Connection 问题分析

传统的JDBC数据库连接使用 DriverManager 来获取 ,每次向数据库建立连接的时候都要将 Connection 加载到内存中, 再验证IP地址、用户名和密码。需要数据库连接的时候,就向数据库要求一个 ,频繁的进行数据库连接操作将占用很多的系统资源 ,容易造成服务器崩溃 。
每一次数据库连接 ,使用完后都得断开 ,如果程序出现异常而未能关闭 ,将导致数据库内存泄漏 ,最终将导致重启数据库 。
传统获取连接的方式 ,不能控制创建的连接数量 ,如连接过多 ,也可能导致内存泄漏 ,MySQL崩溃 。
解决传统开发中的数据库连接问题 ,可以采用数据库连接池技术(connection pool)

数据库连接池种类

- 1.JDBC 的数据库连接池使用 javax.sqI.DataSource 来表示 ,DataSource只是一个接口 ,该接口通常由第三方提供实现[提供.jar]
- 2.C3P0数据库连接池速度相对较慢稳定性不错 (hibernate, spring)
- 3.DBCP数据库连接池,速度相对C3P0较快 ,但不稳定
- 4.Proxool数据库连接池 ,有监控连接池状态的功能 ,稳定性较C3P0差一点
- 5.BoneCP 数据库连接池速度快
- 6.Druid( 德鲁伊 )是阿里提供的数据库连接池 ,集 DBCP 、C3P0 、Proxool优点于一身的库连接池

image-20230213183909411

2.C3P0应用实例

2-1.相关参数,在程序中指定 user, url , password 等
public void testC3P0_One() throws IOException, PropertyVetoException, SQLException {
//1.创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2.通过配置文件获取连接信息
Properties properties = new Properties();
properties.load(new FileInputStream("src/com/hspedu/resource/jdbc.properties"));
//3.设置配置文件参数
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
//4.给数据源comboPooledDataSource设置相关参数
//注意连接管理是由comboPooledDataSource来管理
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//设置初始化连接数
comboPooledDataSource.setInitialPoolSize(10);
//设置最大连接数
comboPooledDataSource.setMaxPoolSize(50);
//得到连接(getConnection()就是从DataSource实现)
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
2-2.使用配置文件模板来完成

前置操作
1.将C3P0的配置文件放到src目录下
2.设置配置文件的相关信息

c3p0-config.xml:

<c3p0-config>
<named-config name="hsp_jdbc">
<!-- 驱动类 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- url-->
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/hsp_jdbc</property>
<!-- 用户名 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">123456</property>
<!-- 每次增长的连接数-->
<property name="acquireIncrement">5</property>
<!-- 初始的连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最小连接数 -->
<property name="minPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">10</property>
<!-- 可连接的最多的命令对象数 -->
<property name="maxStatements">5</property>
<!-- 每个连接对象可连接的最多的命令对象数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
public void testC3P0_Two() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("hsp_jdbc");
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}

3.Druid(德鲁伊)应用实例

前置操作
1.导入Druid.jar
2.将配置文件导入到src目录下

druid.properties:

#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/hsp_jdbc?rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/girls
username=root
password=123456
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=20
#max wait time (5000 mil seconds)
maxWait=5000
public void test() throws Exception {
//创建Properties对象
Properties properties = new Properties();
properties.load(new FileInputStream("src/druid.properties"));
//创建一个指定对象的数据库连接池(Druid连接池)
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();
connection.close();
}

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


通过德鲁伊数据库连接池获取连接对象

/**
* @Author: XIYAN
* @Date: 2023/2/13 19:41
* @注释:基于Druid数据库连接池的工具类
*/
public class JdbcUtilsByDruid {
//创建连接池对象
private static DataSource dataSource;
//在静态代码块初始化代码
static {
//创建Properties对象
Properties properties = new Properties();
//读取配置文件信息
try {
properties.load(new FileInputStream("src/druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//编写getConnection方法
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//编写colose方法
/*
注:
在数据库连接池技术中并不是真正的关闭连接而是把使用的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);
}
}
}

1.实际使用使用工具类 JDBCUtilsByDruid

1-1.更新记录
/**
* 更新记录
*/
public static void upDate() {
//1.创建Connection对象
Connection connection = null;
//2.创建PeoparedStatement对象
PreparedStatement preparedStatement = null;
//3.数据库操作
String updateSql = "update actor set name=? where id=?";
try {
//4.得到连接
connection = JdbcUtilsByDruid.getConnection();
//5.预处理SQL语句
preparedStatement = connection.prepareStatement(updateSql);
//6.处理占位符
preparedStatement.setString(1, "志昂");
preparedStatement.setInt(2, 2);
//执行SQL语句并返回影响行数
int update = preparedStatement.executeUpdate();
System.out.println(update > 0 ? "更新记录成功" : "执行失败");
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//7.关闭连接
JdbcUtils.close(null, preparedStatement, connection);
}
}
1-2.插入记录
/**
* 插入记录
*/
public static void inSert() {
Scanner scanner = new Scanner(System.in);
Connection connection = null;
PreparedStatement preparedStatement = null;
String insertSql = "insert into admin values(null,?,?)";
try {
connection = JdbcUtilsByDruid.getConnection();
preparedStatement = connection.prepareStatement(insertSql);
boolean flag = true;
while (flag) {
System.out.print("请输入用户名:");
String user = scanner.nextLine();
preparedStatement.setString(1, user);
System.out.print("请输入密码");
String pwd = scanner.nextLine();
preparedStatement.setString(2, pwd);
int insert = preparedStatement.executeUpdate();
System.out.println(insert > 0 ? "插入记录成功" : "执行失败");
System.out.print("是否继续添加记录(y/n):");
String w = scanner.next();
if (!"y".equals(w)) {
flag = false;
}
String dubug = scanner.nextLine();
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, preparedStatement, connection);
}
}
1-3.删除记录
/**
* 删除记录
*/
public static void delEte() {
Scanner scanner = new Scanner(System.in);
Connection connection = null;
PreparedStatement preparedStatement = null;
String deleteSql = "delete from admin where username=?";
try {
connection = JdbcUtilsByDruid.getConnection();
preparedStatement = connection.prepareStatement(deleteSql);
System.out.print("请输入需要删除的用户名:");
String user = scanner.nextLine();
preparedStatement.setString(1, user);
int delete = preparedStatement.executeUpdate();
System.out.println(delete > 0 ? "删除记录成功" : "执行失败");
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, preparedStatement, connection);
}
}
1-4.查询记录
/**
* 查询记录
*/
public static void selEct() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String selectSql = "select id,username,pwd from admin";
try {
connection = JdbcUtilsByDruid.getConnection();
preparedStatement = connection.prepareStatement(selectSql);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt(1);
String username = resultSet.getString(2);
String pwd = resultSet.getString(3);
System.out.println(id + "\t" + username + "\t" + pwd);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(resultSet, preparedStatement, connection);
}
}

九、Apache—DBUtils


1.分析问题

关闭 Connection 后 ,ResuItSet 结果集无法使用
ResuItSet 不利于数据的管理

image-20230213175214567

2.基本介绍

commons-dbutils 是 Apache 组织提供的一个开源 JDBC( 工具类库 , 它是对 JDBC 的封装,使用 dbutils 能极大简化jdbc编码的工作量 。
DbUtiIs 类:

* QueryRunner 类 : 该类封装了 SQL 的执行 , 是线程安全的 。 可以实现增 、 删 、 改 、 查 、 批处理
* 使用 QueryRunner 类实现查询
* ResultSetHandIer 接口:该接口用于处理 java.sqI.ResultSet, 将数据按要求转换为另一种形式

方法:

- update--插入、修改、删除,返回受影响的行数
- insert--支持插入操作,获取自增列作为返回值
- query--查询操作,自动处理ResultSet需要Handler的配合

image-20230213202632803

3.实际使用DBUtils

3-1.插入记录
public void testInsert() throws SQLException {
//简化sql操作,结合Handler来处理常见的查询减少代码量
QueryRunner queryRunner = new QueryRunner();
//获取连接
Connection connection = JdbcUtils.getConnection();
String sql="insert into dogs values(null,?,?,?)";
int update = queryRunner.update(connection, sql, "a", "公", 12);
System.out.println(update);
DbUtils.closeQuietly(connection);
}
3-2.删除记录
public void testDelete() throws SQLException {
QueryRunner queryRunner = new QueryRunner();
String sql="delete from dogs where age>?";
Connection connection = JdbcUtils.getConnection();
int update = queryRunner.update(connection, sql, 10);
System.out.println(update);
DbUtils.closeQuietly(connection);
}
3-3.查询记录(单条)
public void testSelect() throws SQLException {
QueryRunner queryRunner = new QueryRunner();
Connection connection = JdbcUtils.getConnection();
String sql="select id,name,sex,age from dogs where id=?";
//执行sql的时候需要Handler对象,参数为查询到对象的class
BeanHandler<Dogs> dogsBeanHandler = new BeanHandler<>(Dogs.class);
Dogs dogs = queryRunner.query(connection, sql, dogsBeanHandler, 4);
System.out.println(dogs);
DbUtils.closeQuietly(connection);
}
3-4.查询记录(多条)
public void testSelectAll() throws SQLException {
QueryRunner queryRunner = new QueryRunner();
Connection connection = JdbcUtils.getConnection();
String sql="select id,name,sex,age from dogs where id<?";
BeanListHandler<Dogs> dogsBeanHandler = new BeanListHandler<>(Dogs.class);
List<Dogs> dogsList = queryRunner.query(connection, sql, dogsBeanHandler, 10);
dogsList.forEach(System.out::println);
DbUtils.closeQuietly(connection);
}
3-5.修改记录
public void testupDate() throws SQLException {
QueryRunner queryRunner = new QueryRunner();
Connection connection = JdbcUtils.getConnection();
String sql="update dogs set age=? where name=?";
int update = queryRunner.update(connection, sql, 19, "f");
System.out.println(update);
DbUtils.closeQuietly(connection);
}
3-6.查询记录(聚合函数)
public void testAggregate() throws SQLException {
QueryRunner queryRunner = new QueryRunner();
Connection connection = JdbcUtils.getConnection();
//最大值
String maxSql="select max(age) from dogs";
ScalarHandler<Object> handler = new ScalarHandler<>();
int max = (Integer) queryRunner.query(connection, maxSql, handler);
System.out.println(max);
DbUtils.closeQuietly(connection);
}

4.再次分析问题(解决:抽象CRUD方法)

通过编写上述代码后相对于直接使用JdbcUtils简化了不少代码,但是我们发现代码重复量太高

十、抽象CRUD(BasicDao)


1.分析问题

apache-dbutils+Druid 简化了JDBC开发 ,但还有不足 :

* SQL 语句是固定 ,不能通过参数传入 ,通用性不好 ,需要进行改进 ,更方便执行增删改查
* 对于 select 操作 ,如果有返回值 ,返回类型不能固定 ,需要使用泛型
* 将来的表很多 ,业务需求复杂 ,不可能只靠一个 Java 类完成

image-20230213202821986

2.基本说明

DAO :data access object 数据访问对象这样的通用类 ,称为 BasicDao ,是专门和数据库交互的 ,即完成对数据库 ( 表 ) 的 crud 操作 。
在 BaiscDao 的基础上 ,实现一张表对应一个 Dao,更好的完成功能。

3.BasicDAO 应用实例

/**
* @Author: XIYAN
* @Date: 2023/2/13 15:28
* @注释:BaseDao是所有针对数据库操作的基本类
* 需要在里面设置一些通用方法来解决增删查改代码重复的问题
*/
public class BasicDao {
//定义QueryRunner类型的属性,值为对象
QueryRunner queryRunner = new QueryRunner();
/**
* 该方法是进行增删改的通用方法
* @param sql 传入需要操作的sql
* @param params 传入需要使用的值
* @return 返回受影响的行数
*/
public int update(String sql, Object... params) {
//打开链接
Connection connection = JdbcUtils.getConnection();
try {
//执行成功返回受影响的行数
return queryRunner.update(connection, sql, params);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
//关闭连接
DbUtils.closeQuietly(connection);
}
}
/**
* 根据传入的sql查询单个对象的方法
* @param clazz 查询后要返回的对象
* @param sql 查询单个对象的sql
* @param params sql参数
* @return 查询到的对象
* @param <T> 根据calzz得到一个泛型将这个泛型作为对象返回(属性与表字段相同)
*/
public <T>T selectOne(Class<T> clazz,String sql,Object ... params){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new BeanHandler<>(clazz),params);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally{
DbUtils.closeQuietly(connection);
}
}
/**
*
* @param clazz 查询后要返回的对象
* @param sql 查询的sql
* @param params sql参数
* @return 查询到的对象
* @param <T> 根据calzz得到一个泛型将这个泛型作为对象返回(属性与表字段相同)
*/
public <T>List<T> selectList(Class<T> clazz,String sql,Object ... params){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new BeanListHandler<>(clazz),params);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally{
DbUtils.closeQuietly(connection);
}
}
/**
* 通用的聚合函数查询方法
* @param sql 传入的sql
* @param params sql参数
* @return 返回查询到的值
*/
public Object selectAggregate(String sql,Object ... params){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new ScalarHandler<>(),params);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally{
DbUtils.closeQuietly(connection);
}
}
}

所有的笔记来源于:韩顺平 (bilibili.com)

本文作者:顔をして

本文链接:https://www.cnblogs.com/ynxiyan/p/17106242.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   顔をして  阅读(36)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 I Really Want to Stay at Your House Samuel Kim,Lorien
  2. 2 将月亮藏进躯体 豆芽鱼(庄东茹)
  3. 3 辞九门回忆 解忧草,冰幽
  4. 4 I'M Not Girl 伊晗
将月亮藏进躯体 - 豆芽鱼(庄东茹)
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 周仁

作曲 : 音融三喜/是的麦克雷

制作人 : 庄侃/照明

万籁寂静 息事宁人 冷场冷清

暴雨刚叫停 洗过的风景

和善的陷阱

无人机 低空的飞行

航拍我落寞的表情

你的眼睛 游离在半空

无情

摆手神情 消失黎明 目光随行

噩梦里清醒 逃脱的侥幸

我的动情 该没怎么使用

却已经剧终

我将月亮藏进了躯体

葬于那片无光的禁区

纵容它野蛮生长本意

怀疑一旦产生 罪名就成立

我将月亮藏进了躯体

藏好它那白色的外衣

黑暗里寻觅静止空气

那不是风 是睡熟的空气

摆手神情 消失黎明 目光随行

噩梦里清醒 逃脱的侥幸

我的动情 该没怎么使用

却已经剧终

我将月亮藏进了躯体

葬于那片无光的禁区

纵容它野蛮生长本意

怀疑一旦产生 罪名就成立

我将月亮藏进了躯体

藏好它那白色的外衣

黑暗里寻觅静止空气

那不是风 是睡熟的空气

监制 : 苏斌

制作公司 : 三喜音乐(深圳)有限公司

企宣统筹 : 火焰波比

【未经著作权人许可,不得以任何方式使用】