目录
3.Connection 创建 PreparedStatement 对象
一、JDBC 入门
1.客户端操作 MySQL 数据库的方式
a.使用第三方客户端来访问 MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL。
b.使用 MySQL 自带的命令行方式。
c.通过 Java 来访问 MySQL 数据库,今天要学习的内容。
(1)JDBC概念
JDBC 规范定义接口,具体的实现由各大数据库厂商来实现。JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。
使用 JDBC 的好处:
a.程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
b.使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库。
(2)使用 JDBC 开发使用到的包
2.JDBC 的核心 API
3.导入驱动 Jar 包
4.加载和注册驱动
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//抛出类找不到的异常,注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
}
}
com.mysql.jdbc.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 类
1.DriverManager 作用
a.管理和注册驱动。
b.创建数据库的连接。
2.类中的方法
3.使用 JDBC 连接数据库的四个参数
4.连接数据库的 URL 地址格式
协议名:子协议://服务器名或 IP 地址:端口号/数据库名?参数=参数值
(1)MySQL 写法
(2)MySQL 中的简写
前提:必须是本地服务器,端口号是 3306
jdbc:mysql:///数据库名
(3)乱码的处理
如果数据库出现乱码,可以指定参数: ?characterEncoding=utf8,表示让数据库以 UTF-8 编码来处理数据。
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
5.得到 MySQL 的数据库连接对象
(1)使用用户名、密码、URL 得到连接对象
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 得到连接对象
*/
public class Test{
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/test";
//使用用户名、密码、URL 得到连接对象
Connection connection = DriverManager.getConnection(url, "root", "root");
System.out.println(connection);
}
}
(2)使用属性文件和 url 得到连接对象
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class Test{
public static void main(String[] args) throws SQLException {
//url 连接字符串
String url = "jdbc:mysql://localhost:3306/test";
//属性对象
Properties pt= new Properties();
//把用户名和密码放在 info 对象中
pt.setProperty("user","root");
pt.setProperty("password","root");
Connection connection = DriverManager.getConnection(url, pt);
System.out.println(connection);
}
}
三、Statement 接口
1.JDBC 访问数据库的步骤
a.注册和加载驱动(可以省略)。
b.获取连接。
c.Connection 获取 Statement 对象。
d.使用 Statement 对象执行 SQL 语句。
e.返回结果集。
f.释放资源。
2.Statement 作用
代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
3.Statement 中的方法
Statement接口中的方法:
int executeUpdate(String sql) 用于发送 DML 语句,增删改的操作,insert、update、delete 参数:SQL 语句返回值:返回对数据库影响的行数。
ResultSet executeQuery(String sql) 用于发送 DQL 语句,执行查询的操作。select 参数:SQL 语句返回值:查询的结果集。
4.释放资源
a.需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接。
b.释放原则:先开的后关,后开的先关。ResultSet Statement Connection。
c.放在哪个代码块中:finally 块。
5.执行 DDL 操作
(1)需求
使用 JDBC 在 MySQL 的数据库中创建一张学生表。
(2)代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Test{
public static void main(String[] args) {
// 数据库连接 URL
String url = "jdbc:mysql://localhost:3306/test";
// 数据库用户名
String username = "root";
// 数据库密码
String password = "root";
try {
// 加载 MySQL JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取数据库连接
Connection connection = DriverManager.getConnection(url, username, password);
// 创建 SQL 语句执行对象
Statement statement = connection.createStatement();
// 创建学生表的 SQL 语句
String sql = "CREATE TABLE students (" +
"id INT AUTO_INCREMENT PRIMARY KEY," +
"name VARCHAR(255)," +
"age INT," +
"grade INT" +
")";
// 执行 SQL 语句创建表
statement.executeUpdate(sql);
System.out.println("学生表创建成功!");
// 关闭连接和语句对象
statement.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
6.执行 DML 操作
(1)需求
使用 JDBC 连接到 MySQL 数据库中的学生表,向学生表中添加 4 条记录,主键是自动增长。
(2)代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test{
public static void main(String[] args) {
// 数据库连接 URL
String url = "jdbc:mysql://localhost:3306/test";
// 数据库用户名
String username = "root";
// 数据库密码
String password = "root";
try {
// 加载 MySQL JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取数据库连接
Connection connection = DriverManager.getConnection(url, username, password);
// 准备插入数据的 SQL 语句
String sql = "INSERT INTO students (name, age, grade) VALUES (?,?,?)";
PreparedStatement statement = connection.prepareStatement(sql);
// 设置第一条记录的值
statement.setString(1, "张三");
statement.setInt(2, 18);
statement.setInt(3, 90);
statement.executeUpdate();
// 设置第二条记录的值
statement.setString(1, "李四");
statement.setInt(2, 19);
statement.setInt(3, 85);
statement.executeUpdate();
// 设置第三条记录的值
statement.setString(1, "王五");
statement.setInt(2, 20);
statement.setInt(3, 92);
statement.executeUpdate();
// 设置第四条记录的值
statement.setString(1, "赵六");
statement.setInt(2, 18);
statement.setInt(3, 88);
statement.executeUpdate();
System.out.println("四条记录插入成功!");
// 关闭连接和语句对象
statement.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
7.执行 DQL 操作
(1)ResultSet 接口
a.作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
b.接口中的方法:
ResultSet 接口中的方法:
boolean next(): 1)游标向下移动 1 行;2)返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false数据类型。
getXxx() :1)通过字段名,参数是 String 类型。返回不同的类型;2)通过列号,参数是整数,从 1 开始,返回不同的类型。
(2)需求
使用 JDBC 连接到 MySQL 数据库中的学生表,查询出年龄大于 18 岁的学生的姓名、年龄和成绩,并将查询结果以格式化的方式输出到控制台。
(3)代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Test{
public static void main(String[] args) {
// 数据库连接 URL
String url = "jdbc:mysql://localhost:3306/test";
// 数据库用户名
String username = "root";
// 数据库密码
String password = "root";
try {
// 加载 MySQL JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取数据库连接
Connection connection = DriverManager.getConnection(url, username, password);
// 创建 SQL 语句执行对象
Statement statement = connection.createStatement();
// 执行查询年龄大于 18 岁的学生的 SQL 语句
String sql = "SELECT name, age, grade FROM students WHERE age > 18";
ResultSet resultSet = statement.executeQuery(sql);
// 输出查询结果
System.out.println("姓名\t年龄\t成绩");
while (resultSet.next()) {
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
int grade = resultSet.getInt("grade");
System.out.println(name + "\t" + age + "\t" + grade);
}
// 关闭结果集、语句对象和连接
resultSet.close();
statement.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
(4)ResultSet 接口中的注意事项
a.如果光标在第一行之前,使用 rs.getXX()获取列值,报错:Before start of result set。
b.如果光标在最后一行之后,使用 rs.getXX()获取列值,报错:After end of result set。
c.使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection。
四、数据库工具类 JdbcUtils
1.创建类 JdbcUtil 包含 3 个方法
a.可以把几个字符串定义成常量:用户名,密码,URL,驱动类。
b.得到数据库的连接:getConnection()。
c.关闭所有打开的资源:close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtils {
// 可以把几个字符串定义成常量:用户名,密码,URL,驱动类
private static final String USER = "root";
private static final String PWD = "root";
private static final String URL = "jdbc:mysql://localhost:3306/test";
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) {
close(conn, stmt, null);
}
public static void close(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs!= null) {
rs.close();
}
if (stmt!= null) {
stmt.close();
}
if (conn!= null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2. 案例:用户登陆
(1)需求
首先,有一张用户表,接着向该表中添加几条用户记录。然后,使用 Statement 字符串拼接的方式实现用户登录功能,即让用户在控制台上输入用户名和密码,以此进行登录验证。
(2)代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class Test{
public static void main(String[] args) {
// 数据库连接 URL
String url = "jdbc:mysql://localhost:3306/test";
// 数据库用户名
String username = "root";
// 数据库密码
String password = "root";
try {
// 加载 MySQL JDBC 驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取数据库连接
Connection connection = DriverManager.getConnection(url, username, password);
// 添加几条用户记录
String insertSql = "INSERT INTO users (username, password) VALUES ('user1', 'pass1'), ('user2', 'pass2')";
Statement insertStatement = connection.createStatement();
insertStatement.executeUpdate(insertSql);
// 用户在控制台上输入用户名和密码
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String inputUsername = scanner.nextLine();
System.out.print("请输入密码:");
String inputPassword = scanner.nextLine();
// 使用 Statement 字符串拼接进行登录验证
String loginSql = "SELECT * FROM users WHERE username='" + inputUsername + "' AND password='" + inputPassword + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(loginSql);
if (resultSet.next()) {
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
// 关闭资源
resultSet.close();
statement.close();
insertStatement.close();
connection.close();
scanner.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
五、PreparedStatement 接口
1.继承结构与作用
PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句。
2.PreparedSatement 的执行原理
a.因为有预先编译的功能,提高 SQL 的执行效率。
b.可以有效的防止 SQL 注入的问题,安全性更高。
3.Connection 创建 PreparedStatement 对象
Connection 接口中的方法:
PreparedStatement prepareStatement(String sql) :指定预编译的SQL语句,SQL 语句中使用占位符,创建一个语句对象。
4.PreparedStatement 接口中的方法
PreparedStatement 接口中的方法:
int executeUpdate() :执行 DML,增删改的操作,返回影响的行数。
ResultSet executeQuery(): 执行 DQL,查询的操作,返回结果集。
5.PreparedSatement 的好处
a.prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
b.安全性更高,没有 SQL 注入的隐患。
c.提高了程序的可读性。
6.使用 PreparedStatement 的步骤
a.编写 SQL 语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?"。
b.获得 PreparedStatement 对象。
c.设置实际参数:setXxx(占位符的位置, 真实的值)。
d.执行参数化 SQL 语句。
e.关闭资源
7.PreparedStatement 执行 DML 操作
package com.deyuan;
import com.deyuan.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test{
public static void main(String[] args) throws SQLException {
insert();
update();
}
// 插入记录
private static void insert() throws SQLException {
try (Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("insert into student values(null,?,?,?)")) {
ps.setString(1, "张三");
ps.setBoolean(2, true);
ps.setDate(3, Date.valueOf("1999-11-11"));
int row = ps.executeUpdate();
System.out.println("插入了" + row + "条记录");
}
}
// 更新记录: 换名字和生日
private static void update() throws SQLException {
try (Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("update student set name=?, birthday=? where id=?")) {
ps.setString(1, "李四");
ps.setDate(2, Date.valueOf("1999-03-23"));
ps.setInt(3, 5);
int row = ps.executeUpdate();
System.out.println("更新" + row + "条记录");
}
}
}
六、JDBC 事务的处理
1.API 介绍
Connection 接口中与事务有关的方法:
void setAutoCommit(boolean autoCommit): 参数是 true 或 false如果设置为 false,表示关闭自动提交,相当于开启事务。
void commit() :提交事务。
void rollback(): 回滚事务。
2.开发步骤
(1)步骤
a.获取连接
b.开启事务
c.获取到 PreparedStatement
d.使用 PreparedStatement 执行两次更新操作
e.正常情况下提交事务
f.出现异常回滚事务
g.最后关闭资源
(2)示例代码
package com.example;
import com.example.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test{
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
try {
// 注册驱动(已在静态代码块中完成)
// 获取连接
connection = JdbcUtils.getConnection();
// 开启事务
connection.setAutoCommit(false);
// 从张三扣钱
ps1 = connection.prepareStatement("update account set balance = balance -? where name=?");
ps1.setInt(1, 500);
ps1.setString(2, "张三");
ps1.executeUpdate();
// 给 李四加钱
ps2 = connection.prepareStatement("update account set balance = balance +? where name=?");
ps2.setInt(1, 500);
ps2.setString(2, "李四");
ps2.executeUpdate();
// 提交事务
connection.commit();
System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
try {
// 事务的回滚
connection.rollback();
System.out.println("转账失败,已回滚事务。");
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
// 关闭资源
try {
if (ps1!= null) {
ps1.close();
}
if (ps2!= null) {
ps2.close();
}
if (connection!= null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}