《JDBC》笔记
回顾
-
理解mysql索引的作用
-- 创建单列索引 create index 索引名 on 表名(列名) -- 显示索引信息 show index from 表名 -- 删除索引 drop index 索引名 on 表名
-
能够理解事务的概念
- 事务的四大特性 ACID
- 原子性
- 一致性
- 隔离性
- 持久性
- mysql操作事务的语句
事务的操作 MySQL操作事务的语句 开启事务 start transaction 提交事务 commit 回滚事务 rollback 设置回滚点 savepoint 名字 回到回滚点 rollback to 名字 查询事务的自动提交情况 select @@autocommit 设置事务的手动提交方式 set @@autocommit=1/0 - 事务的四大特性 ACID
-
会使用mysql字符串函数
字符串函数 说明 CHAR_LENGTH(s) 获取字符串长度 CONCAT(s1,s2…sn) 拼接字符串 LOWER(s) 转成小写 UPPER(s) 转成大写 SUBSTR(s, start, length) 取子字符串 TRIM(s) 去掉前后的空格 -
会使用mysql日期函数
日期函数 说明 ADDDATE(d,n) 给日期加天数得到新的日期 CURDATE() 获取现在的日期 DATEDIFF(d1,d2) 计算2个日期相差的天数 NOW() 获取现在的日期和时间 -
能够理解JDBC的概念
- 由一组接口组成,实现类由数据库厂商实现
- 我们写的代码可以访问不同的数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5fZFg9K-1592210865252)(/assets/1562312829882.png)]
-
如何创建连接
DriverManager类中的静态方法 | 描述 |
---|---|
Connection getConnection (String url, String user, String password) | 通过URL,用户名,密码获取连接 |
Connection getConnection (String url, Properties info) | 通过URL,属性集合获取连接 |
-
连接数据库的四个参数
- 用户名
- 密码
- jdbc:mysql://localhost:3306/数据库名?参数=值 或 jdbc:mysql:///数据库名
- 驱动:com.mysql.jdbc.Driver
学习目标
- 能够使用Connection接口
- 能够使用Statement接口
- 能够使用ResultSet接口
- 能够使用JDBC操作事务
- 能够编写JDBC工具类
- 能够完成JDBC实现登录案例
- 能够通过PreparedStatement完成增、删、改、查
Connection和Statement接口
目标
- Connection接口的作用和方法
- Statement接口的作用和方法
Connection接口
作用
每次访问数据库之前,第一件事情是创建连接对象,代表数据库连接对象。
- 由DriverManager来创建
- 创建语句对象:Statement
方法
Connection接口中的方法 | 描述 |
---|---|
Statement createStatement() | 通过连接对象创建语句对象 |
Statement接口
作用
代表要执行的SQL语句
方法
三个方法都有一个字符串的参数,参数就是要执行的SQL语句
Statement接口中的方法 | 描述 |
---|---|
boolean execute(String sql) | (了解) 作用:执行SQL语句,可以执行任意的SQL语句, 通常用来执行DDL语句 返回值:如果有结果集返回true,如果没有结果集返回false 查询有结果集,增删改没有结果集 |
int executeUpdate(String sql) | 作用:用来执行DML中增删改操作 返回值:受影响的行数 |
ResultSet executeQuery(String sql) | 作用:用来执行DQL查询操作 返回值:查询到的结果集 |
释放资源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jObSpHxY-1592210865256)(assets/image-20200510092112830.png)]
- 需要释放的对象:关闭结果集ResultSet,语句对象Statement,连接对象Connection
- 释放顺序:先开的后关,后开的先关。
- 打开顺序:Connection -> Statement -> ResultSet
- 关闭顺序:ResultSet -> Statement -> Connection
- 写在哪里
- 可以放在try()代码块中,因为所有的接口都实现了AutoCloseable接口。放在try()括号中的代码必须是实现这个接口,才能自动完毕。
- 放在finally中去关闭
小结
-
Connection接口的作用是什么
创建语句对象
-
Statement接口的作用是什么
执行SQL语句
JDBC:执行DDL操作
目标
使用JDBC创建执行DDL操作
需求
建议:所有要执行的SQL语句,应该在SQLyog中确定它是正确的,才写到Java中去。
使用JDBC在MySQL的数据库中创建一张学生表
- id是主键,整数类型,自增长
- name是varchar(20),非空
- 性别是boolean类型
- 生日是date类型
/* 创建学生表
1. id是主键,整数类型,自增长
2. name是varchar(20),非空
3. 性别是boolean类型
4. 生日是date类型
*/
create table student(
id int primary key auto_increment,
name varchar(20) not null,
sex boolean, -- mysql中生成的是tinyint类型
birthday date
);
步骤
-
添加mysql的驱动包
-
创建连接
-
通过连接对象得到语句对象
-
通过语句对象发送SQL语句给服务器,执行SQL
-
通过语句对象发送SQL语句给服务器
-
释放资源
代码
package com.itheima;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 使用JDBC的DDL语句创建一张表
*/
public class Demo01DDL {
public static void main(String[] args) throws SQLException {
//1. 创建连接对象 (先在SQLyog中创建day22的数据库)
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day22", "root", "root");
//2. 通过连接对象得到语句对象
Statement statement = connection.createStatement();
//3. 通过语句对象执行DDL语句,注:SQL语句中不要有分号
statement.execute("create table student( id int primary key auto_increment, name varchar(20) not null, sex boolean, birthday date)");
//4. 释放资源,先开的后关,后开的先关
statement.close();
connection.close();
//5. 如果没有异常表示执行成功, 控制台输出创建成功
System.out.println("创建学生表成功");
}
}
小结
执行DDL语句使用Statement接口的哪个方法?
boolean execute(String sql)
在实际的应用中很少去使用JDBC来执行DDL语句,因为建库或建表这些代码会在mysql中先写好并执行。
通常是执行DML或DQL比较多
JDBC:执行DML操作
目标
使用JDBC来实现增删改操作
需求
- 向学生表添加4条记录
- 更新其中2条记录
- 删除其中1条记录
接口中的方法
Statement接口中的方法 | 描述 |
---|---|
int executeUpdate(String sql) | 作用:执行增删改操作 返回值:返回影响的行数 |
SQL语句
-- 插入操作
insert into student (name, sex, birthday) values ('name', 'sex', 'birthday'),('name', 'sex', 'birthday'),('name', 'sex', 'birthday'),('name', 'sex', 'birthday');
-- 更新操作,修改所有女生生日为今天
update student set birthday = curdate() where sex = 0;
-- 删除id为3的用户
delete from student where id=3;
代码
package com.itheima;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 使用JDBC来实现增删改操作
*/
public class Demo2DML {
//执行插入操作,添加4条记录
@Test
public void testInsert() throws SQLException {
//1.创建连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql:///day22", "root", "root");
//2.得到语句对象
Statement statement = connection.createStatement();
//3.执行DML,boolean类型既可以使用true或false,也可以使用1或0
int row = statement.executeUpdate("insert into student (name, sex, birthday) values ('孙悟空', true, '2011-02-12')," +
"('猪八戒', true, '1999-12-12'),('白骨精', false, '1993-02-06'),('嫦娥', false, '1999-11-23')");
//4.释放资源
statement.close();
connection.close();
//5.打印影响的行数
System.out.println("插入了" + row + "行记录");
}
@Test
public void testUpdate() throws SQLException {
//1.创建连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql:///day22", "root", "root");
//2.得到语句对象
Statement statement = connection.createStatement();
//3.执行DML,boolean类型既可以使用true或false,也可以使用1或0
int row = statement.executeUpdate("update student set birthday = curdate() where sex = 0");
//4.释放资源
statement.close();
connection.close();
//5.打印影响的行数
System.out.println("修改了" + row + "行记录");
}
@Test
public void testDelete() throws SQLException {
//1.创建连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql:///day22", "root", "root");
//2.得到语句对象
Statement statement = connection.createStatement();
//3.执行DML,boolean类型既可以使用true或false,也可以使用1或0
int row = statement.executeUpdate("delete from student where id=3");
//4.释放资源
statement.close();
connection.close();
//5.打印影响的行数
System.out.println("删除了" + row + "行记录");
}
}
小结
增删改使用statement接口中哪个方法?
int executeUpdate(String sql)
ResultSet接口
目标
- ResultSet接口的执行原理
- 接口中方法
原理图
方法
ResultSet接口中的方法 | 描述 |
---|---|
boolean next() | 1. 向下移动1行 2. 如果所指的位置有数据返回true,否则返回false |
数据类型 get数据类型(参数) | 获取这一行的数据,取值的方法有2种: 1. 通过列号取(从1开始,列号是整数类型) 2. 通过列名取(列名是一个字符串类型) |
ResultSet接口中的getXxx()方法
常用数据类型对应表
这只是建议,并不绝对这么写
注:日期类型返回的并不是java.util.Date,而是java.sql.Date
这三个类java.sql.Date, java.sql.Time, java.sql.Timestamp 共同的父类是java.util.Date
小结
ResultSet接口中的方法 | 描述 |
---|---|
boolean next() | 1. 向下移动一行 2. 如果所指的行有数据,返回true,否则返回false |
数据类型 getXxx(参数) | 获取这一行的数据,有两种方式: 1. 通过列号 2. 通过列名 |
JDBC:执行DQL操作
目标
使用JDBC实现DQL查询操作
需求
确保数据库中有3条以上的记录,查询所有的学员信息
查询结果
步骤
- 得到连接对象
- 得到语句对象
- 执行SQL语句后得到结果集ResultSet对象
- 循环遍历取出每一条记录
- 输出的控制台上
- 释放资源
代码
package com.itheima;
import java.sql.*;
/**
* 执行DQL操作
*/
public class Demo03DQL {
public static void main(String[] args) throws SQLException {
//1.创建连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql:///day22", "root", "root");
//2.获取语句对象
Statement statement = connection.createStatement();
//3.执行DQL,得到结果集
ResultSet resultSet = statement.executeQuery("select * from student");
//4.遍历结果集,输出每一行的数据
while (resultSet.next()) { //返回true表示有数据
//4.1使用列号
/*int id = resultSet.getInt(1);
String name = resultSet.getString(2);
boolean sex = resultSet.getBoolean(3);
Date birthday = resultSet.getDate(4);*/
//4.2使用列名
int id = resultSet.getInt("id"); //也可以使用别名,必须与mysql中列名能对应的
//String id = resultSet.getString("id"); //如果能自动转换,jdbc会自动转换
String name = resultSet.getString("name");
//int name = resultSet.getInt("name"); //不能转换,会报错
boolean sex = resultSet.getBoolean("sex");
Date birthday = resultSet.getDate("birthday");
System.out.println("编号:" + id + ",姓名:" + name + ",性别:" + sex + ",生日:" + birthday);
}
//5.释放资源:结果集,语句对象,连接对象
resultSet.close();
statement.close();
connection.close();
}
}
小结
查询数据库使用Statement的哪个方法?
ResultSet executeQuery(String sql)
创建JDBC数据库工具类
目标
创建一个可以通用的Jdbc数据库工具类
需求
上面的代码中出现了很多重复的代码,可以把这些公共代码抽取出来。
正常关闭的代码
package com.itheima;
import java.sql.*;
/**
* 执行DQL操作
*/
public class Demo03DQL {
public static void main(String[] args) {
//1. 先选中代码 2.按ctrl+alt+t
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1.创建连接对象
connection = DriverManager.getConnection("jdbc:mysql:///day22", "root", "root");
//2.获取语句对象
statement = connection.createStatement();
//3.执行DQL,得到结果集
resultSet = statement.executeQuery("select * from student");
//4.遍历结果集,输出每一行的数据
while (resultSet.next()) { //返回true表示有数据
//4.1使用列号
/*int id = resultSet.getInt(1);
String name = resultSet.getString(2);
boolean sex = resultSet.getBoolean(3);
Date birthday = resultSet.getDate(4);*/
//4.2使用列名
int id = resultSet.getInt("id"); //也可以使用别名,必须与mysql中列名能对应的
//String id = resultSet.getString("id"); //如果能自动转换,jdbc会自动转换
String name = resultSet.getString("name");
//int name = resultSet.getInt("name"); //不能转换,会报错
boolean sex = resultSet.getBoolean("sex");
Date birthday = resultSet.getDate("birthday");
System.out.println("编号:" + id + ",姓名:" + name + ",性别:" + sex + ",生日:" + birthday);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//5.释放资源:结果集,语句对象,连接对象。每个close方法都会抛出异常
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
步骤
创建类JdbcUtils包含2个方法:
- 将数据库的4个参数定义成常量
- 得到数据库的连接:getConnection()
- 关闭所有打开的资源:
close(Connection conn, Statement stmt),
close(Connection conn, Statement stmt, ResultSet rs)
代码
package com.itheima.utils;
import java.sql.*;
/**
* 创建工具类
* 1. 直接获取连接对象
* 2. 释放资源
*/
public class JdbcUtils {
/**
* 获取连接
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql:///day22", "root", "root");
}
/**
* 关闭连接
* 查询调用这个方法
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭连接
* 增删改没有结果集
*/
public static void close(Connection connection, Statement statement) {
//直接调用上面的方法
close(connection, statement, null);
}
}
使用工具类
package com.itheima;
import com.itheima.utils.JdbcUtils;
import java.sql.*;
/**
* 执行DQL操作
*/
public class Demo03DQL {
public static void main(String[] args) {
//1. 先选中代码 2.按ctrl+alt+t
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1.创建连接对象
connection = JdbcUtils.getConnection();
//2.获取语句对象
statement = connection.createStatement();
//3.执行DQL,得到结果集
resultSet = statement.executeQuery("select * from student");
//4.遍历结果集,输出每一行的数据
while (resultSet.next()) { //返回true表示有数据
//4.2使用列名
int id = resultSet.getInt("id"); //也可以使用别名,必须与mysql中列名能对应的
String name = resultSet.getString("name");
boolean sex = resultSet.getBoolean("sex");
Date birthday = resultSet.getDate("birthday");
System.out.println("编号:" + id + ",姓名:" + name + ",性别:" + sex + ",生日:" + birthday);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//5.释放资源:结果集,语句对象,连接对象。每个close方法都会抛出异常
JdbcUtils.close(connection, statement, resultSet);
}
}
}
小结
工具类主要有哪两个功能?
- 简化获取连接的代码
- 简化关闭连接的代码
JDBC:事务的处理
目标
使用JDBC来处理事务:实现银行转账的操作
API介绍
Connection接口中与事务有关的方法 | 说明 |
---|---|
void setAutoCommit(boolean autoCommit) | 设置事务的提交方式 true 表示自动提交 false 表示手动提交 如果要开启事务,设置为false |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
准备数据
-- 创建账户表:id,name,balance
create table account(
id int primary key auto_increment, -- 主键自动增长
name varchar(20), -- 账户名
balance int -- 账户的余额
);
-- 添加数据:Jack和Rose各1000块
insert into account values(null, 'Jack',1000),(null,'Rose',1000);
select * from account;
-- 模拟Jack转账给Rose 200块钱的功能:
-- 至少要执行2条SQL语句
-- 1. 更新Jack账户,从Jack中减去200
-- 2. 更新Rose账户,给Rose加200块
-- 执行第1条
update account set balance=balance-500 where name='Jack';
-- 执行第2条,可能会出现第1条语句执行成功,第2条语句没有执行,或者执行失败的情况
update account set balance=balance+500 where name='Rose';
-- 查询转账后结果
select * from account;
-- 还原2个账户的钱为1000
update account set balance = 1000;
开发步骤
没有事务的转账情况
- 获取连接
- 获取到Statement
- 使用Statement执行两次更新操作
- 最后关闭资源
使用事务的情况
- 获取连接
- 开启事务
- 获取到Statement
- 使用Statement执行两次更新操作
- 正常情况下提交事务
- 出现异常回滚事务
- 最后关闭资源
package com.itheima;
import com.itheima.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 通过事务实现转账的功能
*/
public class Demo04Transaction {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
//1.获取连接
connection = JdbcUtils.getConnection();
//1.5 手动提交事务
connection.setAutoCommit(false);
//2.创建语句对象
statement = connection.createStatement();
//3.执行更新操作:Jack扣钱
statement.executeUpdate("update account set balance=balance-500 where name='Jack'");
//模拟出现异常的情况
System.out.println(1 / 0);
//4.执行更新操作:Rose加钱
statement.executeUpdate("update account set balance=balance+500 where name='Rose'");
//4.5 提交事务
connection.commit();
//5.没有异常提示转账成功
System.out.println("转账成功");
} catch (Exception e) { //只要出现异常就输出转账失败
//回滚事务
try {
connection.rollback(); //就算出来了异常,日志文件也是会被清除
} catch (SQLException ex) {
ex.printStackTrace();
}
//6.有异常提示转账失败
System.out.println("转账失败");
}
//5.释放资源
finally {
JdbcUtils.close(connection, statement);
}
}
}
小结
Connection接口中与事务有关的方法 | 说明 |
---|---|
void setAutoCommit(boolean autoCommit) | 设置手动提交事务 设置为false就是手动提交事务 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
应用案例:用户登陆
目标
通过访问数据库,查询数据库中的用户,实现登录的功能
需求
- 有一张用户表id,name,password,添加2条用户记录
-- 建表
create table user(
id int primary key auto_increment,
username varchar(20) not null unique, -- 登录名不能重复
password varchar(32) not null
);
-- 插入记录
insert into user values(null, 'Jack','123'),(null,'Rose','456');
-- 什么时候登录成功,有记录就是登录成功
select * from user where username='Jack' and password='123';
-- 什么时候登录失败,没有查到记录
select * from user where username='Newboy' and password='890';
- 用户在控制台上输入用户名和密码,实现登录的功能
步骤
- 得到用户从控制台上输入的用户名和密码
- 调用下面写的登录方法来实现登录
- 写一个登录的方法
- 通过工具类得到连接
- 创建语句对象,使用拼接字符串的方式生成SQL语句
- 查询数据库,如果有记录则表示登录成功,否则登录失败
- 释放资源
代码
package com.itheima;
import com.itheima.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* 实现用户的登录
*/
public class Demo05Login {
public static void main(String[] args) throws SQLException {
//用户输入用户名和密码
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine(); //Jack
System.out.println("请输入密码:");
String password = scanner.nextLine(); //123
//开始登录,使用JDBC访问数据库,本质上就是一个查询操作
//1.获取连接
Connection connection = JdbcUtils.getConnection();
//2.得到语句对象
Statement statement = connection.createStatement();
//3.查询用户名和密码的记录
//注:SQL语句要拼接字符串
String sql = "select * from user where username='" + username + "' and password='" + password + "'";
System.out.println("生成的SQL语句:" + sql);
ResultSet resultSet = statement.executeQuery(sql);
//4.如果有结果集返回就登录成功,因为只有一条记录,所以不需要使用while遍历,使用if就可以
if (resultSet.next()) {
System.out.println("登录成功,欢迎您:" + username);
}
//5.没有结果集登录失败
else {
System.out.println("登录失败,请重试");
}
//6.释放资源
JdbcUtils.close(connection, statement, resultSet);
}
}
SQL注入问题
这个程序有一个致命的bug,因为我们的SQL语句是通过拼接字符串生成的,会有SQL注入的问题。
问题分析
-- 如果这么写是可以查询所有的记录的
select * from user where true;
-- SQL注入生成的语句
select * from user where username='newboy' and password='a' or '1'='1'
select * from user where false and false or true
解决方法:
我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。
要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。
小结
登录的SQL语句如何写?
select * from user where username='用户名' and password='密码'
PreparedStatement接口的执行原理
目标
- 为什么要使用PreparedStatement
- PreparedStatement的原理
继承结构
通过继承结构可以知道,它的父接口是Statement,Prepared准备好的SQL语句。MySQL中的语句也是要编译以后才执行,编译是需要消耗时间的。
比父接口更强大的地方:
- SQL语句会预先编译,执行效率会更高。
- 解决SQL注入的问题,更安全
- SQL语句代码的可读性更好,所有要替换的参数使用占位符,占位符是?问号
PreparedSatement的执行原理
如果执行相同的SQL语句,只需要编译一次,而且是预先编译。在创建语句对象的时候就提供了SQL语句,而不是等到执行的时候才提供SQL语句。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yiw38ExQ-1592210865280)(assets/image-20200510141135875.png)]
PreparedSatement的小结
- SQL语句是预先编译的,创建的时候提供SQL语句,执行的时候没有SQL语句
- 安全性更高,没有SQL注入的问题
- 代码的可读性会更好
PreparedStatement中相关的方法
目标
- 如何得到PreparedStatement对象
- PreparedStatement对象有哪些方法
Connection创建PreparedStatement对象
Connection接口中的方法 | 描述 |
---|---|
PreparedStatement prepareStatement(String sql) | 通过连接对象创建预编译语句对象, 创建的时候要提供SQL语句,语句有可能有占位符 如果有占位符,后面需要替换占位符为真实的值 |
接口中的方法
这两个方法都没有参数,不需要提供SQL语句
PreparedStatement接口中的方法 | 描述 |
---|---|
int executeUpdate() | 实现增删改的操作 返回影响的行数 |
ResultSet executeQuery() | 实现查询的操作,返回结果集 |
设置占位符的方法
PreparedStatement的方法 | 说明 |
---|---|
void set数据类型(int 参数1,数据类型 参数2) | 参数1:第几个占位符(从1开始) 参数2:替换占位符的真实值 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGLgMEIM-1592210865285)(assets/1562316815441.png)]
小结
PreparedStatement接口中的方法 | 描述 |
---|---|
int executeUpdate() | 实现增删改的操作 |
ResultSet executeQuery() | 实现查询的操作 |
PreparedStatement:执行DML操作
目标
- 向学生表中添加1条记录代码
- 将id为2的用户,姓名更新为"蜘蛛精",性别换成女
- 将id为4的学员删除
代码
package com.itheima;
import com.itheima.utils.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 1. 向学生表中添加1条记录代码
* 2. 将id为2的用户,姓名更新为"蜘蛛精",性别换成女
* 3. 将id为4的学员删除
*/
public class Demo06Prepared {
@Test
public void testInsert() throws SQLException {
//1.创建连接对象
Connection connection = JdbcUtils.getConnection();
//2.创建预编译的语句对象,提供了SQL语句
PreparedStatement ps = connection.prepareStatement("insert into student (name, sex, birthday) values (?, ?, ?)");
//3.替换占位符为真实的值
ps.setString(1, "二郎神");
ps.setBoolean(2, true);
ps.setDate(3, Date.valueOf("2011-02-20")); //注:这是java.sql.Date类中静态方法
//4.执行SQL语句
int row = ps.executeUpdate(); //没有SQL语句
//5.释放资源
JdbcUtils.close(connection, ps);
System.out.println("插入了" + row + "条记录");
}
//将id为2的用户,姓名更新为"蜘蛛精",性别换成女
@Test
public void testUpdate() throws SQLException {
//1.创建连接对象
Connection connection = JdbcUtils.getConnection();
//2.创建预编译的语句对象,提供了SQL语句
PreparedStatement ps = connection.prepareStatement("update student set name=?, sex=? where id = ?");
//3.替换占位符为真实的值
ps.setString(1, "蜘蛛精");
ps.setBoolean(2, false);
ps.setInt(3, 2);
//4.执行SQL语句
int row = ps.executeUpdate(); //没有SQL语句
//5.释放资源
JdbcUtils.close(connection, ps);
System.out.println("更新了" + row + "条记录");
}
//将id为4的学员删除
@Test
public void testDelete() throws SQLException {
//1.创建连接对象
Connection connection = JdbcUtils.getConnection();
//2.创建预编译的语句对象,提供了SQL语句
PreparedStatement ps = connection.prepareStatement("delete from student where id=?");
//3.替换占位符为真实的值
ps.setInt(1, 4);
//4.执行SQL语句
int row = ps.executeUpdate(); //没有SQL语句
//5.释放资源
JdbcUtils.close(connection, ps);
System.out.println("删除" + row + "条记录");
}
}
小结
执行DML使用哪个方法?
int executeUpdate()
案例:使用预编译语句改写登录程序
目标
改写前面的登录案例,解决SQL注入的问题
执行效果
代码
package com.itheima;
import com.itheima.utils.JdbcUtils;
import java.sql.*;
import java.util.Scanner;
/**
* 实现用户的登录
*/
public class Demo07Login {
public static void main(String[] args) throws SQLException {
//用户输入用户名和密码
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine(); //Jack
System.out.println("请输入密码:");
String password = scanner.nextLine(); //123
//1.获取连接
Connection connection = JdbcUtils.getConnection();
//2.得到预编译语句对象,注:占位符外面没有单引号
PreparedStatement preparedStatement = connection.prepareStatement("select * from user where username=? and password=?");
//3.替换占位符
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
//4.执行查询操作
ResultSet resultSet = preparedStatement.executeQuery(); //没有SQL语句的参数
if (resultSet.next()) { //如果有记录表示登录成功
System.out.println("登录成功,欢迎您:" + username);
}
else { //登录失败
System.out.println("登录失败,请重试");
}
//5.释放资源
JdbcUtils.close(connection, preparedStatement, resultSet);
}
}
小结
预编译语句可以有效的避免SQL注入的问题
案例:执行DQL封装成集合的操作
目标
-
表与类之间的关系
-
将查询的多条记录封装成List<Student>集合
表与类之间的关系
如果结果集关闭,结果集中的数据就不能使用了。在DAO层将结果集中的数据重新封装到一个新的对象中,就可以在DAO层放心的关闭结果集,以后只需要使用重新封装的这个对象即可。
- 如果是一条记录,封装成实体类对象
- 如果是多条记录,封装成一个集合
降低了与数据库的耦合度
这种映射关系称为ORM:Object Relational Mapping 对象关系映射
案例:查询多条记录
需求:
查询所有的学生类,封装成List<Student>返回
执行效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-537m68Iq-1592210865293)(assets/image-20200510150525749.png)]
开发步骤:
- 创建一个集合用于封装所有的记录
- 得到连接对象
- 得到语句对象,SQL语句没有占位符
- 得到结果集,每次循环封装一个学生对象
- 把封装好的学生数据放到集合中
- 关闭连接
- 以后使用数据,循环输出集合中学生对象
代码
package com.itheima.entity;
import java.sql.Date;
public class Student {
//建议属性名与表中列名相同,如果列名是多个单词:user_name,那么属性名:userName
private int id;
private String name;
private boolean sex;
private Date birthday;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex=" + sex +
", birthday=" + birthday +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
package com.itheima;
import com.itheima.entity.Student;
import com.itheima.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 将表中所有的记录封装成一个集合
*/
public class Demo08List {
public static void main(String[] args) throws SQLException {
//1.创建连接对象
Connection connection = JdbcUtils.getConnection();
//2.创建预编译的语句对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from student");
//3.执行查询操作
ResultSet resultSet = preparedStatement.executeQuery();
//4.遍历整个结果集
//创建一个集合
List<Student> students = new ArrayList<>();
while (resultSet.next()) {
//5.每条记录封装成一个学生对象
Student student = new Student();
student.setId(resultSet.getInt("id"));
student.setName(resultSet.getString("name"));
student.setBirthday(resultSet.getDate("birthday"));
student.setSex(resultSet.getBoolean("sex"));
//6.将学生对象添加到集合中
students.add(student);
}
//7.释放资源,关闭连接 (结果集不可用)
JdbcUtils.close(connection,preparedStatement, resultSet);
//8.后期可以使用集合,其实就是从数据库中查询出来的数据
students.forEach((student -> System.out.println(student)));
}
}
小结
表与类之间的对应关系是怎样的?
- 表结构->类结构
- 每条记录->对象
- 字段名->属性名
学习总结
-
能够使用Connection接口
Connection接口中的方法 描述 Statement createStatement() 创建语句对象,没有SQL参数 PreparedStatement prepareStatement(String sql) 创建预编译的语句对象,有SQL参数 -
能够使用Statement接口
以下三个方法都是有SQL语句的参数
Statement接口中的方法 描述 boolean execute(String sql) 作用:可以执行任意的SQL
返回值:如果有结果集返回true,没有就返回falseint executeUpdate(String sql) 作用:执行增删改
返回值:影响的行数ResultSet executeQuery(String sql) 作用:执行查询操作
返回值:结果集 -
能够使用ResultSet接口
ResultSet接口中的方法 描述 boolean next() 1. 向下移动一行
2. 如果当前行有数据,返回true,否则返回false数据类型 getXxx(参数) 从结果集中获取数据
1. 通过列号
2. 通过列名 -
能够使用JDBC操作事务
Connection接口中与事务有关的方法 说明 void setAutoCommit(boolean autoCommit) 设置为false,表示手动提交事务 void commit() 提交事务 void rollback() 回滚事务 -
能够编写JDBC工具类
- 得到数据库的连接:getConnection()
- 关闭所有打开的资源:
close(Connection conn, Statement stmt)
close(Connection conn, Statement stmt, ResultSet rs)
-
能够完成JDBC实现登录案例
-
能够通过PreparedStatement完成增、删、改、查
方法没有SQL参数:
PreparedStatement接口中的方法 | 描述 |
---|---|
int executeUpdate() | 实现增删改的操作,返回影响的行数 |
ResultSet executeQuery() | 实现查询的操作,返回结果集 |
作业
完成作业文件夹中:PreparedStatement练习
int executeUpdate(String sql) | 作用:执行增删改
返回值:影响的行数 |
| ResultSet executeQuery(String sql) | 作用:执行查询操作
返回值:结果集 |
-
能够使用ResultSet接口
ResultSet接口中的方法 描述 boolean next() 1. 向下移动一行
2. 如果当前行有数据,返回true,否则返回false数据类型 getXxx(参数) 从结果集中获取数据
1. 通过列号
2. 通过列名 -
能够使用JDBC操作事务
Connection接口中与事务有关的方法 说明 void setAutoCommit(boolean autoCommit) 设置为false,表示手动提交事务 void commit() 提交事务 void rollback() 回滚事务 -
能够编写JDBC工具类
- 得到数据库的连接:getConnection()
- 关闭所有打开的资源:
close(Connection conn, Statement stmt)
close(Connection conn, Statement stmt, ResultSet rs)
-
能够完成JDBC实现登录案例
-
能够通过PreparedStatement完成增、删、改、查
方法没有SQL参数:
PreparedStatement接口中的方法 | 描述 |
---|---|
int executeUpdate() | 实现增删改的操作,返回影响的行数 |
ResultSet executeQuery() | 实现查询的操作,返回结果集 |