JDBC入门
客户端操作MySQL数据库的方式:
- 使用第三方客户端来访问 MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL。
- 使用 MySQL 自带的命令行方式。
- 通过 Java 来访问 MySQL 数据库。
什么是JDBC
JDBC 规范定义 接口 ,具体的实现由各大数据库厂商来实现。
JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。
使用 JDBC 的好处:
1.程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
2.使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库
使用 JDBC 开发使用到的包:
| 会使用到的包 | 说明 |
|---|---|
| java.sql | 所有与 JDBC 访问数据库相关的接口和类 |
| javax.sql | 数据库扩展包,提供数据库额外的功能。如:连接池 |
| 数据库的驱动 | 由各大数据库厂商提供,需要额外去下载,是对 JDBC 接口实现的类 |
JDBC 的核心 API
| 接口或类 | 作用 |
|---|---|
| DriverManager 类 | 管理和注册数据库驱动,得到数据库连接对象 |
| Connection 接口 | 一个连接对象,可用于创建 Statement 和 PreparedStatement 对象 |
| Statement 接口 | 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器 |
| PreparedStatemen | 一个 SQL 语句对象,是 Statement 的子接口 |
| ResultSet 接口 | 用于封装数据库查询的结果集,返回给客户端 Java 程序 |
步骤
- 导入jar包
复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下,右键–>Add As Library。 - 注册驱动
Class.forName(“com.mysql.jdbc.Driver”);
如果是本地服务器,端口号是 3306
则可以简写为jdbc:mysql:///数据库名
乱码问题
如果数据库出现乱码,可以指定参数: ?characterEncoding=utf8,表示让数据库以 UTF-8 编码来处理数据。
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
**注意:**从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。Class.forName
这句话可以省略。
3. 获取数据库连接对象
通过连接字符串,用户名,密码来得到数据库的连接对象
Connection getConnection (String url, String user, String password)
Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/数据库名”, “root”, “密码”);
4. 定义sql语句
5. 获取执行sql的对象Statement
Statement stmt = conn.createStatement();
6. 执行sql
| Statement 接口中的方法 | 描述 |
|---|---|
| int executeUpdate(String sql) | 用于发送 DML 语句,增删改的操作,insert、update、delete。参数:SQL 语句。返回值:返回对数据库影响的行数 |
| ResultSet executeQuery(String sql) | 用于发送 DQL 语句,执行查询的操作。select。参数:SQL 语句。返回值:查询的结果集 |
stmt.executeUpdate(sql);
7. ResultSet处理执行结果

ResultSet接口中的方法:
| ResultSet接口中的方法 | 描述 |
|---|---|
| boolean next() | 游标向下移动 1 行。返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false |
| 数据类型 getXxx() | 通过字段名,参数是 String 类型。返回不同的类型。通过列号,参数是整数,从 1 开始。返回不同的类型 |

