一、引言
1.1数据的存储
在单纯的开发java程序中,数据是储存在内存里的,关闭或重启程序,数据就会丢失没法长期保存。
这个时候有下面的解决方法
- I/O流,储存在本地磁盘,解决了长期保存的问题,但是没有结构和逻辑,不方便管理和维护
- 通过关系型数据库,如02-MySQl将数据交给数据库来管理。
1.2数据的操作
明确了数据存在哪,但想对数据做增删改查该怎么办,也就是用JDBC。
2.1JDBC的概念
- JDBC就是java语言操作关系型数据库的一些api
- java负责提供接口规范,数据库厂商负责实现,JDBC独立于任意一种数据库
- 程序员去调用接口,接口去第哦啊用驱动的实现
三、JDBC快速入门
3.1JDBC
java
ini
public class JDBCQuick {
public static void main(String[] args) throws Exception {
//1.注册驱动(jdk6之后甚至可以不写)
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接对象(在本机ip且端口没有改变的情况, localhost:3306可以省略)
String url="jdbc:mysql://localhost:3306/atguigu";//连接地址
String username="root";
String password="cby";
Connection connection = DriverManager.getConnection(url, username, password);
//3.获取执行sql语句的对象
Statement statement = connection.createStatement();
//4.编写sql语句并执行,接受返回的结果集(也就是mysql的表格)
String sql="SELECT emp_id,emp_name,emp_salary,emp_age FROM t_emp";
ResultSet resultSet = statement.executeQuery(sql);
//5.处理结果:遍历resultSet结果集
while(resultSet.next()){
int empId = resultSet.getInt("emp_id");
String empName = resultSet.getString("emp_name");
double empSalary = resultSet.getDouble("emp_salary");
int empAge = resultSet.getInt("emp_age");
System.out.println(empId+"\t"+empName+"\t"+empSalary+"\t"+empAge);
}
//6.释放资源(先开后关)
resultSet.close();
statement.close();
connection.close();
}
}
四、核心 API 理解
4.1 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");- 在 Java 中,当使用 JDBC连接数据库时,需要加载duiying1数据库特定的驱动程序,以便与数据库进行通信。
- 从 JDK6 开始,不再需要显式地调用
Class.forName()来加载 JDBC 驱动程序,只要在类路径中集成了对应的 jar 文件,会自动在初始化时注册驱动程序。
4.2 Connection
-
Connection 接口是 JDBC API 的重要接口,用于建立与数据库的通信通道。换而言之,Connection 对象不为空,则代表一次数据库连接。
-
在建立连接时,需要指定数据库 URL、用户名、密码参数。
- URL:
jdbc:mysql://localhost:3306/atguigu
String url="jdbc:mysql://localhost:3306/atguigu";//连接地址
String username="root";
String password="cby";
Connection connection = DriverManager.getConnection(url, username, password);
- URL:
-
Connection 接口还负责管理事务,Connection 接口提供了 commit 和 rollback 方法,用于提交事务和回滚事务。
-
可以创建 Statement 对象,用于执行 SQL 语句并与数据库进行交互。
-
在使用 JDBC 技术时,必须要先获取 Connection 对象,在使用完毕后,要释放资源,避免资源占用浪费及泄漏。
4.3 Statement
-
Statement 接口用于执行 SQL 语句并与数据库进行交互。它是 JDBC API 中的一个重要接口。通过 Statement 对象,可以向数据库发送 SQL 语句并获取执行结果。
-
结果可以是一个或多个结果。
- 增删改:受影响行数单个结果。
- 查询:单行单列、多行多列、单行多列等结果。
-
但是 Statement 接口在执行 SQL 语句时,会产生 SQL 注入攻击问题:
- 当使用 Statement 执行动态构建的 SQL 查询时,往往需要将查询条件与 SQL 语句拼接在一起,直接将参数和 SQL 语句一并生成,让 SQL 的查询条件始终为 true 得到结果。
eg:
ini
//3. 获取执行SQL语句对象
Statement statement = connection.createStatement();
System.out.println("请输入员工姓名:");
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
//4. 编写SQL语句,并执行,接受返回的结果
String sql = "SELECT emp_id,emp_name,emp_salary,emp_age FROM t_emp WHERE emp_name = '"+name+"'";
ResultSet resultSet = statement.executeQuery(sql);
如果输入的是xxx' or '1'= '1
那么在mysql里面就会是 :SELECT emp_id,emp_name,emp_salary,emp_age FROM t_emp WHERE emp_name = 'xxx' or '1'= '1'
结果一直为true,会把整个数据库遍历
4.4PreparedStatement
-
PrepareStatement 是 Statement 的子接口,用来执行预编译的 SQL 查询。作用:
-
预编译 SQL 语句,在执行到connection.prepareStatement(sql) 时就会预编译 SQL 语句,那个时候 SQL 语句已经固定了。因此它也带来了性能的提升。同一 SQL 语句在多次执行下可以复用,不用每次重新编译和解析。(但这个我还不太懂具体是怎样带来的性能提升)
-
防止 SQL 注入,采用问号占位符的方式
- 只要这个位置本来就该写一个具体的数据(数字 / 字符串 / 日期) ,就可以换成
?; - 只要这个位置是 SQL 语法(表、列、关键字、符号),就不能用
? - 所有传入的参数都会被一对单引号包裹起来。(这里很特殊,他是把传入的值单纯当成一坨来看的,不管里面是什么,再奇怪的组合都不管,就只是把他当成一串字符串,里面的
'、or、=、--全部只是普通文字,不会被数据库识别成 SQL 语法符号,然后再去完成和where条件的比较)
-
使用即使传入xxx' or '1'= '1在mysql里面得到的WHERE emp_name = 'xxx' or '1'= '1'和Statement看上去一模一样但是已经发生根本的改变了。
用PreparedStatement传进去的内容,数据库只当 "一整块纯文本",不解析语法
ini
//3. 获取执行SQL语句对象
PreparedStatement preparedStatement = connection.prepareStatement("SELECT emp_id,emp_name,emp_salary,emp_age FROM t_emp WHERE emp_name = ?");
System.out.println("请输入员工姓名:");
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
// 给占位符赋值
preparedStatement.setString(1, name);
//4. 执行SQL语句,接受返回的结果 ResultSet resultSet = preparedStatement.executeQuery();
PreparedStatement 靠预编译锁定 SQL 结构,用?占位符隔离数据,再自动转义参数引号(JDBC 驱动调用 setString 时自动把参数里所有单引号替换成两个单引号,避免截断sql语句),双重防护杜绝 SQL 注入。
4.5 ResultSet
-
ResultSet是 JDBC API 中的一个接口,用于表示从数据库中执行查询语句所返回的结果集。它提供了一种用于遍历和访问查询结果的方式。 -
遍历结果:ResultSet 可以使用
next()方法将游标移动到结果集的下一行,逐行遍历数据库查询的结果,返回值为 boolean 类型,true 代表有下一行结果,false 则代表没有。 -
获取单列结果:可以通过 getXxx 的方法获取单列的数据,该方法为重载方法,支持索引和列名进行获取。
- 按列下标(数字,查询 SQL 里字段从左往右数,第 1 个字段填 1,依次递增;)
ini
String name = rs.getString(2);
int age = rs.getInt(4);
- 按列名字符串(推荐)
ini
String name = rs.getString("emp_name");
int age = rs.getInt("emp_age");