目录
一. JDBC 基本概念
二. 快速入门
三. 详解JDBC中各个接口和类
四. 抽取JDBC工具类
内容
一. JDBC 基本概念
概念:Java DataBase Connectivity:Java数据库连接,是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
JDBC本质:其实是官方定义的一套操作所有关系型数据库的规则,即接口。各个数据库服务商去实现这套接口,提供数据库驱动jar包。我们可以使用这套JDBC接口编程,真正执行的代码是驱动jar包中的实现类。
二. 快速入门
步骤:
1. 导入驱动jar包 mysql-connector-java-8.0.15.jar
2. 注册驱动
3. 获取数据连接对象 Connection
4. 定义SQL语句
5. 获取执行SQL语句的对象 Statement(后期我们不会再使用该类,须使用 PreparedStatement)
6. 执行SQL语句,接收返回结果
7. 处理结果
8. 释放资源:倒序释放,先获取的资源后释放,后获取的资源先释放。
代码实现:
// 1. 导入驱动jar包 jar包版本:8.0.15
// 2. 注册驱动。com.mysql.cj.jdbc.Driver加载进字节码文件时,Driver类中的静态代码被执行:注册驱动。
// MySQL 5之后注册驱动的逻辑可以不用写。
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "88888888");
// 4. 定义SQL语句,语句后面不可以加分号
String sql = "UPDATE account SET balance = 500 WHERE id = 1";
// 5. 获取sql对象,Statement
Statement stmt = connection.createStatement();
// 6. 执行SQL语句,接收返回结果
int count = stmt.executeUpdate(sql);
// 7. 处理结果
System.out.println("count = " + count);
// 8. 释放资源
stmt.close();
connection.close();
三. 详解JDBC中各个接口和类
1、DriverManager:驱动管理对象
① 注册驱动:加载指定的【数据库驱动jar包】
static synchronized void registerDriver(java.sql.Driver driver)
开发中我们使用如下代码注册驱动:
Class.forName("com.mysql.cj.jdbc.Driver");
因为,Driver 中存在如下静态代码块,静态代码会随着Driver类的加载而加载,注册驱动。
Driver中的静态代码块:
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
注意:MySQL5之后的驱动jar包可以省略注册驱动的步骤。建议仍然注册,兼容老版本。
②获取数据库连接
方法:
public static Connection getConnection(String url, String user, String password);
参数:
url:指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
jdbc:mysql://
是固定写法。- ip地址(域名)可以找到指定的计算机
- 端口号:可以找到指定计算机上安装的mysql服务器
- user:数据库登录用户名
- password:数据库登录密码
示例:jdbc:mysql://localhost:3306/db2
:连接本机db2数据库
- 细节:如果连接的是本机的mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
2、Connection:数据库连接对象
功能
① 获取执行 sql 的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
②管理事务:
- 开启事务:void setAutoCommit(boolean autoCommit); 设置参数为false,即开启事务。
- 提交事务:void commit()
- 回滚事务:void rollback()
3、Statement
执行SQL的对象。用于执行静态SQL语句并返回其生成的结果的对象。静态sql容易产生sql注入问题。
SQL注入问题:在拼接sql语句时,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题。
① 执行sql
// 可以执行任意的sql,了解即可。
boolean execute(String sql)
// 执行DML(update、insert、delete)语句、DDL(对数据库和表操作 create、alter、drop)语句
// DDL不常用。因为为了更方便的设计数据库和表,我们一般不使用JDBC来完成。
// 返回值为INT类型:表示执行sql语句影响的行数。执行DDL语句,没有返回结果,默认0。
// 返回值作用:可以通过影响的行数,判断DML语句是否执行成功,返回值>0则执行成功;返回,执行失败。
int executeUpdate(String sql)
// 执行DQL(select)语句
ResultSet executeQuery(String sql)
4、ResultSet
结果集对象,封装查询的结果
next 方法:ResultSet光标最初位于第一行之前,第一次调用方法next使第一行成为当前行,第二行调用使第二行称为当前行,以此类推。当返回false时,表示光标位于最后一行之后;返回true时,表示新的当前行有效。
// 游标向下移动一行,判断当前行是否最后一行末尾。
boolean next();
调用 next()
方法后,若返回值为true,那么,我们就可以使用诸如:String getString(int columnIndex)
、int getInt(int columnIndex)
、String getString(String columnLabel)
、int getInt(String columnLabel)
等等 getXXX 方法,获取当前行的记录数据。
其中:
若方法参数传 int,代表列的编号,默认从1开始。
若方法参数传 String,代表列的名称,即数据库对应的字段。
例如:
while (resultSet.next()) { // 判断当前行是否有效
int id = resultSet.getInt(1);
String name = resultSet.getString("name");
BigDecimal balance = resultSet.getBigDecimal("balance");
}
实现功能:查询db1数据库中employee表,将查询结果封装为对象,然后装载集合。然后打印。
public List<Employee> findAll() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<Employee> list = new ArrayList<>();
try {
// 1. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取mysql服务器连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "88888888");
// 3. 定义sql语句
String sql = "SELECT * FROM employee";
// 4. 获取sql执行对象
stmt = conn.createStatement();
// 5. 执行sql语句
rs = stmt.executeQuery(sql);
// 6. 处理数据
Employee emp = null;
while (rs.next()) {
int pk_id = rs.getInt("pk_id");
String name = rs.getString("name");
double salary = rs.getDouble("salary");
String gender = rs.getString("gender");
Date join_date = rs.getDate("join_date");
int department_id = rs.getInt("department_id");
emp = new Employee();
emp.setPk_id(pk_id);
emp.setName(name);
emp.setSalary(salary);
emp.setJoin_date(join_date);
emp.setDepartment_id(department_id);
if (gender != null) {
emp.setGender(gender.toCharArray()[0]);
}
list.add(emp);
}
} 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 {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
5、PreparedStatement
用于执行预编译SQL语句并返回其生成的结果的对象。以后开发中使用 PreparedStatement 替代 Statement,来完成增删改查的所有操作,因为其解决了SQL注入问题,且效率高于 Statement 。后起都使用PreparedStatement
① 预编译sql:参数使用 ? 作为占位符。
② 使用步骤:
- 导入驱动jar包 mysql-connector-java-8.0.15.jar
- 注册驱动
- 获取数据连接对象 Connection
- 定义SQL语句
- 注意:sql的参数使用 ? 作为占位符。如: SELECT * FROM user WHERE username = ? AND password = ?;
- 获取执行sql语句的对象 PreparedStatement prepareStatement(String sql);
- 给 ? 赋值
- 方法:使用 setXXX(参数1, 参数2)
- 参数1:?的位置编号,从1开始;
- 参数2:?的值;
- 方法:使用 setXXX(参数1, 参数2)
- 执行sql语句,不需要传递sql语句参数。
- 处理结果
- 释放资源
四. 抽取JDBC工具类: JDBCUtils
通过练习,我们发现,在注册驱动、获取mysql服务连接、释放资源等代码实现,存在冗余。因为开发中,我们可以封装JDBC的工具类,简化代码。
① 我们可以在工具类中,在静态代码块中完成驱动的注册;
② 封装获取连接对象,url/user/password参数的获取,我们通过配置文件jdbc.properties来获取,保证工具类的通用性;
③ 释放资源,根据需求,重载即可。
配置文件:jdbc.properties
url=jdbc:mysql://localhost:3306/db1
user=root
password=88888888
driver=com.mysql.cj.jdbc.Driver
JDBCUtils.java:
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();
// 动态获取指定的配置文件路径。直接src为根路径,统一资源定位符,[配置文件在src下创建]
URL resUrl = classLoader.getResource("jdbc.properties");
String path = resUrl.getPath();
System.out.println("path = " + path);
// 2. 加载文件
pro.load(new FileReader(path));
// 3. 获取属性赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
try {
// 4. 注册驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @description: 获取连接对象
* @param
* @return: java.sql.Connection
* @author: King
* @date: 2019-03-01 17:18
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
/**
* @description: 释放资源
* @param stmt sql执行对象
* @param conn mysql服务连接对象
* @return: void
* @author: King
* @date: 2019-03-01 17:20
*/
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();
}
}
}
/**
* @description: 释放资源
* @param rs 结果集对象
* @param stmt sql执行对象
* @param conn mysql服务连接对象
* @return: void
* @author: King
* @date: 2019-03-01 17:21
*/
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();
}
}
}
/**
* @description: 释放资源
* @param rs 结果集对象
* @param pstmt sql执行对象
* @param conn mysql服务连接对象
* @return: void
* @author: King
* @date: 2019-03-01 19:21
*/
public static void close(ResultSet rs, PreparedStatement pstmt, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}