持续学习&持续更新中…
学习态度:守破离
JDBC FOR MySQL
什么是JDBC
如何通过Java操作数据库
JDBC是属于JavaSE的一部分
下载MySQL的JDBC实现
地址链接:
解释:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-versions.html
下载:https://dev.mysql.com/downloads/connector/j/
JDBC细节
JDBC——MySQL的url格式
JDBC版本与JavaSE版本
注意:从Java SE 6(JDBC4.0)开始我们就不用显示的注册驱动程序了。
JDBC使用步骤
最基本的JDBC程序——MySQL驱动包6.x之前
public class Main {
private static final String DRIVER_MYSQL = "com.mysql.jdbc.Driver";
private static final String mysqlURL = "jdbc:mysql://localhost:3306/xmg";
private static final String user = "root";
private static final String password = "root";
/* JDBC属于JavaSE的一部分 具体看图示Java.png */
/* 最基本的JDBC访问MySQL程序 */
public static void main(String[] args) throws Exception {
/* 步骤一: 将Driver注册到DriverManager 加载驱动 */
// 方式一 推荐使用 装载MySQL驱动类
Class.forName(DRIVER_MYSQL);
// 方式二 不推荐使用
// DriverManager.registerDriver(new Driver());
/* 步骤二:利用DriverManager创建数据库连接 获取数据库连接对象 */
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
/* 步骤三:利用Connection创建SQL语句对象 */
Statement statement = connection.createStatement();
/* 步骤四:执行SQL语句 SQL语句不用加;*/
final String sql = "INSERT INTO customer (id, name, phone, company_id) VALUES (101, 'lphahaha', '11122223333', 1)";
statement.execute(sql);
/* 步骤五:关闭释放资源 */
statement.close();
connection.close();
}
}
也可以使用try-with-resources来简写代码,确保关闭释放资源
public static void main(String[] args) {
try {
Class.forName(DRIVER_MYSQL);
} catch (ClassNotFoundException e) {
System.err.println("加载驱动失败!");
e.printStackTrace();
}
// try-with-resources
try (
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
Statement statement = connection.createStatement()
) {
final String sql = "INSERT INTO customer (id, name, phone, company_id) VALUES (101, 'lphahaha', '11122223333', 1)";
statement.execute(sql);
} catch (Exception e) {
e.printStackTrace();
}
}
又因为从Java SE 6(JDBC4.0)开始我们就不用显示的注册驱动程序。
所以代码可以简化如下:
public static void main(String[] args) throws Exception{
try (
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
Statement statement = connection.createStatement()
) {
final String sql = "INSERT INTO customer (name, phone, company_id) VALUES ('niuniuniu', '66666666666', 2)";
statement.execute(sql);
}
}
最基本的JDBC程序——MySQL驱动包6.x开始及之后
public class Main {
private static final String DRIVER_MYSQL = "com.mysql.cj.jdbc.Driver";
private static final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
private static final String user = "root";
private static final String password = "root";
public static void main(String[] args) {
try {
// Java SE 6开始就不用显示加载驱动了
// 这儿显示加载驱动的目的是为了学习这块的知识点
Class.forName(DRIVER_MYSQL);
} catch (ClassNotFoundException e) {
System.out.println("加载驱动失败!");
}
try (
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
Statement statement = connection.createStatement()
) {
final String sql = "INSERT INTO customer (id, name, phone, company_id) VALUES (104, 'hahaha', '44444444444', 3)";
statement.execute(sql);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Statement常用API
ResultSet常用API
注意:
-
游标刚开始没有指向任何记录,指向的是第一条记录之前的东西,当调用next方法之后,它才会指向第一条记录。
-
使用
resultSet.getXXX(int columnIndex)
获取数据时,columnIndex是从1开始的(columnIndex指的是表的第几列)。 -
使用
resultSet.getXXX(String columnLabel)
获取数据时,columnLabel不一定是表的列名(如果查询的时候使用了别名,那么此时的columnLabel就是别名)。
使用上述API查询表中的数据
private static void test3() throws Exception {
final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
final String user = "root";
final String password = "root";
final String sql = "SELECT * FROM customer";
try (
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)
) {
while (resultSet.next()) {
System.out.println(
"name : " + resultSet.getString("name") + " ;" +
"phone : " + resultSet.getString("phone") + " ;" +
"company id : " + resultSet.getInt("company_id")
);
}
}
}
使用上述API查询表中的数据(优化版)
在向数据库插入某个记录的时候,有可能该记录的某一列上的数据不存在,因此就没有向该列插入数据,导致该列的数据为NULL。
如果是这种情况的话,我们查询数据的时候就不应该使用基本类型,而是应该使用对象数据类型(假设表中某个INT类型的字段的数据为NULL,那么直接使用resultSet.getInt()
的话,返回值会是0
),而ResultSet又没有提供resultSet.getInteger()
之类的API,因此我们可以使用resultSet.getObject()
,查询出数据后可以进行判空操作,判空操作之后再继续下一步操作。
private static void test4() throws Exception {
final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
final String user = "root";
final String password = "root";
final String sql = "SELECT company_id myCpid FROM customer";
try (
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)
) {
while (resultSet.next()) {
Object myCpid = resultSet.getObject("myCpid");
if (myCpid != null) {
// int id = (Integer) myCpid;
// 推荐使用对象类型
Integer id = (Integer) myCpid;
// 执行相应的操作
// ......
}
}
}
}
举一个SQL注入问题的例子(使用Statement)
① 建表 & 插入数据:
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name varchar(20) NOT NULL UNIQUE,
pswd varchar(20) DEFAULT NULL
);
INSERT INTO user (name, pswd) VALUES ('123', '123');
INSERT INTO user (name, pswd) VALUES ('lpruoyu', 'lpruoyu');
INSERT INTO user (name, pswd) VALUES ('root', 'root');
② 编写Java代码(使用Statement来进行查询):
private static void login(String name, String pswd) throws Exception {
final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
final String user = "root";
final String password = "root";
final String sql = "SELECT * FROM user WHERE name = '" + name + "' AND pswd = '" + pswd + "'";
try (
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)
) {
// 返回结果只可能有1条或者0条
if (resultSet.next()) {
// 返回值有一条记录 找到该用户
System.out.println("登录成功!");
} else {
// 没有返回值 没有找到该用户
System.out.println("登录失败,用户名或密码不正确!");
}
}
}
如果用户将pswd传入' OR '1' = '1
的话,就相当于执行下列的语句(此时name的值是什么已经无所谓了,我就什么都没写):
很明显,这样做的话,使用上述Java代码进行登录,每次都会登录成功(由于AND
优先级高于 OR
,因此OR前面的name = '' AND pswd = ''
是一个整体A,OR后面的'1' = '1'
是一个整体B,这时WHERE后面就有两部分组成:A OR B,而B是恒成立的,因此每次都会查询出结果。),这就导致了SQL Injection(SQl注入)问题,程序就会产生漏洞。
那么该如何修复这种问题呢?使用PreparedStatement!
PreparedStatement
使用PreparedStatement来优化上述例子,防止SQL Injection问题:
private static void login(String name, String pswd) throws Exception {
final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
final String user = "root";
final String password = "root";
final String sql = "SELECT * FROM user WHERE name = ? AND pswd = ?";
try (
Connection connection = DriverManager.getConnection(mysqlURL, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(sql);
) {
preparedStatement.setString(1, name);
preparedStatement.setString(2, pswd);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
// ResultSet没法使用try-with-resources
// 所以ResultSet需要关闭
resultSet.close();
}
}
参考
李明杰: Java从0到架构师②JavaEE技术基石.
本文完,感谢您的关注支持!