常用数据类型转换表:
| 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 同时有日期和时间 |
- 释放资源
- 需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
- 释放原则:先开的后关,后开的先关。ResultSet --> Statement --> Connection
- 放在哪个代码块中:finally 块
利用JDBC进行增加,修改和查询操作
/**
* 创建一张学生表
*/
package com.itheima;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCTest {
public static void main(String[] args) {
//1. 创建连接
Connection conn = null;
Statement statement = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://jdbcstudy", "root", "mysql");
//2. 通过连接对象得到语句对象
statement = conn.createStatement();
//3. 通过语句对象发送 SQL 语句给服务器
//4. 执行 SQL
statement.executeUpdate("create table student (id int PRIMARY key auto_increment, " +
"name varchar(20) not null, gender boolean, birthday date)");
//5. 返回影响行数(DDL 没有返回值)
System.out.println("创建表成功");
} catch (SQLException e) {
e.printStackTrace();
}
//6. 释放资源
finally {
//关闭之前要先判断
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 向学生表中添加 4 条记录,主键是自动增长
*/
package com.itheima;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCTest {
public static void main(String[] args) throws SQLException {
// 1) 创建连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql:///jbcstudy", "root",
"mysql");
// 2) 创建 Statement 语句对象
Statement statement = connection.createStatement();
// 3) 执行 SQL 语句:executeUpdate(sql)
int count = 0;
// 4) 返回影响的行数
count += statement.executeUpdate("insert into student values(null, '张三', 1, '1993-03-
24')");
count += statement.executeUpdate("insert into student values(null, '李四', 0, '1995-03-
24')");
count += statement.executeUpdate("insert into student values(null, '王五', 1, '1903-03-24')");
count += statement.executeUpdate("insert into student values(null, 'Jack', 0, '1993-03-
11')");
System.out.println("插入了" + count + "条记录");
// 5) 释放资源
statement.close();
connection.close();
}
}
/**
* 查询所有的学生信息
*/
package com.itheima;
import java.sql.*;
public class Demo6DQL {
public static void main(String[] args) throws SQLException {
//1) 得到连接对象
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcstudy","root","mysql");
//2) 得到语句对象
Statement statement = connection.createStatement();
//3) 执行 SQL 语句得到结果集 ResultSet 对象
ResultSet rs = statement.executeQuery("select * from student");
//4) 循环遍历取出每一条记录
while(rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
boolean gender = rs.getBoolean("gender");
Date birthday = rs.getDate("birthday");
//5) 输出的控制台上
System.out.println("编号:" + id + ", 姓名:" + name + ", 性别:" + gender + ", 生日:" +birthday);
}
//6) 释放资源
rs.close();
statement.close();
connection.close();
}
}
数据库工具JdbcUtils
上面写的代码中出现了很多重复的代码,可以把这些公共代码抽取出来。
创建类 JdbcUtil 包含 3 个方法:
- 可以把几个字符串定义成常量:用户名,密码,URL,驱动类。
- 得到数据库的连接:getConnection()
- 关闭所有打开的资源:
close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)
工具类代码:
package com.itheima.utils;
import java.sql.*;
/**
* 访问数据库的工具类
*/
public class JdbcUtils {
//可以把几个字符串定义成常量:用户名,密码,URL,驱动类
private static final String USER = "root";
private static final String PWD = "mysql";
private static final String URL = "jdbc:mysql://localhost:3306/jdbcstudy";
private static final String DRIVER= "com.mysql.jdbc.Driver";
/**
* 注册驱动
*/
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 得到数据库的连接
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL,USER,PWD);
}
/**
* 关闭所有打开的资源
*/
public static void close(Connection conn, Statement stmt) {
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(Connection conn, Statement stmt, ResultSet rs) {
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
close(conn, stmt);
}
}
SQL 注入问题
当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了
请输入用户名:
username
请输入密码:
a' or '1'='1
select * from user where name='username' and password='a' or '1'='1'
登录成功,欢迎您:username
问题分析:
select * from user where name='username' and password='a' or '1'='1'
name='username' and password='a' 为假
'1'='1' 真
相当于
select * from user where true; 查询了所有记录
我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。
PreparedStatement
PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句。
Connection 创建 PreparedStatement 对象
| Connection 接口中的方法 | 描述 |
|---|---|
| PreparedStatement prepareStatement(String sql) | 指定预编译的 SQL 语句,SQL 语句中使用占位符?创建一个语句对象 |
PreparedStatement 接口中的方法:
| PreparedStatement 接口中的方法 | 描述 |
|---|---|
| int executeUpdate() | 执行 DML,增删改的操作,返回影响的行数。 |
| ResultSet executeQuery() | 执行 DQL,查询的操作,返回结果集 |
PreparedSatement 的好处
- prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。
可以多次传入不同的参数给PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。 - 安全性更高,没有 SQL 注入的隐患。
- 提高了程序的可读性
使用 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 执行 DML 操作
import com.itheima.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo11DML {
public static void main(String[] args) throws SQLException {
//insert();
//update();
delete();
}
//插入记录
private static void insert() throws SQLException {
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("insert into student
values(null,?,?,?)");
ps.setString(1,"张三");
ps.setBoolean(2, true);
ps.setDate(3,java.sql.Date.valueOf("1999-11-11"));
int row = ps.executeUpdate();
System.out.println("插入了" + row + "条记录");
JdbcUtils.close(connection,ps);
}
//更新记录: 换名字和生日
private static void update() throws SQLException {
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("update student set name=?, birthday=?
where id=?");
ps.setString(1,"李四");
ps.setDate(2,java.sql.Date.valueOf("1999-03-23"));
ps.setInt(3,5);
int row = ps.executeUpdate();
System.out.println("更新" + row + "条记录");
JdbcUtils.close(connection,ps);
}
//删除记录: 删除第 5 条记录
private static void delete() throws SQLException {
Connection connection =JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("delete from student where id=?");
ps.setInt(1,5);
int row = ps.executeUpdate();
System.out.println("删除了" + row + "条记录");
JdbcUtils.close(connection,ps);
}
}
JDBC控制事务
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
准备数据:
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('张三', 1000), ('李四', 1000);
| Connection接口中与事务有关的方法 | 说明 |
|---|---|
| void setAutoCommit(boolean autoCommit) | 参数是 true 或 false,如果设置为 false,表示关闭自动提交,相当于开启事务 |
| void commit() | 提交事务 |
| void rollback() | 回滚事务 |
开发步骤:
- 获取连接
- 开启事务
- 获取到 PreparedStatement
- 使用 PreparedStatement 执行两次更新操作
- 正常情况下提交事务
- 出现异常回滚事务(在catch中回滚事务)
- 最后关闭资源
案例代码:
public class JDBCTest {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//2.定义sql
//2.1 张三 - 500
String sql1 = "update account set balance = balance - ? where id = ?";
//2.2 李四 + 500
String sql2 = "update account set balance = balance + ? where id = ?";
//3.获取执行sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//4. 设置参数
pstmt1.setDouble(1,500);
pstmt1.setInt(2,1);
pstmt2.setDouble(1,500);
pstmt2.setInt(2,2);
//5.执行sql
pstmt1.executeUpdate();
// 手动制造异常
int i = 3/0;
pstmt2.executeUpdate();
//提交事务
conn.commit();
} catch (Exception e) {
//事务回滚
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);
JDBCUtils.close(pstmt2,null);
}
}
}