JDBC
简单写了一些Java的JDBC基本操作流程和主要的知识点,对于某些简单的对象暂无详细展开,还请参考JDK文档。
1 概述
1.1 简介
JDBC(Java数据库连接,Java Database Connectivity)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。
JDBC大体还是比较简单的,主要就是包括这5个阶段:
1加载驱动(针对不同数据库) 2链接数据库 3创建状态通道并执行sql 4获得结果集 5 关闭连接
当然也要处理异常,异常在这些阶段都可能发生。另外通常来讲连接数据库和关闭连接的操作都是交由连接池来操作的。
1.2 核心组件
DriverManager: 此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配。
Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象。
Connection:该界面具有用于联系数据库的所有方法。连接对象表示通信上下文,即,与数据库的所有通信仅通过连接对象。
Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数。
ResultSet:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据。
SQLException:此类处理数据库应用程序中发生的任何错误
2 简单案例
JDBC大体就是这样的流程,各JDBC对象的具体方法后续不再详细展开,仅介绍重点的东西,如需了解还请参考JDK文档。
public class Demo1 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获得链接
String username = "root";
String password = "root";
String url = "jdbc:mysql://localhost:3310/kkb2?serverTimezone=UTC";
// 3.开始链接
connection = DriverManager.getConnection(url, username, password);
// 4.定义sql,创建状态通道(进行sql语句的发送)
statement = connection.createStatement();
resultSet = statement.executeQuery("select * from employee");// 执行sql
// 5.取出结果集
while (resultSet.next()) { // 判断是否有下一条数据
// 取出数据 resultSet.getXXX("列名"); xxx表示数据类型
String name = resultSet.getString("name");
System.out.println(name);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
// 6.关闭资源
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
3 预状态通道
3.1 SQL注入问题
由于普通的jdbc是使用字符串拼接的方式去处理sql的,就非常容易导致sql注入问题,如:
String username ="admin";
String password=" 'abc' or 1=1 ";
String sql="select * from users where username= '"+username+"' and password= "+password;
resultSet = statement.executeQuery(sql);
显然在这种情况下就能够直接登录,无论数据库中是否存在该用户。其他也会有诸多问题,不一一列举。
3.2 PreparedStatement
如何处理?在jdbc中有一个接口: PreparedStatement 扩展了Statement接口
这个接口在Statement接口的基础上增加了一些方法,举个案例:(省略try-catch-finally)
PreparedStatement pstmt = null;
String SQL = "Update Employees SET age = ? WHERE id = ?";
pstmt = connection.prepareStatement(SQL);
pstmt.setObject(1,age);
pstmt.setObject(2,id);
ResultSet = pstmt.executeUpdate();
pstmt.close();
该接口对象首先需要由connection.prepareStatement(SQL)
方法来创建,显然SQL语句是预先进行编译过的,
与普通的 statement = connection.createStatement();
不同,一个sql语句是直接对应一个PreparedStatement对象的。
之后,执行sql前,如果sql中有?,就先要使用setXXX()(如:setObjec())方法来进行sql的替换,将sql中的?替换为Java中的对象,XXX表示Java中的某类型的对象。下标从1开始。
然后直接利用该通道对象执行该通道创建时附带的sql语句,得到结果集。如果忘记替换掉sql中的?,将收到一个SQLException。
最后关闭该对象。
总结:对比Statement
和PreparedStatement
:
(1)Statement
属于状态通道,PreparedStatement
属于预状态通道
(2)预状态通道会先编译sql语句,再去执行,比Statement
执行效率高
(3)预状态通道支持占位符?,给占位符赋值的时候,位置从1开始
(4)预状态通道可以防止sql注入,原因:预状态通道在处理值的时候以字符串替换的方式处理(用Java对象替换),而不是拼接。
4 批处理
批量处理允许您将相关的SQL语句分组到批处理中,并通过对数据库的一次调用提交它们。
当您一次向数据库发送多个SQL语句时,可以减少连接数据库的开销,从而提高性能。
Statement批处理
以下是使用语句对象的批处理的典型步骤序列
- 使用createStatement()
方法创建Statement对象。
- 使用setAutoCommit()
将auto-commit设置为false 。
- 使用addBatch()
方法在创建的语句对象上添加您喜欢的SQL语句到批处理中。
- 在创建的语句对象上使用executeBatch()
方法执行所有SQL语句。
- 最后,使用commit()
方法提交所有更改。
Statement stmt = conn.createStatement();
conn.setAutoCommit(false);
//sql1
String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(200,'Zia', 'Ali', 30)";
stmt.addBatch(SQL);
//sql2
String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(201,'Raj', 'Kumar', 35)";
stmt.addBatch(SQL);
//sql3
String SQL = "UPDATE Employees SET age = 35 WHERE id = 100";
stmt.addBatch(SQL);
int[] count = stmt.executeBatch();
conn.commit();
PreparedStatement 差不多,不同的是要先创建sql语句,之后直接加入批处理即可。如果有占位符就得先替换掉再加入批处理。
5 配置文件连接数据库
db.properties
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?serverTimezone=Asia/Shanghai
username=root
password=123456
方法1(IO流):
InputStream inputStream = 当前类名.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(inputStream);
dirverName = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("user");
password = properties.getProperty("password");
方法2(ResourceBundle):
static {
ResourceBundle bundle = ResourceBundle.getBundle("db");
driver = bundle.getString("driverClass");
url = bundle.getString("url");
username = bundle.getString("username");
password = bundle.getString("password");
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
6 连接池
待完善。
7 事务
待完善。