一、JDBC 基础入门
1、概念
JDBC:Java DataBase Connectivity Java 数据库连接,Java 语言操作数据库。
JDBC本质:其实就是官方(Sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动 jar 包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动 jar 包中的实现类。
使用JDBC的好处:
(1)程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
(2)使用一套 Java 代码,进行少量的修改就可以访问其他 JDBC支持的数据库。
2、使用 JDBC 开发使用到的包
会使用到的包 | 说明 |
java.sql | 所有与 JDBC 访问数据库相关的接口和类 |
javax.sql | 数据库扩展包,提供数据库额外的功能。如:连接池 |
数据库的驱动 | 由各大数据库厂商提供,需要额外去下载,是对 JDBC 接口实现的类 |
3、JDBC 的核心 API
接口或类 | 作用 |
DriverManager 类 | 1) 管理和注册数据库驱动 2) 得到数据库连接对象 |
Connection 接口 | 一个连接对象,可用于创建 Statement 和 PreparedStatem |
Statement 接口 | 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器 |
PreparedStatemen 接口 | 一个 SQL 语句对象,是 Statement 的子接口 |
ResultSet 接口 | 用于封装数据库查询的结果集,返回给客户端 Java 程序 |
4、使用步骤
(1)导入驱动 jar 包 mysql-connector-java-5.1.37-bin.jar
① 复制 mysql-connector-java-5.1.37-bin.jar 到项目的 libs 目录下
② 右键 --> Add AS Libray
(2)注册驱动(可以省略)
(3)获取数据连接对象 Connection
(4)定义 SQL 语句
(5)获取执行 SQL 语句的对象 statement
(6)执行 SQL,接收返回结果
(7)处理结果
(8)释放资源
5、步骤实现
(1)导入驱动 jar 包
(2)代码实现
1 //2.注册驱动
2 Class.forName("com.mysql.jdbc.Driver");
3 //3.获取数据库连接对象
4 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/school", "root", "root");
5 //4.定义sql语句
6 String sql = "update student set score= 500 where id = 1";
7 //5.获取执行sql的对象 Statement
8 Statement stmt = conn.createStatement();
9 //6.执行sql
10 int count = stmt.executeUpdate(sql);
11 //7.处理结果
12 System.out.println(count);
13 //8.释放资源
14 stmt.close();
15 conn.close();
二、详解各个对象
1、DriverManager:驱动管理对象
功能:① 管理和注册驱动;② 创建数据库的连接
(1)管理和注册驱动
注册驱动就是告诉程序该使用哪一个数据库驱动 jar包,可以发现 DriverManger里面有一个静态方法
static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
写代码使用:
Class.forName("com.mysql.jdbc.Driver");
通过反射机制将驱动的字节码文件加载入程序里面。
通过查看源码发现:在 com.mysql.jdbc.Driver 类中存在静态代码块
1 static {
2 try {
3 java.sql.DriverManager.registerDriver(new Driver()); // 注册数据库驱动
4 } catch (SQLException E) {
5 throw new RuntimeException("Can't register driver!");
6 }
7 }
静态代码块会优先执行,自动调用了 DriverManager 类内的方法,不需要手动调用方法,更加方便。
注意:mysql5 之后的驱动 jar 包可以省略注册驱动的的步骤。
(2)获取数据库的连接
① 类中的静态方法
Connection getConnection (String url, String user, String password) 通过连接字符串, 用户名, 密码来得到数据库的连接对象
Connection getConnection (String url, Properties info) 通过连接字符串, 属性对象来得到连接对象
② 使用 JDBC 连接数据库的四个参数:
JDBC 连接数据库的四个参数 | 说明 |
username用户名 | 登录的用户名 |
password密码 | 登录的密码 |
连接字符串 URL | 不同的数据库 URL 是不同的, mysql 的写法 jdbc:mysql://localhost:3306/数据库[?参数名=参数值] |
驱动类的字符串名 | com.mysql.jdbc.Driver |
注意:如果连接的是本机 mysql 服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称。
③ MySQL 写法:
④ 乱码的处理:
如果数据库出现乱码,可以指定参数:?characterEncoding=utf8,表示让数据库以utf-8编码来处理数据。
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
⑤ 得到数据库连接对象
a、使用用户名、密码、URL得到连接对象
1 import java.sql.Connection;
2 import java.sql.DriverManager;
3 import java.sql.SQLException;
4 /**
5 * 得到连接对象
6 */
7 public class Demo {
8 public static void main(String[] args) throws SQLException {
9 String url = "jdbc:mysql://localhost:3306/school";
10 //1) 使用用户名、密码、 URL 得到连接对象
11 Connection connection = DriverManager.getConnection(url,"root", "root");
12 //com.mysql.jdbc.JDBC4Connection@68de145
13 System.out.println(connection);
14 }
15 }
b、使用属性文件和URL 得到连接对象
1 import java.sql.Connection;
2 import java.sql.DriverManager;
3 import java.sql.SQLException;
4 import java.util.Properties;
5 public class Demo {
6 public static void main(String[] args) throws SQLException {
7 //url 连接字符串
8 String url = "jdbc:mysql://localhost:3306/day24";
9 //属性对象
10 Properties info = new Properties();
11 //把用户名和密码放在 info 对象中
12 info.setProperty("user","root");
13 info.setProperty("password","root");
14 Connection connection = DriverManager.getConnection(url, info);
15 //com.mysql.jdbc.JDBC4Connection@68de145
16 System.out.println(connection);
17 }
18 }
2、Connection 数据库连接对象(接口)
Connection 接口:具体的实现类由数据库的厂商实现,代表一个连接对象。
作用:① 获取执行 SQL 的对象 ② 管理事务
(1)获取执行 SQL 的对象
Statement createStatement() // 创建一条执行 SQL 语句对象 PreparedStatement prepareStatement(String sql) // 生成预编译SQL的对象
(2)管理事务
开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
提交事务:commit()
回滚事务:rollback()
3、Statement 执行SQL的对象(接口)
作用:代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
方法:
boolean execute(String sql) : 可以执行任意的sql 了解
int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
ResultSet executeQuery(String sql) :执行DQL(select)语句,返回查询的结果集
4、ResultSet 结果集对象
作用:封装数据库查询的结果集,对结果进行遍历,取出每一条记录。
接口中的方法:
ResultSet 接口中的方法 | 描述 |
boolean next() | 1) 游标向下移动 1 行 2) 返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false |
数据类型 getXxx() | 1) 通过字段名,参数是 String 类型。返回不同的类型 2) 通过列号,参数是整数,从 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 同时有日期和时间 |
注意:java.sql.Date、 Time、 Timestamp(时间戳),三个共同父类是: java.util.Date
关于 ResultSet 接口中的注意事项:
① 如果光标在第一行之前,使用 rs.getXX()获取列值,报错: Before start of result set
② 如果光标在最后一行之后,使用 rs.getXX()获取列值,报错: After end of result set
③ 使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection
5、PreparedStatement:执行 SQL 的对象
SQL注入问题:SQL语句进行字符串拼接,用户的输入的特殊关键字内容作为了 SQL 语句语法的一部分,改变了SQL真正的意义,会造成安全性问题,称为 SQL 注入。
(1)PreparedStatement 接口继承结构与作用:
PreparedStatement 是 statement 接口的子接口,继承与父接口中所有的方法,是一个预编译的 SQL 语句。
(2)PreparedStatement 的执行原理
(3)Connection 创建 PreparedStatement 对象
PreparedStatement prepareStatement(String sql) 指定预编译的 SQL 语句, SQL 语句中使用占位符?创建一个语句对象
(4)PreparedStatement 接口中的方法
int executeUpdate() 执行 DML,增删改的操作,返回影响的行数
ResultSet executeQuery() 执行 DQL,查询的操作,返回结果集
(5)PreparedStatement 的好处
① prepareStatement()会先将 SQL 语句发送给数据库预编译。 PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
② 安全性更高,没有 SQL 注入的隐患。
③ 提高了程序的可读性
(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 值。 |
6、资源的释放
(1)需要释放的对象,Result结果集、Statement 语句,Connection 连接
(2)释放原则:先开的后关,后开的先关。ResultSet—> Statement —> Connection
(3)放在 finally块中,无论如何都执行。
三、数据库工具类 JdbcUtils
工具类作用:一个经常要用到的功能,把这个功能做成一个工具类,可以在不同的地方重用,简化书写
将公共的代码抽取出来,封装成一个工具类。同时将需要传递的参数放入配置文件中,直接读取即可。
配置文件:
jdbc.properties
url=jdbc:mysql://localhost:3306/数据库名
user=root
password=root
Driver=com.mysql.jdbc.Driver
工具类:
1 import java.io.FileReader;
2 import java.io.IOException;
3 import java.net.URL;
4 import java.sql.*;
5 import java.util.Properties;
6
7 /**
8 * JDBC工具类
9 */
10 public class JDBCUtils {
11 private static String url;
12 private static String user;
13 private static String password;
14 private static String driver;
15
16 /**
17 * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
18 */
19 static {
20 //读取资源文件,获取值。
21
22 try {
23 //1. 创建Properties集合类。
24 Properties pro = new Properties();
25
26 //获取src路径下的文件的方式--->ClassLoader 类加载器
27 ClassLoader classLoader = JDBCUtils.class.getClassLoader();
28 URL res = classLoader.getResource("jdbc.properties");
29 String path = res.getPath();
30 // System.out.println(path);///D:/IdeaProjects/ks/out/production/day04_jdbc/jdbc.properties
31 //2. 加载文件
32 // pro.load(new FileReader("D:\IdeaProjects\ks\day04_jdbc\src\jdbc.properties")); 绝对路径,不建议使用
33 pro.load(new FileReader(path));
34
35 //3. 获取数据,赋值
36 url = pro.getProperty("url");
37 user = pro.getProperty("user");
38 password = pro.getProperty("password");
39 driver = pro.getProperty("driver");
40 //4. 注册驱动
41 Class.forName(driver);
42 } catch (IOException e) {
43 e.printStackTrace();
44 } catch (ClassNotFoundException e) {
45 e.printStackTrace();
46 }
47 }
48
49
50 /**
51 * 获取连接
52 *
53 * @return 连接对象
54 */
55 public static Connection getConnection() throws SQLException {
56
57 return DriverManager.getConnection(url, user, password);
58 }
59
60 /**
61 * 释放资源
62 *
63 * @param stmt
64 * @param conn
65 */
66 public static void close(Statement stmt, Connection conn) {
67 if (stmt != null) {
68 try {
69 stmt.close();
70 } catch (SQLException e) {
71 e.printStackTrace();
72 }
73 }
74
75 if (conn != null) {
76 try {
77 conn.close();
78 } catch (SQLException e) {
79 e.printStackTrace();
80 }
81 }
82 }
83
84
85 /**
86 * 释放资源
87 *
88 * @param stmt
89 * @param conn
90 */
91 public static void close(ResultSet rs, Statement stmt, Connection conn) {
92 if (rs != null) {
93 try {
94 rs.close();
95 } catch (SQLException e) {
96 e.printStackTrace();
97 }
98 }
99
100 if (stmt != null) {
101 try {
102 stmt.close();
103 } catch (SQLException e) {
104 e.printStackTrace();
105 }
106 }
107
108 if (conn != null) {
109 try {
110 conn.close();
111 } catch (SQLException e) {
112 e.printStackTrace();
113 }
114 }
115 }
116
117 }
四、案例
1、执行DDL操作
需求:使用 JDBC 在 MySQL 的数据库中创建一张学生表
代码实现:
1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.SQLException; 4 import java.sql.Statement; 5 6 /** 7 * 创建一张学生表 8 */ 9 public class Demo { 10 public static void main(String[] args) { 11 //1. 创建连接 12 Connection conn = null; 13 Statement statement = null; 14 try { 15 conn = DriverManager.getConnection("jdbc:mysql:///school", "root", "root"); 16 //2. 通过连接对象得到语句对象 17 statement = conn.createStatement(); 18 //3. 通过语句对象发送 SQL 语句给服务器 19 //4. 执行 SQL 20 statement.executeUpdate("create table student (id int PRIMARY key auto_increment, " + 21 "name varchar(20) not null, gender boolean, birthday date)"); 22 //5. 返回影响行数(DDL 没有返回值) 23 System.out.println("创建表成功"); 24 } catch (SQLException e) { 25 e.printStackTrace(); 26 } 27 //6. 释放资源 28 finally { 29 //关闭之前要先判断 30 if (statement != null) { 31 try { 32 statement.close(); 33 } catch (SQLException e) { 34 e.printStackTrace(); 35 } 36 } 37 if (conn != null) { 38 try { 39 conn.close(); 40 } catch (SQLException e) { 41 e.printStackTrace(); 42 } 43 } 44 } 45 } 46 }
2、执行DML操作
需求:向学生表中添加 4 条记录,主键是自动增长
步骤:
1) 创建连接对象
2) 创建 Statement 语句对象
3) 执行 SQL 语句: executeUpdate(sql)
4) 返回影响的行数
5) 释放资源
代码实现:
1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.SQLException; 4 import java.sql.Statement; 5 6 /** 7 * 向学生表中添加 4 条记录,主键是自动增长 8 */ 9 public class Demo { 10 public static void main(String[] args) throws SQLException { 11 // 1) 创建连接对象 12 Connection connection = DriverManager.getConnection("jdbc:mysql:///school", "root", 13 "root"); 14 // 2) 创建 Statement 语句对象 15 Statement statement = connection.createStatement(); 16 // 3) 执行 SQL 语句: executeUpdate(sql) 17 int count = 0; 18 // 4) 返回影响的行数 19 count += statement.executeUpdate("insert into student values(null, '孙悟空', 1, '1993-03-24')"); 20 count += statement.executeUpdate("insert into student values(null, '白骨精', 0, '1995-03-24')"); 21 count += statement.executeUpdate("insert into student values(null, '猪八戒', 1, '1903-03-24')"); 22 count += statement.executeUpdate("insert into student values(null, '嫦娥', 0, '1993-03-11')"); 23 System.out.println("插入了" + count + "条记录"); 24 // 5) 释放资源 25 statement.close(); 26 connection.close(); 27 } 28 }
3、执行 DQL 操作
需求:确保数据库中有 3 条以上的记录,查询所有的学员信息
步骤:
1) 得到连接对象
2) 得到语句对象
3) 执行 SQL 语句得到结果集 ResultSet 对象
4) 循环遍历取出每一条记录
5) 输出的控制台上
6) 释放资源
代码实现:
1 import java.sql.*; 2 3 /** 4 * 查询所有的学生信息 5 */ 6 public class Demo5 { 7 public static void main(String[] args) throws SQLException { 8 //1) 得到连接对象 9 Connection connection = 10 DriverManager.getConnection("jdbc:mysql://localhost:3306/school","root","root"); 11 //2) 得到语句对象 12 Statement statement = connection.createStatement(); 13 //3) 执行 SQL 语句得到结果集 ResultSet 对象 14 ResultSet rs = statement.executeQuery("select * from student"); 15 //4) 循环遍历取出每一条记录 16 while(rs.next()) { 17 int id = rs.getInt("id"); 18 String name = rs.getString("name"); 19 boolean gender = rs.getBoolean("gender"); 20 Date birthday = rs.getDate("birthday"); 21 //5) 输出的控制台上 22 System.out.println("编号: " + id + ", 姓名: " + name + ", 性别: " + gender + ", 生日: " + 23 birthday); 24 } 25 //6) 释放资源 26 rs.close(); 27 statement.close(); 28 connection.close(); 29 } 30 }
五、JDBC 控制事务
1、事务概念
事务:一个包含多个步骤的业务操作,如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
2、API 介绍
Connection 接口中与事务有关的方法 | 说明 |
void setAutoCommit(boolean autoCommit) | 参数是 true 或 false 如果设置为 false,表示关闭自动提交,相当于开启事务 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
3、使用 Connection 对象来管理事务
-
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务,在执行SQL前开启事务
- 提交事务:commit() 当所有SQL 都执行完提交事务
- 回滚事务:rollback() 在 catch 中回滚事务
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务,在执行SQL前开启事务
4、案例
模拟银行转账的事务。
开发步骤:
1) 获取连接 2) 开启事务 3) 获取到 PreparedStatement 4) 使用 PreparedStatement 执行两次更新操作 5) 正常情况下提交事务 6) 出现异常回滚事务 7) 最后关闭资源
代码实现:
1 import cn.ks.utils.JDBCUtils; 2 import java.sql.Connection; 3 import java.sql.PreparedStatement; 4 import java.sql.SQLException; 5 public class Demo6 { 6 //没有异常,提交事务,出现异常回滚事务 7 public static void main(String[] args) { 8 //1) 注册驱动 9 Connection connection = null; 10 PreparedStatement ps = null; 11 try { 12 //2) 获取连接 13 connection = JDBCUtils.getConnection(); 14 //3) 开启事务 15 connection.setAutoCommit(false); 16 //4) 获取到 PreparedStatement 17 //从 jack 扣钱 18 ps = connection.prepareStatement("update account set balance = balance - ? where name=?"); 19 ps.setInt(1, 500); 20 ps.setString(2,"Jack"); 21 ps.executeUpdate(); 22 //出现异常 23 System.out.println(100 / 0); 24 //给 rose 加钱 25 ps = connection.prepareStatement("update account set balance = balance + ? where name=?"); 26 ps.setInt(1, 500); 27 ps.setString(2,"Rose"); 28 ps.executeUpdate(); 29 //提交事务 30 connection.commit(); 31 System.out.println("转账成功"); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 try { 35 //事务的回滚 36 connection.rollback(); 37 } catch (SQLException e1) { 38 e1.printStackTrace(); 39 } 40 System.out.println("转账失败"); 41 } 42 finally { 43 //7) 关闭资源 44 JDBCUtils.close(ps,connection); 45 } 46 } 47 }