目录
4.5.1需求:使用JDBC再MySQL数据库中创建一张学生表
4.7.3需求:确保数据库中有三条以上的记录,查询所有学员信息
6.3 Connection 创建 PreparedStatement 对象
6.7.1 案例:使用 PreparedStatement 查询一条数据,封装成一个学生 Student 对象
6.7.2案例:将多条记录封装成集合 List,集合中每个元素是一个 JavaBean 实体类
6.8 PreparedStatement 执行 DML 操作
1.JDBC入门
1.1客户端操作 MySQL 数据库的方式:
- 使用第三方客户端来访问 MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL
- 使用 MySQL 自带的命令行方式
- 通过 Java 来访问 MySQL 数据库,今天要学习的内容
1.1.1什么是JDBC
JDBC 规范定义接口,具体的实现由各大数据库厂商来实现。
JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。
使用 JDBC 的好处:
- 程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
- 使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库
1.1.2使用JDBC开发使用到的包:
会使用到的包 说明 | |
java.sql | 所有与 JDBC 访问数据库相关的接口和类 |
javax.sql | 数据库扩展包,提供数据库额外的功能。如:连接池 |
数据库的驱动 | 由各大数据库厂商提供,需要额外去下载,是对 JDBC 接口实现的类 |
1.2JDBC的核心API
接口或类 作用 | |
DriverManager 类 |
|
Connection 接口 | 一个连接对象,可用于创建 Statement 和 PreparedStatement 对象 |
Statement 接口 | 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器。 |
PreparedStatemen 接口 | 一个 SQL 语句对象,是 Statement 的子接口 |
ResultSet 接口 | 用于封装数据库查询的结果集,返回给客户端 Java 程序 |
1.3导入启动Jar包
1.4加载和注册驱动
加载和注册驱动的方法 描述 | |
Class.forName(数据库驱动实现类) | 加载和注册数据库驱动,数据库驱动由 mysql 厂商 "com.mysql.jdbc.Driver" |
为什么可以这样注册驱动?
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//抛出类找不到的异常,注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
}
}
com.mysql.jdbc.Driver源代码:
// Driver 接口,所有数据库厂商必须实现的接口,表示这是一个驱动类。
注:从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。Class.forName 这句话可以省略。
2.DriverManager类
2.1DriverManager作用:
- 管理和注册驱动
- 创建数据库的连接
2.2类中的方法:
DriverManager 类中的静态方法 描述 | |
Connection getConnection (String url, String user, String password) | 通过连接字符串,用户名,密码来得到 数据库的连接对象 |
Connection getConnection (String url, Properties info) | 通过连接字符串,属性对象来得到连接 对象 |
2.3使用JDBC连接数据库的四个参数:
JDBC 连接数据库的四个参数 说明 | |
用户名 | 登录的用户名 |
密码 | 登录的密码 |
连接字符串 URL | 不同的数据库 URL 是不同的,mysql 的写法 jdbc:mysql://localhost:3306/数据库[?参数名=参数值] |
驱动类的字符串名 | com.mysql.jdbc.Driver |
2.4连接数据库的URL地址格式:
协议名:子协议://服务器名或IP地址:端口号/数据库名?参数=参数值
2.4.1MySQL写法:
2.4.2MySQL中可以简写:
前提:必须是本地服务器,端口号是3306
jdbc:mysql:///数据库名
2.4.3乱码的处理
如果数据库出现乱码,可以指定参数: ?characterEncoding=utf8,表示让数据库以 UTF-8 编码来处理数据。
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
2.5案例:得到MySQL的数据库连接对象
1.使用用户名,密码,URL得到连接对象
2.使用属性文件和URL得到连接对象
3.Connection接口:
3.1Connection作用:
Connection 接口,具体的实现类由数据库的厂商实现,代表一个连接对象。
3.2Connection方法:
4Statement接口
4.1JDBC访问数据库的步骤
- 注册和加载驱动(可以省略)
- 获取连接
- Connection 获取 Statement 对象
- 使用 Statement 对象执行 SQL 语句
- 返回结果集
- 释放资源
4.2Statement作用:
代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
4.3Statement中的方法:
Statement 接口中的方法 描述 | |
int executeUpdate(String sql) | 用于发送 DML 语句,增删改的操作,insert、update、delete 参数:SQL 语句 返回值:返回对数据库影响的行数 |
ResultSet executeQuery(String sql) | 用于发送 DQL 语句,执行查询的操作。select 参数:SQL 语句 返回值:查询的结果集 |
4.4释放资源
- 需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
- 释放原则:先开的后关,后开的先关。ResultSet à Statement à Connection
- 放在哪个代码块中:finally 块
4.5执行DDL操作
4.5.1需求:使用JDBC再MySQL数据库中创建一张学生表
4.5.2代码
import java.sql.*;
/**
* 执行DDL语句
*/
public class JDBCDemo7 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
//3.定义sql
String sql = "select * from account";
//4.获取执行sql对象
stmt = conn.createStatement();
//5.执行sql
rs = stmt.executeQuery(sql);
//6.处理结果
//循环判断游标是否是最后一行末尾。
while(rs.next()){
//获取数据
//6.2 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "---" + name + "---" + balance);
}
/* //6.1 让游标向下移动一行
if(rs.next()){
//判断是否有数据
//6.2 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "---" + name + "---" + balance);
}
//6.1 让游标向下移动一行
if(rs.next()){
//判断是否有数据
//6.2 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "---" + name + "---" + balance);
}
//6.1 让游标向下移动一行
if(rs.next()){
//判断是否有数据
//6.2 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "---" + name + "---" + balance);
}
//6.1 让游标向下移动一行
if(rs.next()){
//判断是否有数据
//6.2 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "---" + name + "---" + balance);
}*/
/* //6.1 让游标向下移动一行
rs.next();
//6.2 获取数据
int id2 = rs.getInt(1);
String name2 = rs.getString("name");
double balance2 = rs.getDouble(3);
System.out.println(id2 + "---" + name2 + "---" + balance2);
//6.1 让游标向下移动一行
rs.next();
//6.2 获取数据
int id3 = rs.getInt(1);
String name3 = rs.getString("name");
double balance3 = rs.getDouble(3);
System.out.println(id3 + "---" + name3 + "---" + balance3);*/
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//7.释放资源
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();
}
}
}
}
}
以下程序较为优化:
4.6执行DML操作
需求:向学生表中添加 4 条记录,主键是自动增长
步骤:
- 创建连接对象
- 创建 Statement 语句对象
- 执行 SQL 语句:executeUpdate(sql)
- 返回影响的行数
- 释放资源
4.7执行DQL操作
4.7.1ResultSet接口:
作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
接口中的方法:
ResultSet 接口中的方法 描述 | |
boolean next() |
|
数据类型 getXxx() |
|
4.7.2常用数据类型转换表
SQL 类型 Jdbc 对应方法 返回类型 | ||
BIT(1) bit(n) | getBoolean() | boolean |
TINYINT | getByte() | byte |
SMALLINT | getShort() | short |
INT | getInt() | int |
BIGINT | getLong() | long |
CHAR,VARCHAR | getString() | String |
Text(Clob) Blob | getClob getBlob() | Clob Blob |
DATE | getDate() | java.sql.Date 只代表日期 |
TIME | getTime() | java.sql.Time 只表示时间 |
TIMESTAMP | getTimestamp() | java.sql.Timestamp 同时有日期和时间 |
java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date
4.7.3需求:确保数据库中有三条以上的记录,查询所有学员信息
步骤:
- 得到连接对象
- 得到语句对象
- 执行 SQL 语句得到结果集 ResultSet 对象
- 循环遍历取出每一条记录
- 输出的控制台上
- 释放资源
结果:
import itcast.domain.Emp;
import itcast.util.JDBCUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* * 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
*/
public class JDBCDemo8 {
public static void main(String[] args) {
List<Emp> list = new JDBCDemo8().findAll2();
System.out.println(list);
System.out.println(list.size());
}
/**
* 查询所有emp对象
* @return
*/
public List<Emp> findAll(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<Emp> list = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
//3.定义sql
String sql = "select * from emp";
//4.获取执行sql的对象
stmt = conn.createStatement();
//5.执行sql
rs = stmt.executeQuery(sql);
//6.遍历结果集,封装对象,装载集合
Emp emp = null;
list = new ArrayList<Emp>();
while(rs.next()){
//获取数据
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
// 创建emp对象,并赋值
emp = new Emp();
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
//装载集合
list.add(emp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
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();
}
}
}
return list;
}
4.7.4关于ResultSet接口中的注意事项:
- 如果光标在第一行之前,使用 rs.getXX()获取列值,报错:Before start of result set
- 如果光标在最后一行之后,使用 rs.getXX()获取列值,报错:After end of result set
- 使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection
5.数据库工具类JdbcUtils
什么时候自己创建工具类?
如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。
5.1需求:
上面写的代码中出现了很多重复的代码,可以把这些公共代码抽取出来
5.2创建类 JdbcUtil 包含 3 个方法:
- 可以把几个字符串定义成常量:用户名,密码,URL,驱动类
- 得到数据库的连接:getConnection()
- 关闭所有打开的资源:
close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类
*/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/**
* 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
*/
static{
//读取资源文件,获取值。
try {
//1. 创建Properties集合类。
Properties pro = new Properties();
//获取src路径下的文件的方式--->ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
// System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties
//2. 加载文件
// pro.load(new FileReader("D:\\IdeaProjects\\itcast\\day04_jdbc\\src\\jdbc.properties"));
pro.load(new FileReader(path));
//3. 获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4. 注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
* @return 连接对象
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
/**
* 释放资源
* @param stmt
* @param conn
*/
public static void close(Statement stmt,Connection conn){
if( stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
* @param stmt
* @param conn
*/
public static void close(ResultSet rs,Statement stmt, Connection conn){
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.3案例:用户登录
5.3.1需求:
create table user (
id int primary key auto_increment,
name varchar(20),
password varchar(20)
)
insert into user values (null,'jack','123'),(null,'rose','456');
-- 登录, SQL 中大小写不敏感
select * from user where name='JACK' and password='123';
-- 登录失败
select * from user where name='JACK' and password='333';
5.3.2步骤:
import itcast.util.JDBCUtils;
import java.sql.*;
import java.util.Scanner;
/**
* 练习:
* * 需求:
* 1. 通过键盘录入用户名和密码
* 2. 判断用户是否登录成功
*/
public class JDBCDemo9 {
public static void main(String[] args) {
//1.键盘录入,接受用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//2.调用方法
boolean flag = new JDBCDemo9().login2(username, password);
//3.判断结果,输出不同语句
if(flag){
//登录成功
System.out.println("登录成功!");
}else{
System.out.println("用户名或密码错误!");
}
}
/**
* 登录方法
*/
public boolean login(String username ,String password){
if(username == null || password == null){
return false;
}
//连接数据库判断是否登录成功
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//1.获取连接
try {
conn = JDBCUtils.getConnection();
//2.定义sql
String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
System.out.println(sql);
//3.获取执行sql的对象
stmt = conn.createStatement();
//4.执行查询
rs = stmt.executeQuery(sql);
//5.判断
/* if(rs.next()){//如果有下一行,则返回true
return true;
}else{
return false;
}*/
return rs.next();//如果有下一行,则返回true
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,stmt,conn);
}
return false;
}
5.3.3SQL注入问题
当我们输入以下密码,发现账号也可以登录成功
问题分析:
6.PreparedStatement 接口
6.1继承结构与作用:
PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句。
6.2PreparedSatement 的执行原理
- 因为有预先编译的功能,提高 SQL 的执行效率。
- 可以有效的防止 SQL 注入的问题,安全性更高。
6.3 Connection 创建 PreparedStatement 对象
Connection 接口中的方法 描述 | |
PreparedStatement prepareStatement(String sql) | 指定预编译的 SQL 语句,SQL 语句中使用占位符 ?创建一个语句对象 |
6.4 PreparedStatement 接口中的方法:
PreparedStatement 接口中的方法 描述 | |
int executeUpdate() | 执行 DML,增删改的操作,返回影响的行数。 |
ResultSet executeQuery() | 执行 DQL,查询的操作,返回结果集 |
6.5PreparedSatement 的好处
- prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
- 安全性更高,没有 SQL 注入的隐患。
- 提高了程序的可读性
6.6使用 PreparedStatement 的步骤:
- 编写 SQL 语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?";
- 获得 PreparedStatement 对象
- 设置实际参数:setXxx(占位符的位置, 真实的值)
- 执行参数化 SQL 语句
- 关闭资源
PreparedStatement 中设置参数的方法 描述 | |
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 值。 |
/**
* 登录方法,使用PreparedStatement实现
*/
public boolean login2(String username ,String password){
if(username == null || password == null){
return false;
}
//连接数据库判断是否登录成功
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//1.获取连接
try {
conn = JDBCUtils.getConnection();
//2.定义sql
String sql = "select * from user where username = ? and password = ?";
//3.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
//给?赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
//4.执行查询,不需要传递sql
rs = pstmt.executeQuery();
//5.判断
/* if(rs.next()){//如果有下一行,则返回true
return true;
}else{
return false;
}*/
return rs.next();//如果有下一行,则返回true
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,pstmt,conn);
}
return false;
}
}
6.7表与类的关系
6.7.1 案例:使用 PreparedStatement 查询一条数据,封装成一个学生 Student 对象
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;
public class Demo9Student {
public static void main(String[] args) throws SQLException {
//创建学生对象
Student student = new Student();
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("select * from student where id=?");
//设置参数
ps.setInt(1,2);
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()) {
//封装成一个学生对象
student.setId(resultSet.getInt("id"));
student.setName(resultSet.getString("name"));
student.setGender(resultSet.getBoolean("gender"));
student.setBirthday(resultSet.getDate("birthday"));
}
//释放资源
JdbcUtils.close(connection,ps,resultSet);
//可以数据
System.out.println(student);
} }
6.7.2案例:将多条记录封装成集合 List<Student>,集合中每个元素是一个 JavaBean 实体类
![](https://img-blog.csdnimg.cn/1e1d2a55af1846bc8a546bf9eea21b7f.png)
![](https://img-blog.csdnimg.cn/20c1453502be482aaf401da570275ce4.png)
6.8 PreparedStatement 执行 DML 操作
7.JDBC 事务的处理
7.1 准备数据
![](https://img-blog.csdnimg.cn/566be0324aac49d4b7b39468a378ce0a.png)
7.2API介绍
Connection 接口中与事务有关的方法 说明 | |
void setAutoCommit(boolean autoCommit) | 参数是 true 或 false 如果设置为 false,表示关闭自动提交,相当于开启事务 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
7.3开发步骤
- 获取连接
- 开启事务
- 获取到 PreparedStatement
- 使用 PreparedStatement 执行两次更新操作
- 正常情况下提交事务
- 出现异常回滚事务
- 最后关闭资源
代码: