typora-root-url: img
typora-copy-images-to: img
JDBC&连接池
回顾
-
会使用mysql字符串函数
CONCAT: 连接字符串
CHAR_LENGTH: 获取字符串长度
LOWER: 转小写
UPPER: 转大写
SUBSTR: 截取字符串
REPLACE: 替换
-
会使用mysql日期函数
ADDDATE: 添加指定的天数
DATEDIFF: 前面的时间-后面的时间,返回的是天数
CURDATE: 获取当前的年月日
CURTIME: 获取当前的时分秒
NOW: 获取当前的年月日时分秒
YEAR: 获取年
MONTH: 获取月
学习目标
- 能够理解JDBC的概念
- 能够使用JDBC实现对单表数据增、删、改、查
- 能够使用JDBC操作事务
- 能够编写JDBC工具类
- 能够完成JDBC实现登录案例
- 能够通过PreparedStatement完成增、删、改、查
- 能够完成PreparedStatement改造登录案例
JDBC的概念
目标
- 学习JDBC的概念
- 学习JDBC的作用
客户端操作MySQL数据库的方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YBk6koO-1597663684238)(/1550289380140.png)]
- 使用DOS命令行方式
- 使用第三方客户端来访问MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL
- 通过Java来访问MySQL数据库,今天要学习的内容
什么是JDBC
Java Data Base Connectivity:Java数据库连接
JDBC的作用
通过JDBC让Java可以操作数据库
JDBC的由来
-
直接写代码操作数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6N4bXhVj-1597663684243)(/jdbc01.png)]
直接写代码操作数据库存在的问题:- 不知道MySQL数据库的操作方式,解析方式
- 代码繁琐,写起来麻烦
- MySQL和Oracle等其他数据库的操作方式和解析方式不同,每个数据库都要写一套代码
- MySQL和Oracle等其他数据库相互切换麻烦
-
JDBC规范定义接口,具体的实现由各大数据库厂商来实现
JDBC是Java访问数据库的标准规范。真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用JDBC接口中的方法即可。数据库驱动由数据库厂商提供。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aXB4kWJD-1597663684260)(/1550373498072.png)]
JDBC的好处
- 我们只需要会调用JDBC接口中的方法即可,使用简单
- 使用同一套Java代码,进行少量的修改就可以访问其他JDBC支持的数据库
小结
-
说出JDBC的概念?
Java数据库连接
-
说出JDBC的作用?
通过JDBC可以让Java操作数据库
JDBC核心API的介绍
目标
学习JDBC四个核心对象
JDBC会用到的包
- java.sql:JDBC访问数据库的基础包
- javax.sql: JDBC访问数据库的扩展包
- 数据库的驱动,各大数据库厂商来实现
JDBC四个核心对象
这几个类都是在java.sql包中
- DriverManager: 用于注册驱动
- Connection: 表示数据库的连接
- Statement: 执行SQL语句的对象
- ResultSet: 结果集或一张虚拟表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qS7E8Ykr-1597663684301)(/jdbc03.png)]
JDBC访问数据库的步骤
- 通过DriverManager注册驱动
- 获取连接Connection
- 通过Statement,将我们写的SQL语句运送到数据库执行
- 返回结果集ResultSet
小结
JDBC四个核心对象?
DriverManager: 注册驱动
Connection: Java程序和数据库之间的连接
Statement: 执行SQL语句的对象
ResultSet: 结果集
JDBC注册驱动
我们Java程序需要通过数据库驱动才能连接到数据库,因此需要注册驱动。
目标
学习导入mysql驱动Jar包
学习JDBC注册数据库驱动
在注册驱动前需要先导入驱动的Jar包
导入驱动Jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FDRjr7d3-1597663684318)(/1555942738648.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yIRlkEUz-1597663684321)(/1555942725646.png)]
注册驱动
我们Java程序需要通过数据库驱动才能连接到数据库,因此需要注册驱动。
MySQL的驱动的入口类是:com.mysql.jdbc.Driver
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bc8WXQOc-1597663684323)(/jdbc07.png)]
API介绍
java.sql.DriverManager
类用于注册驱动。提供如下方法注册驱动
static void registerDriver(Driver driver)
向 DriverManager 注册给定驱动程序。
使用步骤
1.DriverManager.registerDriver(驱动对象); 传入对应参数即可
案例代码
public class Demo01 {
public static void main(String[] args) throws Exception {
// 注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
}
}
通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
// 自己自动注册
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vF8KROVk-1597663684325)(/jdbc08.png)]
注意:使用
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
,存在两方面不足
- 硬编码,后期不易于程序扩展和维护
- 驱动被注册两次
使用Class.forName("com.mysql.jdbc.Driver");
加载驱动,这样驱动只会注册一次
public class Demo01 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver"); // 后期可以将"com.mysql.jdbc.Driver"字符串写在文件中.
}
}
演示:Class.forName("包名.类名");
会走这个类的静态代码块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rSl7EAgM-1597663684326)(/jdbc10.png)]
通常开发我们使用Class.forName() 加载驱动。Class.forName("com.mysql.jdbc.Driver");
会走Driver类的静态代码块。在静态代码块中注册一次驱动。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2GrUTrh2-1597663684328)(/jdbc09.png)]
总结:注册MySQL驱动使用
Class.forName("com.mysql.jdbc.Driver");
小结
-
如何注册数据库驱动?
Class.forName("com.mysql.jdbc.Driver");
获取Connection连接和Statement
Connection
表示Java程序与数据库之间的连接,只有拿到Connection才能操作数据库。
目标
学习JDBC获取数据库连接
学习获取Statement对象
API介绍
java.sql.DriverManager
类中有如下方法获取数据库连接
static Connection getConnection(String url, String user, String password)
连接到给定数据库 URL ,并返回连接。
参数说明
String url
:连接数据库的URL,用于说明连接数据库的位置String user
:数据库的账号String password
:数据库的密码
连接数据库的URL地址格式:协议名:子协议://服务器名或IP地址:端口号/数据库名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xmpbhvO6-1597663684330)(/jdbc13.png)]
MySQL写法:jdbc:mysql://localhost:3306/day16
如果是本地服务器,端口号是默认的3306,则可以简写:jdbc:mysql:///day16
注意事项
如果数据出现乱码需要在url加上参数: ?characterEncoding=utf8,表示让数据库以UTF-8编码来处理数据。
如: jdbc:mysql://localhost:3306/day16?characterEncoding=utf8
获取Statement对象
在java.sql.Connection
接口中有如下方法获取到Statement
对象
Statement createStatement()
创建一个 Statement 对象来将 SQL 语句发送到数据库
使用步骤
- 注册驱动
- 调用方法:DriverManager.getConnection(url, user, password); 传入对应参数即可
案例代码
public class Demo01 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
// 连接到MySQL
// url: 连接数据库的URL
// user: 数据库的账号
// password: 数据库的密码
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day24", "root", "root");
System.out.println(conn);
}
}
案例效果
1.连接成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAsrrCmS-1597663684332)(/jdbc14.png)]
2.连接失败
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIfoYH2T-1597663684334)(/jdbc15.png)]
小结
-
JDBC获取数据库连接使用哪个API?
Connection conn = DriverManager.getConnection(url, 数据库账号, 数据库密码);
-
通过JDBC连接mysql的URL写法?
jdbc:mysql://localhost:3306/数据库名 如果连接的是本机端口号是3306,可以省略为: jdbc:mysql:///数据库名
JDBC实现对单表数据增、删、改
目标
学习JDBC实现对单表数据增、删、改
准备数据
-- 创建分类表
CREATE TABLE category (
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100)
);
-- 初始化数据
INSERT INTO category (cname) VALUES('家电');
INSERT INTO category (cname) VALUES('服饰');
INSERT INTO category (cname) VALUES('化妆品');
JDBC实现对单表数据增、删、改
我们要对数据库进行增、删、改、查,需要使用Statement
对象来执行SQL语句。
API介绍
Statement的API介绍
-
boolean execute(String sql) 用执行任何SQL语句,如果是查询返回true,如果不是查询语句返回false; 通常不用
-
int executeUpdate(String sql) 用于执行增删改等语句; 返回影响的行数
-
ResultSet executeQuery(String sql) 用于执行查询语句; 返回查询到的结果集
executeQuery:用于执行查询SQL
executeUpdate:用于执行除查询外的SQL
使用步骤
- 注册驱动
- 获取连接
- 获取Statement对象
- 使用Statement对象执行SQL语句
- 释放资源
案例代码
public class Demo03 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///day24", "root", "root");
System.out.println(conn);
// 从连接中拿到一个Statement对象
Statement stmt = conn.createStatement();
// 1.插入记录
String sql = "INSERT INTO category (cname) VALUES ('手机');";
int i = stmt.executeUpdate(sql);
System.out.println("影响的行数:" + i);
// 2.修改记录
sql = "UPDATE category SET cname='汽车' WHERE cid=4;";
i = stmt.executeUpdate(sql);
System.out.println("影响的行数:" + i);
// 3.删除记录
sql = "DELETE FROM category WHERE cid=1;";
i = stmt.executeUpdate(sql);
System.out.println("影响的行数:" + i);
// 释放资源
stmt.close();
conn.close();
}
}
案例效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tHZ38HOv-1597663684337)(/jdbc17.png)]
小结
使用JDBC操作数据库的步骤:
- 注册驱动
- 获取连接Connection
- 获取Statement小货车
- 执行SQL语句 executeUpdate(SQL) 执行增删改
- 关闭资源
JDBC实现对单表数据查询
目标
JDBC实现对单表数据查询
ResultSet
用于保存执行查询SQL语句的结果。
我们不能一次性取出所有的数据,需要一行一行的取出。
ResultSet的原理
- ResultSet内部有一个指针,记录获取到哪行数据
- 调用next方法, ResultSet内部指针会移动到下一行数据
- 我们可以通过ResultSet得到一行数据 getXxx得到某列数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FjE4TnM3-1597663684339)(/jdbc18.png)]
ResultSet获取数据的API
其实ResultSet获取数据的API是有规律的get后面加数据类型。我们统称getXXX()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xgqJRsfN-1597663684342)(/jdbc19.png)]
使用JDBC查询数据库中的数据的步骤
- 注册驱动
- 获取连接
- 获取到Statement
- 使用Statement执行SQL
- ResultSet处理结果
- 关闭资源
案例代码
public class Demo04 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///day24", "root", "root");
Statement stmt = conn.createStatement();
String sql = "SELECT * FROM category;";
ResultSet rs = stmt.executeQuery(sql);
// 内部有一个指针,只能取指针指向的那条记录
while (rs.next()) { // 指针移动一行,有数据才返回true
// 取出数据
int cid = rs.getInt("cid");
String cname = rs.getString("cname");
System.out.println(cid + " == " + cname);
}
// 关闭资源
rs.close();
stmt.close();
conn.close();
}
}
案例效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9xLCg73l-1597663684344)(/jdbc20.png)]
小结
-
从ResultSet中能一次取出所有数据吗?
只能通过游标一条一条获取
-
如何通过ResultSet取数据
while (rs.next()) { rs.getXxx(字段名); rs.getXxx(字段的索引); }
JDBC事务
目标
学习JDBC操作事务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6d2K14aW-1597663684345)(/1597631600673.png)]
之前我们是使用MySQL的命令来操作事务。接下来我们使用JDBC来操作银行转账的事务。
准备数据
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('张三', 1000), ('李四', 1000);
API介绍
Connection
接口中与事务有关的方法
-
void setAutoCommit(boolean autoCommit) throws SQLException; false:开启事务, ture:关闭事务
-
void commit() throws SQLException; 提交事务
-
void rollback() throws SQLException; 回滚事务
使用步骤
- 注册驱动
- 获取连接
- 开启事务
- 获取到Statement
- 使用Statement执行SQL
- 提交或回滚事务
- 关闭资源
案例代码
public class Demo05 {
public static void main(String[] args) {
Connection conn = null;
try {
// 拿到连接
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///day24", "root", "root");
// 开启事务
conn.setAutoCommit(false);
Statement pstmt = conn.createStatement();
// 张三减500
String sql = "UPDATE account SET balance = balance - 500 WHERE id=1;";
pstmt.executeUpdate(sql);
// 模拟异常
// int i = 10 / 0;
// 李四加500
sql = "UPDATE account SET balance = balance + 500 WHERE id=2;";
pstmt.executeUpdate(sql);
pstmt.close();
// 成功,提交事务
System.out.println("成功,提交事务");
conn.commit();
} catch (Exception e) {
// 失败,回滚事务
try {
System.out.println("出了异常,回滚事务");
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
案例效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvhrIrdF-1597663684347)(/jdbc21.png)]
小结
JDBC中与事务相关的API?
在Connection接口中
setAutoCommit(false): 开启事务
commit(): 提交事务
rollback(): 回滚事务
编写JDBC工具类
目标
编写JDBC获取连接与关闭连接工具类
通过上面案例需求我们会发现每次去执行SQL语句都需要注册驱动,获取连接,得到Statement,以及释放资源。发现很多重复的劳动,我们可以将重复的代码定义到某个类的方法中。直接调用方法,可以简化代码。
那么我们接下来定义一个JDBCUtils
类。把注册驱动,获取连接,得到Statement,以及释放资源的代码放到这个类的方法中。以后直接调用方法即可。
编写JDBC工具类步骤
- 将固定字符串定义为常量
- 在静态代码块中注册驱动
- 提供一个获取连接的方法
static Connection getConneciton();
- 定义关闭资源的方法
close(Connection conn, Statement stmt, ResultSet rs)
- 重载关闭方法
close(Connection conn, Statement stmt)
案例代码
JDBCUtils.java
public class JDBCUtils {
// 1.将固定字符串定义为常量
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql:///day24";
private static final String USER = "root";
private static final String PASSWORD = "root";
// 2.在静态代码块中注册驱动(只注册一次)
// 当这个类加载到内存的时候就走这个静态代码块,再去触发Driver类中的静态代码块,主动注册
static {
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {}
}
// 3.提供一个获取连接的方法static Connection getConneciton();
// 我们面向JDBC编程
public static Connection getConnection() throws SQLException {
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
return conn;
}
// 5.重载关闭方法close(Connection conn, Statement stmt)
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
// 4.定义关闭资源的方法close(Connection conn, Statement stmt, ResultSet rs)
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
}
小结
编写JDBC工具类的目的是什么?
简化重复代码,让JDBC使用更加简便
JDBC实现登录案例
目标
模拟用户输入账号和密码登录网站
- 输入正确的账号,密码,显示登录成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MF1Tif8H-1597663684350)(/登录案例01.png)] - 输入错误的账号,密码,显示登录失败
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YIWejXz0-1597663684351)(/登录案例02.png)]
案例分析
- 使用数据库保存用户的账号和密码
- 让用户输入账号和密码
- 使用SQL根据用户的账号和密码去数据库查询数据
- 如果查询到数据,说明登录成功
- 如果查询不到数据,说明登录失败
实现步骤
-
创建一个用户表保存用户的账号和密码,并添加一些数据,SQL语句如下:
CREATE TABLE USER ( id INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(50), PASSWORD VARCHAR(50) ); INSERT INTO USER (NAME, PASSWORD) VALUES('admin', '123'), ('test', '123'), ('gm', '123');
-
编写代码让用户输入账号和密码
public class Demo07 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine(); }
-
使用SQL根据用户的账号和密码去数据库查询数据
public class Demo07 { public static void main(String[] args) throws Exception { // 让用户输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine(); // 使用SQL根据用户的账号和密码去数据库查询数据 Connection conn = JDBCUtils.getConnection(); Statement stmt = conn.createStatement(); String sql = "SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';"; } }
-
如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败
public class Demo07 { public static void main(String[] args) throws Exception { // 让用户输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine(); // 使用SQL根据用户的账号和密码去数据库查询数据 Connection conn = JDBCUtils.getConnection(); Statement stmt = conn.createStatement(); String sql = "SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';"; // 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败 ResultSet rs = stmt.executeQuery(sql); if (rs.next()) { //能进来查询到了数据. String name2 = rs.getString("name"); System.out.println("欢迎您," + name2); } else { //查询不到数据,说明登录失败 System.out.println("账号或密码错误..."); } JDBCUtils.close(conn, stmt, rs); } } }
小结
登录案例核心思路
- 在数据库中存储一批账号密码
- 让用户输入账号密码
- 根据用户输入的账号和密码到数据中查询
- 如果能够查到数据,登陆成功
- 如果查不到数据,登陆失败
SQL注入介绍
目标
学习SQL注入的概念
SQL注入问题
在我们前面JDBC实现登录案例中,当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了
请输入用户名:
hehe
请输入密码:
a'or'1'='1
问题分析:
// 代码中的SQL语句
"SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';";
// 将用户输入的账号密码拼接后
"SELECT * FROM user WHERE name='hehe' AND password='a'or'1'='1';"
我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。
要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。需要使用PreparedSatement类解决SQL注入。
小结
什么是SQL注入?
用户输入的内容和SQL语句拼接改变SQL语句原本的含义
PreparedStatement预编译对象
目标
了解PreparedSatement的执行原理
PreparedStatement的执行原理
继承结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-63MlJUmt-1597663684353)(/pstmt01.png)]
我们写的SQL语句让数据库执行,数据库不是直接执行SQL语句字符串。和Java一样,数据库需要执行编译后的SQL语句(类似Java编译后的字节码文件)。
Satement
对象每执行一条SQL语句都会先将这条SQL语句发送给数据库编译,数据库再执行。
Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO users VALUES (1, '张三', '123456');");
stmt.executeUpdate("INSERT INTO users VALUES (2, '李四', '666666');");
上面2条SQL语句我们可以看到大部分内容是相同的,只是数据略有不一样。数据库每次执行都编译一次。如果有1万条类似的SQL语句,数据库需要编译1万次,执行1万次,显然效率就低了。
prepareStatement()
会先将SQL语句发送给数据库预编译。PreparedStatement
会引用着预编译后的结果。可以多次传入不同的参数给PreparedStatement
对象并执行。相当于调用方法多次传入不同的参数。
String sql = "INSERT INTO users VALUES (?, ?, ?);";
// 会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数
pstmt.setString(1, 1);
pstmt.setInt(2, "张三");
pstmt.setString(3, "123456");
pstmt.executeUpdate();
// 再次设置参数
pstmt.setString(1, 2);
pstmt.setInt(2, "李四");
pstmt.setString(3, "66666");
pstmt.executeUpdate();
上面预编译好一条SQL,2次传入了不同的参数并执行。如果有1万条类似的插入数据的语句。数据库只需要预编译一次,传入1万次不同的参数并执行。减少了SQL语句的编译次数,提高了执行效率。
示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sjUuaI1K-1597663684354)(/pstmt09.png)]
PreparedStatement的好处
prepareStatement()
会先将SQL语句发送给数据库预编译。PreparedStatement
会引用着预编译后的结果。可以多次传入不同的参数给PreparedStatement
对象并执行。减少SQL编译次数,提高效率。- 安全性更高,没有SQL注入的隐患。
- 提高了程序的可读性。
小结
PreparedSatement的好处?
- 提高效率
- 安全,没有SQL注入
- 提高SQL语句的可读性
PreparedStatement的API介绍
目标
学习PreparedSatement相应的API
继承结构
Statement
↑ 继承
PreparedStatement
获取PreparedStatement
在java.sql.Connection
有获取PreparedSatement
对象的方法
PreparedStatement prepareStatement(String sql)
会先将SQL语句发送给数据库预编译。PreparedStatement对象会引用着预编译后的结果。
"SELECT * FROM user WHERE name=? AND password=?;"; 参数化的SQL ?表示占位符,将来会传递具体值
PreparedStatement的API介绍
在java.sql.PreparedStatement
中有设置SQL语句参数,和执行参数化的SQL语句的方法
-
void setDouble(int parameterIndex, double x) 将指定参数设置为给定 Java double 值。
-
void setFloat(int parameterIndex, float x) 将指定参数设置为给定 Java REAL 值。
-
void setInt(int parameterIndex, int x) 将指定参数设置为给定 Java int 值。
-
void setLong(int parameterIndex, long x) 将指定参数设置为给定 Java long 值。
-
void setObject(int parameterIndex, Object x) 使用给定对象设置指定参数的值。
-
void setString(int parameterIndex, String x) 将指定参数设置为给定 Java String 值。
-
ResultSet executeQuery() 在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的ResultSet对象。
-
int executeUpdate() 在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言DML语句,比如 INSERT、UPDATE 或 DELETE 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。
PreparedSatement使用步骤
- 获取连接
- 编写SQL语句,未知内容使用?占位:
"SELECT * FROM user WHERE name=? AND password=?;";
参数化的SQL - 获得PreparedStatement对象
- 设置实际参数
- 执行参数化SQL语句
- 关闭资源
小结
PreparedSatement如何设置?的参数
setXxx(第几个?, 数据);
PreparedSatement如何执行SQL
ResultSet executeQuery(): 执行查询
int executeUpdate(): 执行除查询外的SQL
PreparedStatement实现增删改
目标
学习PreparedSatement实现增删改
创建表结构
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
address VARCHAR(50)
);
添加数据
向Employee表添加3条记录
// 添加数据: 向Employee表添加3条记录
public static void addEmployee() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "INSERT INTO employee VALUES (NULL, ?, ?, ?);";
// prepareStatement()会先将SQL语句发送给数据库预编译。
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数
pstmt.setString(1, "刘德华");
pstmt.setInt(2, 57);
pstmt.setString(3, "香港");
int i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
// 再次设置参数
pstmt.setString(1, "张学友");
pstmt.setInt(2, 55);
pstmt.setString(3, "澳门");
i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
// 再次设置参数
pstmt.setString(1, "黎明");
pstmt.setInt(2, 52);
pstmt.setString(3, "香港");
i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
JDBCUtils.close(conn, pstmt);
}
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CgpVOOal-1597663684356)(/pstmt05.png)]
PreparedStatement修改数据
将id为2的学生地址改成台湾
// 修改数据: 将id为2的学生地址改成台湾
public static void updateEmployee() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "UPDATE employee SET address=? WHERE id=?;";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "台湾");
pstmt.setInt(2, 2);
int i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
JDBCUtils.close(conn, pstmt);
}
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p7QOfxpP-1597663684357)(/pstmt06.png)]
PreparedStatement删除数据
删除id为2的员工
// 删除数据: 删除id为2的员工
public static void deleteEmployee() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "DELETE FROM employee WHERE id=?;";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 2);
int i = pstmt.executeUpdate();
System.out.println("影响的行数:" + i);
JDBCUtils.close(conn, pstmt);
}
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JKOJjHui-1597663684358)(/pstmt07.png)]
小结
PreparedSatement实现增删改使用哪个方法?
int executeUpdate(); // 执行增删改SQL语句
不是任何地方都可以写?,字段值不确定的时候才可以?
使用PreparedSatement改写登录案例
目标
使用PreparedSatement改写登录案例
- 输入正确的账号密码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N33fI0Dp-1597663684359)(/1551606829574.png)] - 输入错误的密码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2w9rcKjA-1597663684360)(/1551606884763.png)]
PreparedSatement使用步骤
- 编写SQL语句,未知内容使用?占位
- 获得PreparedStatement对象
- 设置实际参数
- 执行参数化SQL语句
- 关闭资源
案例代码
public class Demo02 {
public static void main(String[] args) throws Exception {
// 让用户输入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入账号: ");
String name = sc.nextLine();
System.out.println("请输入密码: ");
String password = sc.nextLine();
// 获取连接
Connection conn = JDBCUtils.getConnection();
// 编写SQL语句,未知内容使用?占位
String sql = "SELECT * FROM user WHERE name=? AND password=?;";
// prepareStatement()会先将SQL语句发送给数据库预编译。
PreparedStatement pstmt = conn.prepareStatement(sql);
// 指定?的值
// parameterIndex: 第几个?,从1开始算
// x: 具体的值
pstmt.setString(1, name);
pstmt.setString(2, password); // 正确的密码
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
String name = rs.getString("name");
System.out.println("name:" + name);
} else {
System.out.println("没有找到数据...");
}
JDBCUtils.close(conn, pstmt, rs);
}
}
案例效果
- 输入正确的账号密码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOfzyR8I-1597663684362)(/1551606829574.png)] - 输入错误的密码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8OWghcwS-1597663684363)(/1551606884763.png)]
小结
将SQL中的账号密码使用?占位
使用PreparedStatement给?赋值
再执行
PreparedSatement查询数据
目标
使用PreparedSatement实现查询数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cdjdStim-1597663684365)(/1551613022301.png)]
查询id小于8的员工信息,并保存到员工对象中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCbTlr7E-1597663684366)(/1551613450029.png)]
查询id小于8的员工信息,并保存到员工类中
实现步骤
- 定义Employee类
- 执行SQL语句,保存ResultSet
- 创建一个集合用于保存所有的员工对象
- 每次循环将一条记录存放到一个员工对象中
- 把员工对象放到集合中
- 关闭资源
代码
public class Employee {
private int id;
private String name;
private int age;
private String address;
// 省略构造方法/getter/setter
}
// 查询数据: 查询id小于8的员工信息,并保存到员工类中
public static void queryEmployee() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql = "SELECT * FROM employee WHERE id<?;";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 26);
ResultSet rs = pstmt.executeQuery();
// 创建集合存放多个Employee2对象
ArrayList<Employee> list = new ArrayList<>();
while (rs.next()) {
// 移动到下一行有数据,取出这行数据
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String address = rs.getString("address");
// 创建Employee2对象
Employee e = new Employee(id, name, age, address);
// 将创建好的员工添加到集合中
list.add(e);
}
// 输出对象
for (Employee e : list) {
System.out.println(e);
}
JDBCUtils.close(conn, pstmt, rs);
}
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32IfmTrf-1597663684368)(/pstmt08.png)]
小结
将查询的结果中每条记录放到一个对象中
连接池介绍
目标
学习连接池的原理和好处
没有连接池的现状
-
之前JDBC访问数据库的步骤:
创建数据库连接 →运行SQL语句→关闭连接
每次数据库访问执行这样重复的动作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FO54p55j-1597663684370)(/连接池01.png)] -
每次创建数据库连接的问题
- 获取数据库连接需要消耗比较多的资源,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,而数据库创建连接通常需要消耗相对较多的资源。这样数据库连接对象的使用率低。
我们现实生活中每日三餐。我们并不会吃一餐饭就将碗丢掉,而是吃完饭后将碗放到碗柜中,下一餐接着使用。目的是重复利用碗,我们的数据库连接也可以重复使用,可以减少数据库连接的创建次数。提高数据库连接对象的使用率。
连接池的概念: 连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的。
连接池的原理
- 启动连接池,连接池就会初始化一些连接
- 当用户需要使用数据库连接,直接从连接池中取出
- 当用户使用完连接,会将连接重新放回连接池中[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3GbC682-1597663684371)(/1551614039056.png)]
连接池好处
连接池中会保存一些连接,这些连接可以重复使用,降低数据资源的消耗
常用连接池的介绍
javax.sql.DataSource
表示数据库连接池,是JDK中提供的一个接口,没有具体的实现,它的实现由连接池的厂商去实现。我们只需要学习这个工具如何使用即可。
public interface DataSource {
Connection getConnection();
...
}
常用的连接池实现组件有以下这些
- 阿里巴巴-德鲁伊Druid连接池:Druid是阿里巴巴开源平台上的一个项目
- C3P0是一个开源的连接池,目前使用它的开源项目有Hibernate,Spring等。
- DBCP(DataBase Connection Pool)数据库连接池,是Tomcat使用的连接池组件。
小结
连接池的原理?
1.启动连接池时,准备好一批连接
2.从连接池中取出连接给用户使用
3.当连接池使用完毕后,放回连接池中,方便重复使用
连接池的好处?
减少数据库连接的创建和销毁,节省资源
C3P0连接池
目标
学习C3P0连接池
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGBDFvGQ-1597663684372)(/1551619323240.png)]
C3P0连接池简介
C3P0地址:https://sourceforge.net/projects/c3p0/?source=navbar
C3P0的jar包:c3p0-0.9.5.2.jar
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cj6gE5tw-1597663684374)(/1550387401441.png)]
C3P0常用的配置参数解释
参数 | 说明 |
---|---|
initialPoolSize | 连接池刚启动时,连接池内包含的连接数量 |
maxPoolSize | 连接池中最多可以放多少个连接 |
checkoutTimeout | 连接池中没有连接时最长等待时间 |
maxIdleTime | 连接池中的空闲连接多久没有使用就会回收。默认是0,0表示不回收 |
C3P0配置文件
我们看到要使用C3P0连接池,需要设置一些参数。那么这些参数怎么设置最为方便呢?使用配置文件方式。
配置文件的要求
- 文件名:c3p0-config.xml
- 放在源代码即src目录下
配置文件c3p0-config.xml
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">2000</property>
<property name="maxIdleTime">1000</property>
</default-config>
<named-config name="itheimac3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">15</property>
<property name="checkoutTimeout">2000</property>
<property name="maxIdleTime">1000</property>
</named-config>
</c3p0-config>
API介绍
com.mchange.v2.c3p0.ComboPooledDataSource
类表示C3P0的连接池对象,常用2种创建连接池的方式:
1.无参构造,使用默认配置
2.有参构造,使用命名配置
ComboPooledDataSource类API
-
public ComboPooledDataSource() 无参构造使用默认配置(使用xml中default-config标签中对应的参数)
-
public ComboPooledDataSource(String configName) 有参构造使用命名配置(configName:xml中配置的名称,使用xml中named-config标签中对应的参数)
-
public Connection getConnection() throws SQLException 从连接池中取出一个连接
使用步骤
- 导入jar包
c3p0-0.9.5.2.jar
和mchange-commons-java-0.2.12.jar
- 复制配置文件
c3p0-config.xml
放在src目录下,配置对应参数 - 创建连接池对象
ComboPooledDataSource
- 从连接池中获取连接对象
- 使用连接对象操作数据库
- 关闭资源,将连接还回连接池中
注意事项
C3P0配置文件名称必须为c3p0-config.xml
C3P0命名配置可以有多个
案例代码
-
准备数据
CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT, score DOUBLE DEFAULT 0.0 );
-
配置文件
<c3p0-config> <!-- 使用默认的配置读取连接池对象 --> <default-config> <!-- 连接参数 --> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property> <property name="user">root</property> <property name="password">root</property> <!-- 连接池参数 --> <property name="initialPoolSize">5</property> <property name="maxPoolSize">10</property> <property name="checkoutTimeout">2000</property> <property name="maxIdleTime">1000</property> </default-config> <named-config name="itheimac3p0"> <!-- 连接参数 --> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property> <property name="user">root</property> <property name="password">root</property> <!-- 连接池参数 --> <property name="initialPoolSize">5</property> <property name="maxPoolSize">15</property> <property name="checkoutTimeout">2000</property> <property name="maxIdleTime">1000</property> </named-config> </c3p0-config>
-
java代码
public class Demo01 { public static void main(String[] args) throws Exception { // 方式一: 使用默认配置(default-config) // new ComboPooledDataSource(); // ComboPooledDataSource ds = new ComboPooledDataSource(); // 方式二: 使用命名配置(named-config:配置名) // new ComboPooledDataSource("配置名"); ComboPooledDataSource ds = new ComboPooledDataSource("otherc3p0"); // for (int i = 0; i < 10; i++) { // Connection conn = ds.getConnection(); // System.out.println(conn); // } // 从连接池中取出连接 Connection conn = ds.getConnection(); // 执行SQL语句 String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, "张三"); pstmt.setInt(2, 25); pstmt.setDouble(3, 99.5); int i = pstmt.executeUpdate(); System.out.println("影响的行数: " + i); pstmt.close(); conn.close(); // 将连接还回连接池中 } }
案例效果
-
正常获取连接池中连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5w6EKV9o-1597663684375)(/连接池06.png)] -
获取连接池中连接超时
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLiTKCOH-1597663684378)(/连接池07.png)] -
使用连接池中的连接往数据库添加数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WVIEfzHO-1597663684379)(/连接池05.png)]
注意:配置文件名称必须为:
c3p0-config.xml
,将配置文件放在src目录下
使用配置文件方式好处
只需要单独修改配置文件,不用修改代码
多个配置的好处
- 可以连接不同的数据库:db1,db2
- 可以使用不同的连接池参数:maxPoolSize
- 可以连接不同厂商的数据库:Oracle或MySQL
小结
CP30使用步骤?
- 导入jar包
c3p0-0.9.5.2.jar
和mchange-commons-java-0.2.12.jar
- 复制配置文件
c3p0-config.xml
放在src目录下,配置对应参数 - 创建连接池对象
ComboPooledDataSource
- 从连接池中获取连接对象
- 使用连接对象操作数据库
- 关闭资源,将连接还回连接池中
C3P0常用参数?
参数 | 作用 |
---|---|
initialPoolSize | 刚启动连接池时,包含的连接数量 |
maxPoolSize | 连接池支持的最大的连接数量 |
checkoutTimeout | 没有获取到连接,最长的等待时间 |
maxIdleTime | 最大空闲时间,指定时间没有人使用,这个连接会被销毁,默认0,0表示不销毁 |
Druid连接池
目标
学习Druid连接池的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGtYEJE5-1597663684381)(/1551620535241.png)]
Druid连接池介绍
Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控数据库连接池和SQL的执行情况。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。Druid地址:https://github.com/alibaba/druid
DRUID连接池使用的jar包:druid-1.0.9.jar
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uGeIXKlK-1597663684383)(/1550388331344.png)]
Druid常用的配置参数
参数 | 说明 |
---|---|
initialSize | 刚启动连接池时,连接池中包含连接的数量 |
maxActive | 连接池中最多可以放多少个连接 |
maxWait | 获取连接时最大等待时间,单位毫秒 |
Druid连接池基本使用
API介绍
com.alibaba.druid.pool.DruidDataSourceFactory
类有创建连接池的方法
public static DataSource createDataSource(Properties properties)
创建一个连接池,连接池的参数使用properties中的数据
我们可以看到Druid连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保存对应的参数。
Druid连接池的配置文件名称随便,放到src目录下面方便加载
druid.properties
文件内容:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day17
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
使用步骤
-
导入druid-1.0.0.jar的jar包
-
复制druid.properties文件到src下,并设置对应参数
-
加载properties文件的内容到Properties对象中
-
创建Druid连接池,使用配置文件中的参数
-
从Druid连接池中取出连接
-
执行SQL语句
-
关闭资源
案例代码
- 在src目录下新建一个Druid配置文件,命名为:druid.properties,内容如下
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day25
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
java代码
public class Demo04 {
public static void main(String[] args) throws Exception {
// 加载配置文件中的配置参数
InputStream is = Demo04.class.getResourceAsStream("/druid.properties");
Properties pp = new Properties();
pp.load(is);
// 创建连接池,使用配置文件中的参数
DataSource ds = DruidDataSourceFactory.createDataSource(pp);
// for (int i = 0; i < 10; i++) {
// Connection conn = ds.getConnection();
// System.out.println(conn);
// }
// 从连接池中取出连接
Connection conn = ds.getConnection();
// 执行SQL语句
String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "王五");
pstmt.setInt(2, 35);
pstmt.setDouble(3, 88.5);
int i = pstmt.executeUpdate();
System.out.println("影响的行数: " + i);
// 执行查询
sql = "SELECT * FROM student;";
ResultSet rs = pstmt.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
double score = rs.getDouble("score");
System.out.println("id: " + id + " ,name: " + name + " ,age = " + age + " ,score = " + score);
}
pstmt.close();
conn.close(); // 将连接还回连接池中
}
}
案例效果
- 正常获取连接池中的连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TtEZ4lNh-1597663684384)(/连接池09.png)] - 获取连接池中的连接超时
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCjdGJYz-1597663684386)(/连接池10.png)] - 使用DRUID连接池中的连接操作数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iVusKMIl-1597663684388)(/连接池11.png)]
小结
Druid使用步骤?
- 导入druid的jar包
- 复制druid.properties文件到src下,并设置对应参数
- 加载properties文件的内容到Properties对象中
- 创建Druid连接池,使用配置文件中的参数
- 从Druid连接池中取出连接
- 执行SQL语句
- 关闭资源
Druid常用的配置参数
参数 | 说明 |
---|---|
initialSize | 刚启动连接池时,初始化的连接数量 |
maxActive | 最大的连接数量 |
maxWait | 没有连接时,最长的等待时间 |
Druid连接池基本使用不管是C3P0连接池,配置大致都可以分为2种:1.连接数据库的参数
,2.连接池的参数
,这2种配置大致参数作用都相同,只是参数名称可能不一样。
编写连接池工具类
我们每次操作数据库都需要创建连接池,获取连接,关闭资源,都是重复的代码。我们可以将创建连接池和获取连接池的代码放到一个工具类中,简化代码。
目标
编写一个连接池的工具类,简化Druid连接池的使用
连接池工具类步骤
- 声明静态连接池成员变量
- 创建连接池对象
- 定义得到连接对象的方法
- 定义关闭资源的方法
案例代码
DataSourceUtils.java
public class DataSourceUtils {
// 1.声明静态数据源成员变量
private static DataSource ds;
// 2.创建连接池对象
static {
// 加载配置文件中的数据
InputStream is = DataSourceUtils.class.getResourceAsStream("/druid.properties");
Properties pp = new Properties();
try {
pp.load(is);
// 创建连接池,使用配置文件中的参数
ds = DruidDataSourceFactory.createDataSource(pp);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 3. 定义得到数据源的方法
public static DataSource getDataSource() {
return ds;
}
// 4. 定义得到连接对象的方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 5.定义关闭资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
// 6.重载关闭方法
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}
测试类代码
public class Demo03 {
public static void main(String[] args) throws Exception {
// 拿到连接
Connection conn = DataSourceUtils.getConnection();
// 执行sql语句
String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "李四");
pstmt.setInt(2, 30);
pstmt.setDouble(3, 50);
int i = pstmt.executeUpdate();
System.out.println("影响的函数: " + i);
// 关闭资源
DataSourceUtils.close(conn, pstmt);
}
}
使用连接池工具类后可以简化代码,我们重点是写SQL去执行。
小结
编写连接池工具类步骤
- 声明静态连接池成员变量
- 创建连接池对象
- 返回连接池中的连接
- 定义close关闭资源
总结
-
能够理解JDBC的概念
Java数据库连接
通过JDBC能让Java操作数据库
-
能够使用JDBC实现对单表数据增、删、改、查
executeUpdate() executeQuery()
public class Demo04 { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 1.通过DriverManager注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.获取连接Connection Connection conn = DriverManager.getConnection("jdbc:mysql:///day19", "root", "root"); // 3.获取连接Statement Statement stmt = conn.createStatement(); // 4.执行SQL语句 // 增 // int row = stmt.executeUpdate("INSERT INTO category VALUES (NULL, '床上用品');"); // 改 // int row = stmt.executeUpdate("UPDATE category SET cname='情趣用品' WHERE cid='1';"); // 删 int row = stmt.executeUpdate("DELETE FROM category WHERE cid= 2;"); System.out.println("row = " + row); stmt.close(); conn.close(); } }
-
能够使用JDBC操作事务
JDBC操作事务的相关3个方法在Connection接口中
setAutoCommit(false); 开启事务
commit(); 提交事务
rollback(); 回滚事务
-
能够编写JDBC工具类
public class JDBCUtils { // 1.将固定字符串定义为常量 private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/day19"; private static final String USER = "root"; private static final String PASSWORD = "root"; // 2.在静态代码块中注册驱动 static { try { System.out.println("我是静态代码块,注册驱动啦!"); Class.forName(DRIVER_CLASS_NAME); } catch (ClassNotFoundException e) { e.printStackTrace(); } } // 3.提供一个获取连接的方法`static Connection getConneciton();` // 这个方法是提供一个Connection给别人使用,有异常要让别人去处理 public static Connection getConnection() throws SQLException { Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); return conn; } // 4.定义关闭资源的方法`close(Connection conn, Statement stmt, ResultSet rs)` // 设计思想: 别人提供数据给我们使用,有问题要处理 public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } // 5.重载关闭方法`close(Connection conn, Statement stmt)` public static void close(Connection conn, Statement stmt) { // 直接调用关闭3个的方法 close(conn, stmt, null); } }
-
能够完成JDBC实现登录案例
将用户保存在数据库表中
public class Demo08 { public static void main(String[] args) throws SQLException { // 1.让用户输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号:"); String username = sc.next(); System.out.println("请输入密码:"); String password = sc.next(); // 2.使用SQL根据用户的账号和密码去数据库查询数据 Connection conn = JDBCUtils.getConnection(); Statement stmt = conn.createStatement(); String sql = "SELECT * FROM user WHERE name='" + username + "' AND password='" + password + "';"; System.out.println("sql = " + sql); ResultSet rs = stmt.executeQuery(sql); if (rs.next()) { // 3.如果查询到数据,说明登录成功 System.out.println("恭喜您, " + username + " 登陆成功!"); } else { // 4.如果查询不到数据,说明登录失败 System.out.println("账号或密码错误!"); } JDBCUtils.close(conn, stmt, rs); } }
-
能够通过PreparedStatement完成增、删、改、查
1.得到PreparedStatement String sql = "DELETE FROM employee WHERE id=?;"; PreparedStatement pstmt = conn.prepareStatement(sql); 2.给?赋值 pstmt.setXxx(第几个?, 具体值); 3.执行SQL语句 int row = pstmt.executeUpdate() // 增、删、改 ResultSet rs = pstmt.executeQuery() // 查
-
能够完成PreparedStatement改造登录案例
SQL不能拼接用户名和密码,改成?的SQL语句 // 2.使用SQL根据用户的账号和密码去数据库查询数据 Connection conn = JDBCUtils.getConnection(); String sql = "SELECT * FROM user WHERE name=? AND password=?;"; PreparedStatement pstmt = conn.prepareStatement(sql); // 给?赋值 pstmt.setString(1, username); pstmt.setString(2, password); ResultSet rs = pstmt.executeQuery();