JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库。
一、JDBC组件
- DriverManager: 一系列的数据库驱动程序,匹配连接使用通信协议从JAVA应用程序中获取合适的数据库驱动程序
- Connection: 所有和数据库相关的上下文操作通过该接口的实现类来提供数据库的事务操作,Statement对象等接口方法
- Statement: 使用创建该接口的对象来进行SQL语句提交到数据库
- ResultSet: 使用SQL查询数据库的结果报存在ResultSet中,是一个set集合,多条数据需要通过遍历该集合来获取对应的结果
- SQLException: 所有JDBC操作异常的统一处理类
二、JDBC编程
1.通过jdbc连接MySQL数据库
需要下载所有需要的MySQL第三方驱动包,使用Maven添加以下依赖
<!--JDBC驱动包依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
2.编程
数据库连接的4个核心配置:
- 数据库驱动包的全路径
- com.mysql.cj.jdbc.Driver
- 根据MySQL的版本确定是否加cj
- 请求数据库的URL
- jdbc:mysql://localhost:3306/库名?useSSL=false&serverTimezone=UTC
- mysql:表示链接的数据库类型
- localhost:端口号,数据库默认端口号为3306
- useSLL:一种网络安全协议
- serverTimezone是指定时区
- (后面两个需要根据自己的MySQL版本进行添加)
- 数据库账号:自己的数据库账号名称
- 数据库密码:自己数据库所设置的密码
编程步骤:
- 加载数据库驱动
- 获取数据库连接
- 创建Statement对象
- 执行SQL语句
- 处理结果集
3.demo演示
student表中的数据:
public class JDBCDemo {
public static void main(String[] args) {
try {
//1.加载数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
/**
* 传递的参数包含三部分:
* String url,:数据库连接的URL
* ->jdbc:数据库的子协议://ip+端口/数据库名
* ->jdbc:mysql://localhost:3306/test
* String user,:数据库连接的用户名
* String password:数据库的密码
* 返回的是connection对象
*
*/
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC",
"root","123456");
//3.创建Statement对象
/**
* statement有三种实现:
* 1、创建基本的Statement对象
* java.sql.Statement -> Statement statement = connection.createStatement()
* 2、创建预编译的Statement对象
* java.sql.PreparedStatement -> PreparedStatement preparedStatement = connection.prepareStatement("");
* 3、创建带有回调功能的CallableStatement对象
* java.sql.CallableStatement -> CallableStatement callableStatement = connection.prepareCall();
*/
Statement statement = connection.createStatement();
String sql = "select * from student";
//4.执行上SQL语句
ResultSet resultSet = statement.executeQuery(sql);
//5.处理结果集
while (resultSet.next()){
String sname = resultSet.getString("Sname");
System.out.println(sname);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
三、SQL注入问题
1、什么是SQL注入问题
由于dao层中执行的SQL语句是拼接出来的,其中一部分内容是用户从用户端输入的,当传入数据中包含SQL关键字(1=1 or)时,就有可能通过这些关键字来改变SQL语句的语义,从而执行一些特殊的操作,这种就称之为SQL注入问题。
demo演示:
- 有user表如下:
- 模拟用户登录
public class Login1 {
public static boolean login(String userName,String passwd){
boolean result = false;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC",
"root","123456");
String sql = "select * from user where userName="+userName+"and passwd="+passwd;
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()){
result = true;
}
resultSet.close();
statement.close();
connection.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
String name = "'wang'"; //此处字符串要加单引号,否则会出错
String passwd = "123";
boolean login = login(name,passwd);
if (login){
System.out.println("登陆成功!");
}else {
System.out.println("登陆失败!");
}
}
}
- SQL注入
public class Login1 {
public static boolean login(String userName,String passwd){
boolean result = false;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC",
"root","123456");
String sql = "select * from user where userName="+userName+"and passwd="+passwd;
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()){
result = true;
}
resultSet.close();
statement.close();
connection.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
//SQL注入
String name = "'wang'";
String passwd = "1=1 or 1234567";
boolean login = login(name,passwd);
if (login){
System.out.println("登陆成功!");
}else {
System.out.println("登陆失败!");
}
}
}
此时可以发现SQL中包含关键字(1=1 or)时,无论输入什么密码都可以登陆成功,这就是SQL注入问题
-
解决SQL注入问题
- 采用预编译的Statement对象处理可以解决SQL注入问题
- PreparedStatement采用预编译机制将SQL语句的主干和参数分别传输数据库,数据库是可以分辨SQL语句中的主干和参数,这样即使SQL中带有关键字,数据库也是仅仅将其当做参数使用,关键字就不能起作用。
-
PreparedStatement主要的优势:
- 防止SQL注入
- 使用预编译机制,执行效率是高于Statement
- sql语句中参数值是通过?形式来替代参数,在使用PreparedStatement方法set?对应的值,相对于SQL直接拼接更加优雅
-
PreparedStatement和Statement区别:
- 语法不同:
- PreparedStatement可以使用预编译的SQL,
- Statement使用静态SQL
- 效率不同:
- PreparedStatement执行效率高于Statement
- 安全性不同:
- PreparedStatement可以防止SQL注入
- Statement不能防止SQL注入
- 语法不同:
上述SQL注入问题demo
public class Login2 {
public static boolean login(String userName,String passwd){
boolean result = false;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC",
"root","123456");
/**
* sql语句上参数通过占位符'?'来代替参数
* 创建PreparedStatement对象后进行参数赋值
* 参数赋值使用preparedStatement.setString(1, "1");
* 该方法两个参数,第一个参数表示是要给SQL中哪一个位置进行赋值,默认其实值是1
* 第二该参数就是对应赋值位置需要进行的赋值
*
*/
String sql = "select * from user where userName=? and passwd=?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1,userName);
ps.setString(2,passwd);
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()){
result = true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
String name = "'wang'";
String passwd = "1=1 or 123";
boolean login = login(name,passwd);
if (login){
System.out.println("登陆成功!");
}else {
System.out.println("登陆失败!");
}
}
}
四、jdbc提供的事务相关操作
connection.setAutoCommit(false);//false:手动提交事务 true:自动提交事务,默认为true
Savepoint t1 = connection.setSavepoint("t1");//设置保存点
connection.commit(); //事务提交到数据库
connection.rollback(); //事务回滚到初始状态
connection.rollback(t1);//事务回滚到t1保存点
connection.setTransactionIsolation(Connection.TRANSACTION_NONE);//设置隔离级别
隔离级别:
Connection.TRANSACTION_NONE 不支持事务
Connection.TRANSACTION_READ_UNCOMMITTED 未提交读
Connection.TRANSACTION_READ_COMMITTED 可提交读
Connection.TRANSACTION_REPEATABLE_READ 可重复读
Connection.TRANSACTION_SERIALIZABLE 串行化