13-数据库之JDBC3
一. JDBC总结 - 使用流程
1. 创建Maven项目, 配置pom.xml,添加依赖
<!-- 指定JDK版本-->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 连接MySQL数据库的依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
</dependencies>
2. 加载JDBC驱动
使用JDBC前,必须加载所需JDBC的驱动程序
Class.forName("com.mysql.cj.jdbc.Driver"); // 抛出异常
3. 获取数据库连接
要连接数据库,必须使用JDBC中的DriverManager类
DriverManager负责JDBC驱动程序, 使用特定的URL和属性来里获取连接
// 注意: 连接数据需要注意连接的是哪个数据?
String url = "jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url,user,password);
4. 执行SQL语句
4.1 Statement对象执行SQL语句
执行SQL语句最常用的方法是使用Statement对象, Statement对象用于执行静态的SQL语句
- 执行DDL语句, exexute(sql); 返回boolean: (true | false)
- 执行DML语句,executeUpdate(sql); 返回整型, (0|非0) — UPDATE, INSERT,DELETE
- 执行DQL语句,executeQuery(sql); 返回集合,ResultSet集合,
next()
Statement statement = connection.createStatement();
String sql = "SELECT id,name,job FROM student";
ResultSet rs = statement.executeQuery(sql);
4.2 preparedStatement对象执行SQL语句
5. 处理查询结果
- DDL: 通过true|false,判断DDL语句是否运行成功
- DML:通过num>0,判断DDL语句是否运行成功
- DQL:使用ResultSet对象的getXXX方法获取查询结果中的数据
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
}
6. 关闭连接
connection.close()
// 通过try/catch自动关闭功能
try (Connection connection = DBUtil.getConnection();){}
7.练习
练习:
1.查看王克晶老师所带班级的信息, 列出老师的姓名,老师性别,班级名称,所在楼层
2.查看1年级1班共有多少人?
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 1.查看王克晶老师所带班级的信息, 列出老师的姓名,老师性别,班级名称,所在楼层
* SELECT t.name,t.gender,c.name,c.floor
* FROM teacher t
* JOIN class c ON t.id = c.teacher_id
* WHERE t.name='王克晶';
*/
public class JDBCTest1 {
public static void main(String[] args) {
try (Connection connection = DBUtil.getConnection();){
Statement statement = connection.createStatement();
String sql = "SELECT t.name,t.gender,c.name,c.floor " +
"FROM teacher t " +
"JOIN class c ON t.id = c.teacher_id " +
"WHERE t.name='王克晶'";
ResultSet rs = statement.executeQuery(sql);
// 获取查询结果
while (rs.next()){
String tname = rs.getString("t.name");
String gender = rs.getString("t.gender");
String cname = rs.getString("c.name");
int floor = rs.getInt("floor");
System.out.println(tname+","+gender+","+cname+","+floor);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 2.查看1年级1班共有多少人?
* SELECT c.name,COUNT(*) number
* FROM class c
* JOIN student s ON s.class_id = c.id
* WHERE c.name='1年级1班';
*/
public class JDBCTest2 {
public static void main(String[] args) {
try (Connection connection = DBUtil.getConnection();){
Statement statement = connection.createStatement();
String sql = "SELECT c.name,COUNT(*) number " +
"FROM class c " +
"JOIN student s ON s.class_id = c.id " +
"WHERE c.name='1年级1班';";
ResultSet rs = statement.executeQuery(sql);
while (rs.next()){
String cname = rs.getString("c.name");
int number = rs.getInt("number");
System.out.println(cname+"人数:"+number);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
二. SQL安全性 - SQL注入
1. 演示SQL注入攻击
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* 模拟一个场景: 登录验证与SQL注入攻击
* 用户输入用户名, 密码, 数据库校验
* 正确: 登录成功, 欢迎你: 昵称
* 错误: 用户名或密码错误
*
*/
public class JDBCDemo8 {
public static void main(String[] args) {
try (Connection connection = DBUtil.getConnection();){
// 接收用户输入
Scanner scanner = new Scanner(System.in);
System.out.println("登录功能");
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
// 执行SQL语句
Statement statement = connection.createStatement();
String sql = "SELECT id,username,password,nickname " +
"FROM userinfo " +
"WHERE username='"+username+"' " +
"AND password = '"+password+"'";
// System.out.println(sql);
ResultSet rs = statement.executeQuery(sql);
// 处理结果
if (rs.next()){
String nickname = rs.getString("nickname");
System.out.println("登录成功,欢迎你:"+nickname);
}else{
System.out.println("用户名或密码错误");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
SELECT id,username,password,nickname FROM userinfo WHERE username=??? AND password = ???;
- 用户输入:
用户名:肖战, 密码: 123456
SELECT id,username,password,nickname FROM userinfo WHERE username='肖战' AND password = '123456';
- 用户输入
用户名: xxx, 密码: a’ OR ‘1’='1;
SELECT id,username,password,nickname FROM userinfo WHERE username=''肖战 AND password = 'a' OR '1'='1';
- AND 优先级高, 判断username=‘肖战’ AND password = ‘a’ ,没有任何数据
- 然后判断OR后面的值, ‘1’=‘1’ 结果为true, 登录成功 — SQL注入攻击
解决: 使用预编译SQL
2. 解决SQL注入攻击 - 预编译SQL
- 定义SQL语句, ? 占位
SELECT id,username,password,nickname FROM userinfo WHERE username='?' AND password = '?';
- 创建执行预编译SQL的执行对象
PreparedStatement ps = connection.PreparedStatement(sql)
;- 设置值
ps.setxxx('第几个问号',值)
- 执行SQL语句
ps.executeQuery()
package jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
/**
* 预编译SQL: 防止SQL注入攻击
* 1. 先将SQL语句发送给数据库,让数据库生成执行计划, 理解SQL执行意图(?占位)
* 2. 创建预编译SQL语句的执行对象(PreparedStatement)
* 3. 设置SQL语句中 ? 的值,并执行SQL语句 (setString(index,value))
*/
public class JDBCDemo9 {
public static void main(String[] args) {
try (Connection connection = DBUtil.getConnection();){
// 接收用户的输入
Scanner scanner = new Scanner(System.in);
System.out.println("登录功能");
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
// 执行预编译SQL
String sql = "SELECT id,username,password,nickname " +
"FROM userinfo " +
"WHERE username=? AND password = ?";
PreparedStatement ps = connection.prepareStatement(sql);
/*
设置 ? 的值
? 是整型, ps.setInt()
? 是字符串,ps.setString()
*/
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
// 判断结果
if (rs.next()){
String nickname = rs.getString("nickname");
System.out.println("登录成功,欢迎:"+nickname);
}else{
System.out.println("用户名或密码错误");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
三. 数据库连接池
阿里巴巴数据库连接池(Druid) 是阿里巴巴开源的基于JDBC数据库连接池
1. 使用流程
1.1 添加依赖 pom.xml ,并刷新Maven
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
1.2 初始化连接池, 指定最大的连接数和初始连接数
package jdbc;
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class DruidUtil {
private static DruidDataSource ds;
// 连接池
static {
initDataSource();
}
private static void initDataSource() {
ds = new DruidDataSource();
ds.setUsername("root");
ds.setPassword("root");
ds.setUrl("jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true");
ds.setMaxActive(30); // 最大连接数
ds.setInitialSize(5); // 初始连接数
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
1.3 设置数据库连接信息
ds.setUrl("jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true");
ds.setMaxActive(30); // 最大连接数
ds.setInitialSize(5); // 初始连接数
package jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
/**
* 数据库连接池
*/
public class JDBCDemo10 {
public static void main(String[] args) {
try (Connection connection = DruidUtil.getConnection();){
// 接收用户的输入
Scanner scanner = new Scanner(System.in);
System.out.println("登录功能");
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
// 执行预编译SQL
String sql = "SELECT id,username,password,nickname FROM userinfo WHERE username=? AND password=?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
// 判断结果
if (rs.next()){
String nickname = rs.getString("nickname");
System.out.println("登录成功,欢迎:"+nickname);
}else{
System.out.println("用户名或密码错误");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
2. 连接池优点
- 提高性能
数据库连接池中可以预先创建一定数量的连接,并一直保持打开的状态, 避免每次请求都需要建立连接和关闭连接的开销, 提高了系统的性能- 节约资源
数据库连接池通过避免每次请求都创建和关闭连接降低了系统中数据库连接的数量, 节省了系统资源, 提高了系统的可扩展性- 避免连接泄露
数据库连接池在使用完毕后会归还连接到连接池中, 避免了长时间占用连接的情况, 减少了连接泄露的机会