JDBC
一、引言
1.1 如何操作数据
使用客户端工具访问数据库,需要手工建立链接,输入用户名和密码登录,编写SQL语句,点击执行,查看操作结果(结果集或受影响行数)。
1.2 实际开发中,会采用客户端操作数据库吗?
在实际开发过程中,当用户的数据发生改变时,不可能通过客户端操作执行SQL语句,因为操作量过大,无法保证效率和正确性
二、JDBC(Java DataBase Connectivity)
2.1 什么是JDBC?
JDBC(Java DataBase Connectivity) Java连接数据库,可以使用Java语言连接数据库完成CRUD操作
2.2 JDBC核心思想
Java中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。
由数据库厂商提供驱动实现类(Driver数据库驱动)
2.3 JDBC API
JDBC 是由多个接口和类进行功能实现
类型 | 全限定名 | 简介 |
---|---|---|
class | java.sql.DriverManager | 管理多个数据库驱动类,提供了获取数据库连接的方法 |
interface | java.sql.Connection | 代表一个数据库连接(当Connection不是NULL时,表示已连接一个数据库) |
interface | java.sql.Statement | 发送SQL语句到数据库的工具 |
interface | java.sql.ResultSet | 保存SQL查询语句的结果数据(结果集) |
class | java.sql.SQLException | 处理数据库应用程序时所发生的异常 |
2.4 环境搭建
- 在项目下新建 lib 文件夹,用于存放 jar 文件
- 将MySQL驱动文件mysql-connector-java-5.1.25-bin.jar 复制到项目的lib文件夹中
- 选中lib文件夹 右键选择 add as library,点击OK
三、JDBC开发步骤
3.1 注册驱动
使用Class.forName(“com.mysql.jdbc.Driver”); 手动加载字节码文件到JVM中
Class.forName("com.mysql.jdbc.Driver");
3.2 连接数据库
- 通过DriverManager.getConnection(url,user,password);获得数据库连接对象
- URL:jdbc:mysql://localhost:3306/database
- user:root
- password:1234
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/database
?useUnicode=true&characterEncoding=utf8","root","1234");
- URL(Uniform Resource Locator)统一资源定位符:由协议、IP、端口、SID(程序实例名称)组成
3.3 获取发送SQL的对象
通过Connection对象获得Statement对象,用于对数据库进行通用访问的
Statement statement = connection.createStatement();
3.4 执行SQL语句
编写SQL语句,并执行,接收执行后的结果
int result = statement.executeUpdate("update stu set where ");
- 注意:在编写DML语句时,一定要注意字符串参数的符号是单引号 ‘值’
- DML语句:增、删、改时,执行的结果是受影响行数(int类型)。
- DQL语句:查询时,返回的是数据结果集(ResultSet结果集)
3.5 处理结果
接收并处理操作结果
if(result > 0){
System.out.println("执行成功");
}
- 受影响行数:逻辑判断,方法返回
- 查询结果集:迭代、依次获取
3.6 释放资源
遵循的是先开后关的原则,释放过程中用到的所有资源对象
statement.close();
connection.close();
四、 ResultSet(结果集)
在执行查询SQL后,存放查询到的结果集数据
4.1 接收结果集
ResultSet rs = statement.executeQuery(sql)
ResultSet rs = statement.executeQuery("SELECT * FROM stu");
4.2 遍历ResultSet中的数据
ResultSet以表(Table)结构进行临时结果的存储,需要通过JDBC API将其中的数据进行依次获取
-
数据行指针:初始位置在第一行数据前,每调用一次boolean next()方法,ResultSet中指针向下移动一行,结果为true,表示当前行有数据
-
rs.getXxx(“列名”); 根据列名获得数据
-
rs.getXxx(整数下标); 代表根据列的编号顺序获得!从1开始
boolean next() throws SQLException;//判断rs结果集中下一行是否有数据
4.2.1 遍历方法
int getInt(int columnIndex) throws SQLException;//获得当前行的第N列的int值
int getInt(String columnLabel) throws SQLException;//获得当前行columnLabel列的int值
注意:列的编号从1开始
4.3 综合案例
对stu表所有的数据进行遍历
4.3.1 根据列的名称获取
public class Test {
public static void main(String[] args) throws Exception{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获得连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8","root","root");
//3.获取执行SQL的对象
Statement statement = connection.createStatement();
//4.编写SQL语句
String sql = "select student_id,student_name,sex,birthday,phone,GradeId from stu;";
ResultSet resultSet = statement.executeQuery(sql);
//5.处理结果 (结果集!)
while(resultSet.next()){//判断结果集中是否有下一行!
//根据列名获取当前行每一列的数据
String student_id = resultSet.getString("student_id");
String student_name = resultSet.getString("student_name");
String sex = resultSet.getString("sex");
String birthday = resultSet.getString("birthday");
String phone = resultSet.getString("phone");
int gradeId = resultSet.getInt("gradeId");
System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
}
//6.释放资源
resultSet.close();
statement.close();
connection.close();
}
}
4.3.2 根据列的下标获取
//5.处理结果 (结果集!)
while(resultSet.next()){//判断结果集中是否有下一行!
//根据列的编号获取当前行每一列的数据
String student_id = resultSet.getString(1);
String student_name = resultSet.getString(2);
String sex = resultSet.getString(3);
String birthday = resultSet.getString(4);
String phone= resultSet.getString(5);
int gradeId = resultSet.getInt(6);
System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
}
五、常见错误
- java.lang.ClassNotFoundException 找不到类(类名书写错误、没有导入jar包)
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException 与SQL语句相关的错误(表名列名书写错误、约束错误、插入的值是String类型,但是没有加单引号)建议:在客户端工具中测试sql语句后,再粘贴到代码中来
- com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry ‘S1003’ for key ‘PRIMARY’ 原因:主键值已存在!更改要插入的主键值
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Unknown column ‘password’ in
- 可能输入的值的类型不对,确定插入元素时,对应的值的类型是否争取
六、SQL注入问题
6.1 什么是SQL注入
当用户输入的数据中有SQL关键字或语法时,并且参与了SQL语句的编译,导致SQL语句编译后条件结果为true,一直得到正确的结果。称为SQL注入
6.2如何避免SQL注入
由于编写的SQL语句,是在用户输入数据后,整合后再编译成SQL语句。所以为了避免SQL注入的问题,使SQL语句在用户输入数据前,SQL语句已经完成编译,成为了完整的SQL语句,再进行填充数据
七、 PreparedStatement
PreparedStatement接口继承了Statement接口。执行SQL语句的方法没有区别。
7.1 PreparedStatement的应用
作用:1.预编译SQL语句,效率高!
2.安全,避免SQL注入
3.可以动态的填充数据,执行多个同构的SQL语句
7.1.1 参数标记
//1.预编译SQL语句
PreparedStatement pstmt = connection.prepareStatement(sql);
注意:PreparedStatement应用时,SQL字符串的参数都由?符号站位,被称为参数标记。在执行该SQL语句前,要为每个?参数赋值
7.1.2 动态参数绑定
pstmt.setXxx(下标,值); 参数下标是从1开始,为指定占位符下标绑定值
//2.为占位符下标赋值
pstmt.setString(1,username);
pstmt.setString(2,password);