本文章以MySQL8.0为标准
第一节 JDBC入门
1.1.什么是JDBC
1.1.1.概念:
JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个 数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即 可,数据库驱动由数据库厂商提供。
1.1.2.快速入门
-
步骤:
1. 导入驱动jar包 mysql-connector-java-8.0.12.jar到项目的libs目录下,右键–>Add As Library
2. 注册驱动
3. 获取数据库连接对象 Connection
4. 定义sql
5. 获取执行sql语句的对象 Statement
6. 执行sql,接受返回结果
7. 处理结果
8. 释放资源 -
代码实现
package cn.JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCDemo1 {
public static void main(String[] args)throws Exception {
// 1. 导入驱动jar包 mysql-connector-java-8.0.12.jar
// 2. 注册驱动
//方法一:
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//方法二:
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 获取数据库连接对象 Connection
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?serverTimezone=UTC", "root", "0826");
// 4. 定义sql
String sql = "Update account set balance = 2000";
// 5. 获取执行sql语句的对象 Statement
Statement stmt = conn.createStatement();
// 6. 执行sql,接受返回结果
int count = stmt.executeUpdate(sql);
// 7. 处理结果
System.out.println("影响的行数为:" + count);
// 8. 释放资源
stmt.close();
conn.close();
}
}
1.2.JDBC核心API
接口或类 | 作用 |
---|---|
DriverManager类 | (1) 管理和注册数据库驱动 (2) 得到数据库连接对象 |
Connection接口 | 一个连接对象,可用于创建 Statement 和 PreparedStatement 对象 |
Statement接口 | 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器 |
PrepareStatement接口 | 一个 SQL 语句对象,是 Statement 的子接口 |
Result接口 | 用于封装数据库查询的结果集,返回给客户端 Java 程序 |
1.3.导入驱动jar包
- 导入驱动jar包 mysql-connector-java-8.0.12.jar到项目的libs目录下,右键–>Add As Library
1.4.加载和注册驱动
加载和注册驱动的方法 | 描述 |
---|---|
Class.forName(“数据库驱动实现类”) | 加载和注册数据库驱动,数据库驱动由MySQL厂商实现"com.mysql.cj.jdbc.Driver" |
- 为什么可以这样注册驱动
Class.forName(“com.mysql.cj.jdbc.Driver”);
- com.mysql.cj.mysql.cj.Driver源码:
Driver 接口,所有数据库厂商必须实现的接口,表示这是一个驱动类。
public class Driver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver()); //注册数据库驱动
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
注: 从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。Class.forName 这句话可以省略。
第二节 DriverManager:驱动管理对象
2.1.DriverManager的作用
- 管理和注册驱动
- 创建数据库的连接
2.2.类中的方法
DriverManager类中的静态方法 | 描述 |
---|---|
Connection getConnection(String url,String user,String password) | 通过连接字符串,用户名,密码来得到数据 库的连接对象 |
Connection getConnection(String url,Properties info) | 通过连接字符串,属性对象来得到连接对象 |
2.3.连接数据库的URL格式:
协议名:子协议://服务器名或 IP 地址:端口号/数据库名?参数=参数值
- msyql中可以简写:
前提必须是本地服务器、端口号为3306
jdbc:mysql:///数据库名
2.4.案例:得到MySQL数据库连接对象
1.使用用户名,密码,url得到数据库连接对象
package cn.JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 得到连接对象
*/
public class Demo2 {
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/day24";
// 使用用户名、密码、 URL 得到连接对象
Connection connection = DriverManager.getConnection(url, "root", "root");
System.out.println(connection);
}
}
2.使用url和属性文件得到MySQL连接对象
package cn.JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class Demo3 {
public static void main(String[] args) throws SQLException {
//url连接字符串
String url = "jdbc:mysql://localhost:3306/day24";
//属性对象
Properties info = new Properties();
//把用户名和密码放在info对象中
info.setProperty("user","root");
info.setProperty("password","root");
Connection connection = DriverManager.getConnection(url, info);
System.out.println(connection);
}
}
第三节 Connection接口:数据库连接对象
3.1.Connection的功能:
- 获取执行SQL的对象
Statement createStatement();
PreparedStatement prepareStatement(String sql); - 管理事务
开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
提交事务:commit
回滚事务:rollback
第四节 Statement接口:执行SQL对象
4.1.Statement的作用:
代表一条执行对象,用于发送一条SQL语句给服务器,用于执行静态SQL语句并返回它所生成结果的对象。
4.2.Statement中的方法:
- int executeUpdate(Srting sql):执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句,返回值为对数据库影响的行数。
- ResultSet executeQuery(String sql):用于发送 DQL 语句,执行查询的操作(select),返回值为查询的结果集。
4.3 释放资源
4.3.1.需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
4.3.2.释放原则:先开的后关,后开的先关。ResultSet > Statement > Connection
4.3.3.放在哪个代码块中:finally 块
4.4.执行DDL操作
4.4.1.需求:使用JDBC在MySQL的数据库中创建一张学生表
4.4.2.代码
package cn.itcast.JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 执行DDL
* 创建一张表
*/
public class JDBCDemo6 {
public static void main(String[] args) {
Statement stmt = null;
Connection conn = null;
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql:///db3?serverTimezone=UTC", "root", "0826");
//定义执行sql语句
String sql = "create table student(id int PRIMARY key auto_increment,name varchar(20) not null, gender boolean, birthday date)";
//获取获取执行sql对象 Statement
stmt = conn.createStatement();
//执行sql语句
int count = stmt.executeUpdate(sql);
//打印结果
System.out.println("影响的行数为:"+count);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//关闭资源
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
stmt.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}
}
4.5.执行DQL操作
4.5.1.ResultSet接口
- 作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
4.5.2.代码
package cn.itcast.JDBC;
import java.sql.*;
/**
* 执行DDL
* 查询表中的内容
*/
public class JDBCDemo7 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//定义sql语句
String sql = "select * from account";
//获取连接数据库的对象 Connection
conn = DriverManager.getConnection("jdbc:mysql:///db3?serverTimezone=UTC", "root", "0826");
//获取执行sql对象 Statement
stmt = conn.createStatement();
//执行sql
rs = stmt.executeQuery(sql);
//循环遍历输出结果
while (rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
double balance = rs.getDouble(3);
System.out.println(id+"---------"+name+"--------"+balance);
}
} 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();
}
}
}
}
}
第五节 数据库工具类JDBCUtils
- 什么时候自己创建工具类?
如果一个功能经常使用到,我们建议把这个功能做成一个工具类,可以在不同的时候重用。
5.1.需求
上面写的代码中有很多重复的代码,可以把这些公共的代码抽取出来。
5.2.JDBCUtils包含三个方法
- 可以把几个字符串定义成常量:用户名,密码,URL,驱动类
- 得到数据库连接:getConnection()
- 关闭所有打开的资源:close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)
5.3.代码
1、配置文件:jdbc.properties
url=jdbc:mysql://localhost:3306/db3?serverTimezone=UTC
user=root
password=0826
driver=com.mysql.cj.jdbc.Driver
2、工具类JDBCUtils.java
package cn.itcast.JDBCUtils;
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 root;
private static String password;
private static String driver;
//文件的读取,只要读取一次即可以拿到这些值。用静态代码块
static {
try {
//创建properties集合类
Properties pro = new Properties();
//获取src路径下的文件方式---------->ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
//获取path,为相对路径
String path = res.getPath();
// System.out.println(path); /C:/IdeaProjects/project01/out/production/JDBC/jdbc.properties
pro.load(new FileReader(path));
//获取数据,贬值
url = pro.getProperty("url");
root = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, root, password);
}
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();
}
}
}
public static void close(ResultSet rs,Statement stmt,Connection conn){
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
close(stmt,conn);
}
}
ClassLoader getClassLoader(): 返回类的类加载器。
url getResource(String name): 找到具有给定名称的资源。
第六节 PrepareStatement接口
6.1.SQL注入问题
- 当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了
请输入用户名:
newboy
请输入密码:
a’ or ‘1’='1 select * from user where name=‘newboy’ and password=‘a’ or ‘1’=‘1’
登录成功,欢迎您:newboy
- 问题分析:
select * from user where name=‘newboy’ and password=‘a’ or ‘1’=‘1’
name=‘newboy’ and password=‘a’ 为假
‘1’=‘1’ 为真
相当于 select * from user where true; 查询了所有记录
我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了 原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进 行简单的字符串拼接
6.2.继承结构与作用
PrepareStatement是Statement接口的子接口,继承于父类中所用的方法,它是预编译的SQL语句。
6.3.PrepareStatement的执行原理
1.因为有预先编译的功能,提高 SQL 的执行效率。
2.可以有效的防止 SQL 注入的问题,安全性更高。
6.4.使用PreparedStatement的步骤:
- 编写 SQL 语句,未知内容使用?占位:“SELECT * FROM user WHERE name=? AND password=?”;
- 获得 PreparedStatement 对象
- 设置实际参数:setXxx(占位符的位置, 真实的值)
- 执行参数化 SQL 语句
- 关闭资源
6.5.对比Statement和PrepareStatement
package cn.itcast.JDBC;
import cn.itcast.JDBCUtils.JDBCUtils;
import java.sql.*;
import java.util.Scanner;
/**
* * 需求:
* 1. 通过键盘录入用户名和密码
* 2. 判断用户是否登录成功
*/
public class JDBCDemo9 {
public static void main(String[] args) {
//键盘录入,接收用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
boolean flag = new JDBCDemo9().login2(username, password);
if (flag){
System.out.println("登录成功!");
}else{
System.out.println("用户名或密码错误");
}
}
/**
* 登录方式:使用Statement实现
*/
public boolean login(String name,String password){
if (name == null || password == null){
return false;
}
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//获取连接对象connection
conn = JDBCUtils.getConnection();
String sql = "select * from user where name = '"+name+"' and password = '"+password+"'";
System.out.println(sql);
//创建执行sql语句对象statement
stmt = conn.createStatement();
//执行sql
rs = stmt.executeQuery(sql);
/* if(rs.next()){
return true;
}else{
return false;
}*/
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.close(rs,stmt,conn);
}
return false;
}
/**
* 登录方式:使用PrepareStatement
* 解决了sql注入问题,开发中应用
*/
public boolean login2(String name,String password){
if (name == null || password == null){
return false;
}
Connection conn = null;
PreparedStatement prep = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select * from user where name = ? and password = ?";
//执行sql对象
prep = conn.prepareStatement(sql);
//贬值
prep.setString(1,name);
prep.setString(2,password);
rs = prep.executeQuery();
System.out.println(sql);
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,prep,conn);
}
return false;
}
}
第七节 JDBC事务的处理
之前我们是使用 MySQL 的命令来操作事务。接下来我们使用 JDBC 来操作银行转账的事务。
7.1.准备数据
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10), balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('Jack', 1000), ('Rose', 1000);
7.2.开发步骤
1.获取连接
2.开启事务
3.获取到PrepareStatement
4.使用PrepareStatement执行两次更新步骤
5.正常情况下提交事务
6.出现异常回滚事务
7.最后关闭资源
7.2.1.案例代码(已省略工具类JDBCUtils代码)
package cn.itcast.JDBC;
import cn.itcast.JDBCUtils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCDemo10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prep1 = null;
PreparedStatement prep2 = null;
try {
//获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false); //false关闭自动提交
//设置sql1为张三减去500
String sql1 = "update account set balance = balance - ? where id = ?";
//设置sql2为李四加上500
String sql2 = "update account set balance = balance + ? where id = ?";
//创建执行sql对象,PrepareStatement
prep1 = conn.prepareStatement(sql1);
prep2 = conn.prepareStatement(sql2);
//设置参数
prep1.setInt(1,500);
prep1.setInt(2,1);
prep2.setInt(1,500);
prep2.setInt(2,2);
//执行 sql 语句
prep1.executeUpdate();
//手动制造异常
//int i = 3/0;
prep2.executeUpdate();
//提交事务
conn.commit();
} catch (SQLException e) {
try {
if (conn !=null){
conn.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.close(prep1,conn);
JDBCUtils.close(prep2,null);
}
}
}