学习目标
- 能够理解JDBC的作用
- 能够理解DriverManager类的作用
- 能够理解Connection接口的作用
- 能够理解Statement接口的作用
- 能够理解ResultSet接口的作用
- 能够使用JDBC执行DDL语句
- 能够使用JDBC执行DML语句
- 能够使用JDBC执行DQL语句
- 能够使用JDBC控制手动事务
- 能够完成登录案例
- 能够理解sql注入问题
- 能够使用PreparedStatement解决sql注入问题
第一章 JDBC概述
1.1 什么是JDBC
Java DataBase Connectivity:Java的数据库连接技术,它是一组Java语言编写的接口API,给多种数据库提供统一的访问接口,是Java访问数据库的标准规范
JDBC访问数据库需要驱动程序,利用驱动程序和数据库进行通信。驱动程序是Java语言编写的实现了JDBC接口的类,由第三方数据库厂商提供
1.2 JDBC的特点
- 它是一组接口,我们的程序是面向接口开发
- 不需要关注具体的实现细节,驱动程序由数据库厂商提供
1.3 面向接口开发的优势
会使用到的包 | 说明 |
---|---|
java.sql | JDBC的核心包 |
javax.sql | 数据库连接池的包 |
数据库的驱动 | 数据库厂商提供 |
JDBC的核心接口和类
接口或类 | 作用 |
---|---|
DriverManager类 | 创建数据库连接 |
Connection接口 | 表示一个数据库连接对象 |
Statement接口 | 表示一个发送给数据库执行的SQL语句 |
ResultSet接口 | 数据库返回的查询结果集 |
JDBC访问数据库的步骤
- 利用DriverManager创建数据库连接
- 获取一个连接对象,客户端利用Statement对象发送SQL到数据库
- 数据库执行SQL语句
- 数据库返回查询的结果集ResultSet给客户端
2.2 DriverManager类
作用
- 注册驱动,JDK1.5之后自动注册
- 创建数据库连接
JDBC连接数据库的四个参数
JDBC连接数据库的四个参数 | 值 |
---|---|
用户名 | root |
密码 | root |
驱动类全名 | 例如MySQL驱动: com.mysql.jdbc.Driver (MyDQL5之前) com.mysql.cj.jdbc.Driver (MySQL6开始) |
连接字符串 | jdbc:mysql://localhost:3306/itcast |
URL后面参数的作用
如果数据库出现乱码,可以指定参数 ?characterEncoding=utf8,表示让数据库以UTF-8编码来处理数据
jdbc:mysql://localhost:3306/itcast?characterEncoding=utf8
如果服务器是本地,端口号是默认的3306,可以简写为
jdbc:mysql:///itcast?characterEncoding=utf8
DriverManager类中的方法
DriverManager类中的静态方法 | 描述 |
---|---|
Connection getConnection (String url, String user, String password) | 根据连接字符串url、用户名、密码获取一个数据库连接对象 |
Connection getConnection (String url, Properties info) | 根据连接字符串url、属性集合获取一个数据库连接对象 |
应用
- 新建项目day16的空java工程
- 再新建一个模块jdbc
- 在jdbc模块下新建一个lib目录,添加mysql数据库驱动
- 在jdbc模块下新建一个测试类
package com.itheima.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* @description: 连接数据库demo
* @author: kelvin
* @date: 2019/8/29/0029-16:47
* @Version: 1.0.0
*/
public class DemoJDBC01 {
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/itcast";
//方式一:使用用户名、密码、URL得到连接对象
Connection connection = DriverManager.getConnection(url, "root", "root");
System.out.println(connection);
//方式二:使用属性文件和url得到连接对象
Properties properties = new Properties();
properties.put("user", "root");
properties.put("password", "root");
Connection connection1 = DriverManager.getConnection(url, properties);
System.out.println(connection1);
}
}
2.3 Connection接口
作用
表示一个连接对象,获取的是它的子类对象
方法
Connection接口中的方法 | 描述 |
---|---|
Statement createStatement() | 创建一个Statement对象,用来发送SQL语句给数据库服务器执行 |
2.4 Statement接口
作用
表示一个数据库执行的SQL语句,获取的是它的子类对象
方法
Statement接口中的方法 | 描述 |
---|---|
boolean execute(String sql) | 作用:可以执行任意的SQL语句,常用来执行DDL 返回值: true:返回的第一个结果是ResultSet对象 false:如果是更新计数或没有结果 |
int executeUpdate(String sql) | 作用:执行增删改操作 返回值:返回影响的记录行数 |
ResultSet executeQuery(String sql) | 作用:用于执行查询操作 返回值:数据库查询的结果集 |
2.5 ResultSet接口
原理图
方法
ResultSet接口中的方法 | 描述 |
---|---|
boolean next() | 将指针向下移动一行,如果当前行由数据,就返回true;否则,返回false |
数据类型 get数据类型(参数) | 1. 根据列名 2. 根据列号,从1开始编号 |
ResultSet接口中的getXXX()方法
方法签名 |
---|
boolean getBoolean(String columnLabel); |
byte getByte(String columnLabel); |
short getShort(String columnLabel); |
int getInt(String columnLabel); |
long getLong(String columnLabel); |
float getFloat(String columnLabel); |
double getDouble(String columnLabel); |
String getString(String columnLabel); |
常用的数据类型转换表
其中,下面的类都是 java.util.Date 的子类
java.sql.Date
java.sql.Time
java.sql.Timestamp
释放资源
每次访问数据库结束,都需要释放资源
- 需要释放的对象:Connection、Statement、ResultSet
- 释放顺序:和创建的顺序相反,
创建是时候:connnection->statement->resultSet
释放的时候:resultSet->statement->connection - 写在哪里:finally的代码块
- 是否可以放在try()代码块中:需要实现AutoCloseable接口,均可以放
第三章 JDBC的应用
3.1 执行DDL操作
需求
使用JDBC在MySQL的数据库中创建一张学生表
- id是主键,整数类型,自增长
- name是varchar(20),非空
- 性别是boolean类型
- 生日是date类型
-- 创建student学生表 create table student( id int primary key auto_increment, name varchar(20) not null, sex boolean, -- 自动转成微整型1 表示真 0 表示假 birthday date );
步骤
package com.itheima.jdbc;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class DemoJDBC03 {
@Test
public void create() throws SQLException {
String url = "jdbc:mysql://localhost:3306/itcast";
// 1. 创建数据库连接
Connection connection = DriverManager.getConnection(url, "root", "root");
// 2. 获取语句对象
Statement statement = connection.createStatement();
// 3. 执行DDL语句
statement.execute("create table student(id int primary key auto_increment,\tname varchar(20) not null,sex boolean,birthday date)");
// 4. 关闭资源
statement.close();
connection.close();
}
}
3.2 执行DML操作
需求
-- 添加4条记录
insert into student (name,sex,birthday) values ('孙悟空',1,'1999-2-10'),('猪八戒',1,'1999-2-10'),('唐三藏',1,'1999-2-10'),('沙悟净',1,'1999-2-10');
-- 更新1条,将唐三藏,姓名改成嫦娥,性别改成女,生日换成1997-04-10
update student set name='嫦娥', sex=0, birthday='1997-04-10' where name='唐三藏';
-- 删除4号记录
delete from student where name='沙悟净';
代码
package com.itheima.jdbc;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @description: JDBC执行DML语句
* @author: kelvin
* @date: 2019/8/29/0029-17:32
* @Version: 1.0.0
*/
public class DemoJDBC04 {
/**
* 执行新增SQL
* @author kelvin
* @date 2019/8/29/0029-17:37
* @param
* @return void
*/
@Test
public void insert() throws SQLException {
String url = "jdbc:mysql://localhost:3306/itcast?characterEncoding=utf8";
// 1.创建数据库连接
Connection connection = DriverManager.getConnection(url, "root", "root");
// 2.获取语句对象
Statement statement = connection.createStatement();
// 3.执行新增SQL
int rows = statement.executeUpdate("insert into student (name,sex,birthday) values ('孙悟空',1,'1999-2-10'),('猪八戒',1,'1999-2-10'),('唐三藏',1,'1999-2-10'),('沙悟净',1,'1999-2-10');");
System.out.println("新增了" + rows + "行");
// 4.关闭资源
statement.close();
connection.close();
}
/**
* 执行更新SQL
* @author kelvin
* @date 2019/8/29/0029-17:40
* @param
* @return void
*/
@Test
public void update() throws SQLException {
String url = "jdbc:mysql://localhost:3306/itcast?characterEncoding=utf8";
// 1.创建数据库连接
Connection connection = DriverManager.getConnection(url, "root", "root");
// 2.获取语句对象
Statement statement = connection.createStatement();
// 3.执行更新SQL
int rows = statement.executeUpdate("update student set name='嫦娥', sex=0, birthday='1997-04-10' where name='唐三藏'");
System.out.println("更新了" + rows + "行");
// 4.关闭资源
statement.close();
connection.close();
}
/**
* 执行删除SQL
* @author kelvin
* @date 2019/8/29/0029-17:40
* @param
* @return void
*/
@Test
public void delete() throws SQLException {
String url = "jdbc:mysql://localhost:3306/itcast?characterEncoding=utf8";
// 1.创建数据库连接
Connection connection = DriverManager.getConnection(url, "root", "root");
// 2.获取语句对象
Statement statement = connection.createStatement();
// 3.执行删除SQL
int rows = statement.executeUpdate("delete from student where name='沙悟净'");
System.out.println("删除了" + rows + "行");
// 4.关闭资源
statement.close();
connection.close();
}
}
3.3 执行DQL操作
需求
查询所有的学员信息
查询结果
步骤
package com.itheima.jdbc;
import java.sql.*;
/**
* @description: 执行DQL语句
* @author: kelvin
* @date: 2019/8/29/0029-17:44
* @Version: 1.0.0
*/
public class DemoJDBC05 {
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/itcast?characterEncoding=utf8";
// 1.获取连接对象
Connection connection = DriverManager.getConnection(url, "root", "root");
// 2.获取语句对象
Statement statement = connection.createStatement();
// 3.执行DQL语句
ResultSet resultSet = statement.executeQuery("select * from student");
// 4.遍历ResultSet
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
boolean sex = resultSet.getBoolean("sex");
Date birthday = resultSet.getDate("birthday");
System.out.println("id: " + id + ", name: " + name + ",sex:" + (sex ? '男': '女') + ",birthday: " + birthday);
}
// 5.关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
第四章 封装JDBC工具类
需求
上面的代码中出现了很多重复的代码,把这些代码抽取到一个公共类里面
步骤
创建类JdbcUtils包含2个方法:
- 得到数据库的连接:getConnection()
- 关闭所有打开的资源:
close(Connection conn, Statement stmt),
close(Connection conn, Statement stmt, ResultSet rs)代码
package com.itheima.utils;
import java.sql.*;
/**
数据库的工具类 */
public class JdbcUtils {
private static String URL = "jdbc:mysql://localhost:3306/day16";
private static String USER_NAME = "root";
private static String PASSWORD = "root";
/**
得到连接对象
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER_NAME, PASSWORD);
}
/**
释放资源,关闭连接等对象
*/
public static void close(Connection connection, Statement statement) {
//先判断对象是否为空
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
释放资源,关闭连接等对象
*/
public static void close(Connection connection, Statement statement, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
close(connection, statement);
}
}
第五章 JDBC事务
5.1 API介绍
Connection接口中与事务有关的方法 | 说明 |
---|---|
void setAutoCommit(boolean autoCommit) | 设置事务模式,参数是false表示手动提交;参数是true表示自动提交 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
5.2 事务控制案例
sql脚本:
create table account(
id int(11) primary key auto_increment,
name varchar(20),
money float
);
insert into account values(null,"tom",1000),(null,"rose",1000);
没有事务的转账情况
- 获取连接
- 获取到Statement
- 使用Statement执行两次更新操作
最后关闭资源
package com.itheima; import com.itheima.utils.JdbcUtils; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; /** 在JDBC中实现转账的操作 */ public class Demo06Transaction { public static void main(String[] args) { Connection connection = null; Statement statement = null; try { //1.得到连接对象 connection = JdbcUtils.getConnection(); //2.得到语句对象 statement = connection.createStatement(); //3.执行更新操作2次,扣钱,加钱 statement.executeUpdate("update account set money = money - 100 where name='tom'"); statement.executeUpdate("update account set money = money + 100 where name='Rose'"); System.out.println("转账成功"); } catch (SQLException e) { e.printStackTrace(); System.out.println("转账失败"); } finally { //4.释放资源 JdbcUtils.close(connection, statement); } } }
使用事务的情况
获取连接
- 开启事务
- 获取到Statement
- 使用Statement执行两次更新操作
- 正常情况下提交事务
- 出现异常回滚事务
- 最后关闭资源
package com.itheima; import com.itheima.utils.JdbcUtils; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; /** 在JDBC中实现转账的操作 */ public class Demo06Transaction { public static void main(String[] args) { Connection connection = null; Statement statement = null; try { //1.得到连接对象 connection = JdbcUtils.getConnection(); //开启事务 connection.setAutoCommit(false); //2.得到语句对象 statement = connection.createStatement(); //3.执行更新操作2次,扣钱,加钱 statement.executeUpdate("update account set money = money - 100 where name='Jack'"); //模拟代码出现异常的情况 int i = 1 / 0; statement.executeUpdate("update account set money = money + 100 where name='Rose'"); //提交事务 connection.commit(); System.out.println("转账成功"); } catch (Exception e) { e.printStackTrace(); try { //回滚事务 connection.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } System.out.println("转账失败"); } finally { //4.释放资源 JdbcUtils.close(connection, statement); } } }
第六章 案例:用户登陆
6.1 实现用户登录
需求
通过用户名和密码能够查询到数据库中记录表示登录成功,否则登录失败
步骤和代码实现
创建一张用户表,添加2条用户记录
-- 建表 create table `user`( id int primary key auto_increment, name varchar(20), password varchar(32) ); -- 插入记录 insert into `user` values(null,'Jack','123'),(null,'Rose','456'); select * from user; -- Jack登陆成功 select * from user where name='Jack' and password='123'; -- Rose登陆成功 select * from user where name='Rose' and password='456';
用户在控制台上输入用户名和密码,实现登录的功能。步骤如下:
- 得到用户从控制台上输入的用户名和密码
- 调用下面写的登录方法来实现登录
- 写一个登录的方法
- 通过工具类得到连接
- 创建语句对象,使用拼接字符串的方式生成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 name = scanner.nextLine(); System.out.println("请输入密码:"); String password = scanner.nextLine(); //访问数据库 //1.通过工具类得到连接对象 Connection connection = JdbcUtils.getConnection(); //2.通过连接对象创建语句对象 Statement statement = connection.createStatement(); //3.执行SQL语句,使用字符串拼接的方式创建SQL语句 String sql = "select * from user where name='" + name + "' and password='" +password+ "'"; System.out.println("服务器运行的SQL语句:" + sql); ResultSet resultSet = statement.executeQuery(sql); //4.查询得到结果集,如果结果集中有记录表示登录成功,否则登录失败 if(resultSet.next()) { //为真表示有记录 System.out.println("欢迎您" + name + ",登录成功!" ); } else { System.out.println("登录失败"); } //5.调用工具类释放资源 JdbcUtils.close(connection,statement,resultSet); } }
6.2 SQL注入问题
访问步骤
- 得到用户从控制台上输入的用户名和密码
输入下面的用户名和密码,即可登陆成功
请输入用户名:
Tom
请输入密码:
123' or '1' = '1
服务器执行SQL语句:select * from user where name='Tom' and password='123' or '1' = '1'
欢迎您Tom,登陆成功!
问题分析
-- 有SQL注入问题的SQL语句
select * from user where name='newboy' and password='a' or '1'='1'
-- SQL的where条件分析
select * from user where true;
select * from user where false and false or true
我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。
要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。
解决问题:不能使用Statement接口,应该使用它的子接口:PreparedStatement,以后我们都使用这个子接口进行编程
第七章 PreparedStatement接口
7.1 接口介绍
继承结构
成员方法
Connection接口中的方法
方法 | 描述 |
---|---|
PreparedStatement prepareStatement(String sql) | 创建一个预编译的SQL语句 |
PreparedStatement接口中的方法
方法 | 描述 |
---|---|
int executeUpdate() | 执行增删改 返回值是影响的行数 |
ResultSet executeQuery() | 查询 返回值是结果集 |
void set数据类型(int 参数1,参数2) | 设置占位符参数 参数1:第几个占位符,从1开始计算 参数2:占位符的值 |
PreparedStatement设置参数
执行原理
7.2 执行DML操作
需求
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;
/**
使用PreparedStatement实现增删改操作
*/
public class Demo07PreparedDML {
@Test
public void insert() throws SQLException {
//1.得到连接对象
Connection connection = JdbcUtils.getConnection();
//2.通过连接对象创建预编译的语句对象,提供占位符的SQL语句
PreparedStatement ps = connection.prepareStatement("insert into student values(null,?,?,?)");
//3.替换占位符的值
ps.setString(1,"白骨精");
ps.setBoolean(2, false);
ps.setDate(3, Date.valueOf("2011-02-22"));
//4.执行增删改的操作
int row = ps.executeUpdate();
//5.返回影响的行数
System.out.println("添加了" + row + "行记录");
//6.释放资源
JdbcUtils.close(connection, ps);
}
/*
将id为2的用户,姓名更新为"天蓬元帅",生日改成"2000-9-9"
*/
@Test
public void update() throws SQLException {
//1.得到连接对象
Connection connection = JdbcUtils.getConnection();
//2.通过连接对象创建预编译的语句对象,提供占位符的SQL语句
PreparedStatement ps = connection.prepareStatement("update student set name=?, birthday=? where id=?");
//3.替换占位符的值
ps.setString(1,"天蓬元帅");
ps.setDate(2, Date.valueOf("2000-9-9"));
ps.setInt(3,2);
//4.执行增删改的操作
int row = ps.executeUpdate();
//5.返回影响的行数
System.out.println("更新" + row + "行记录");
//6.释放资源
JdbcUtils.close(connection, ps);
}
@Test
public void delete() throws SQLException {
//1.得到连接对象
Connection connection = JdbcUtils.getConnection();
//2.通过连接对象创建预编译的语句对象,提供占位符的SQL语句
PreparedStatement ps = connection.prepareStatement("delete from student where id=?");
//3.替换占位符的值
ps.setInt(1,1);
//4.执行增删改的操作
int row = ps.executeUpdate();
//5.返回影响的行数
System.out.println("删除" + row + "行记录");
//6.释放资源
JdbcUtils.close(connection, ps);
}
}
7.3 改写用户登陆案例
代码
package com.itheima;
import com.itheima.utils.JdbcUtils;
import java.sql.*;
import java.util.Scanner;
/**
实现用户的登录
*/
public class Demo08PreparedLogin {
public static void main(String[] args) throws SQLException {
//用于用户的输入
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
String sql = "select * from user where name=? and password=?";
//访问数据库
//1.通过工具类得到连接对象
Connection connection = JdbcUtils.getConnection();
//2.通过连接对象创建预编译的语句对象
PreparedStatement ps = connection.prepareStatement(sql);
//替换占位符
ps.setString(1,name);
ps.setString(2,password);
//3.执行SQL语句,使用字符串拼接的方式创建SQL语句
ResultSet resultSet = ps.executeQuery();
//4.查询得到结果集,如果结果集中有记录表示登录成功,否则登录失败
if(resultSet.next()) { //为真表示有记录
System.out.println("欢迎您" + name + ",登录成功!" );
}
else {
System.out.println("登录失败");
}
//5.调用工具类释放资源
JdbcUtils.close(connection,ps,resultSet);
}
}
7.4 执行DQL并封装结果集
表与类之间的关系
- 如果查询的结果集只有一条记录,封装成一个Student对象。(实体类:封装数据)
- 如果有多个记录,封装成List
对象
类与表之间对应关系:ORM 对象关系映射(Object Relational Mapping)
步骤
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;
/**
将表中的记录封装成List<Student>
*/
public class Demo09List {
public static void main(String[] args) throws SQLException {
List<Student> students = new ArrayList<>();
//1.创建连接对象
Connection connection = JdbcUtils.getConnection();
//2.得到预编译的语句对象
PreparedStatement ps = connection.prepareStatement("select * from student");
//3.查询得到结果集
ResultSet rs = ps.executeQuery();
//4.遍历结果集,将每条记录封装成一个Student对象
while(rs.next()) {
Student student = new Student();
//封装所有的属性
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setSex(rs.getBoolean("sex"));
student.setBirthday(rs.getDate("birthday"));
//5.添加到List集合中
students.add(student);
}
//6.释放资源
JdbcUtils.close(connection,ps,rs);
//7.输出List集合
students.forEach(System.out::println);
}
}
作业
1.练习上课所讲内容
2.完成转账案例
3.完成登录案例,并解决sql注入