回顾之前连接数据库的方式
DOS指令方式,图形化方式
缺点:
只能是简单地SQL语句的测试,无法在项目中操作数据库
引出通过java代码方式连接数据库---JDBC
概述:JDBC是一套连接数据库的标准;具体的实现是由不同的数据库提供的
JDBC核心思想:
Java 中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。由数据库厂商提供驱动实现类(Driver 数据库驱动)。
JDBC操作步骤:
1、加载驱动
2、获取连接对象
3、获取执行对象
4、执行CRUD操作
5、获取执行结果
6、关闭数据库资源
常见的异常问题:
ClassNotFoundException 驱动加载失败
MySQLSyntaxErrorException: 数据库或SQL语句异常
SQLException: Access denied 账户或密码出错
MySQLIntegrityConstraintViolationException 主键冲突
DML操作
//案例:给岗位表添加一条数据
public class DMLTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.通过驱动管理器获取连接对象 alt+enter弹出异常与赋值变量
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1", "root", "123");
//3.通过连接对象获取执行对象
Statement st = conn.createStatement();
//4.进行增删改查操作 增删改:executeUpdate
//String sql = "insert into t_jobs(job_id,job_title,min_salary,max_salary) values('QF_PRA','PRA',13000,18000)";
//String sql = "update t_jobs set min_salary=20000,max_salary=30000 where job_id='QF_PRA'";
String sql = "delete from t_jobs where job_id='QF_PRA'";
//5.反馈结果
int result = st.executeUpdate(sql);
System.out.println("影响的条数:"+result);
//6.关闭资源,先关小的再关大的
DBUtils.closeAll(st,conn);
}
}
DQL操作
//查询学生表的数据
public class DQLTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb1", "root", "123");
Statement st = conn.createStatement();
//获取结果集
ResultSet rs = st.executeQuery("select * from student");
List<Student> list = new ArrayList<>();
//循环遍历,获取所有记录(每次循环,都是一条记录
while(rs.next()){
//int id = rs.getInt(1); //1代表第1列;应用场景:聚合查询反馈
int id = rs.getInt("id"); //id:字段名获取值
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println(id+"-->"+name+"-->"+age);
//应用场景:反馈零散的内容,应该需要对象封装,
list.add(new Student(id,name,age));
}
System.out.println("集合中存储的数据:"+list);
DBUtils.closeAll(rs,st,conn); //关闭资源
}
}
遍历 ResultSet 中的数据
ResultSet 以表(table)结构进行临时结果的存储,需要通过 JDBC API 将其中数据进行依次获取。
数据行指针:初始位置在第一行数据前,每调用一次 boolean next()方法ResultSet 的指针向下移动一行,结果为 true,表示当前行有数据。
rs.getXxx(整数);代表根据列的编号顺序获得,从 1 开始。
rs.getXxx("列名");代表根据列名获得。
常见错误
java.lang.ClassNotFoundException:找不到类(类名书写错误、没有导入jar包)
java.sql.SQLException:与sql语句相关的错误 (约束错误、表名列名书写错误) 建议:在客户端工具中测试SQL语句之后再粘贴在代码中
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 原因:列值Sting类型没有加单引号
Duplicate entry '1' for key 'PRIMARY' 原因,主键值已存在或混乱,更改主键值或清空表
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'password' in
原因:可能输入的值的类型不对,确定是否插入的元素时对应的值的类型正确
SQL注入问题
什么是 SQL 注入
用户输入的数据中有 SQL 关键字或语法并且参与了 SQL 语句的编译,导致 SQL 语句编译后的条件含义为 true,一直得到正确的结果。这种现象称为 SQL 注入。
如何避免 SQL 注入
由于编写的 SQL 语句是在用户输入数据,整合后再进行编译。所以为了避免 SQL 注入的问题,我们要使 SQL 语句在用户输入数据前就已进行编译成完整的 SQL 语句,再进行填充数据。
PreparedStatement【重点
】
PreparedStatement 继承了 Statement 接口,执行 SQL 语句的方法无异。
PreparedStatement的应用
作用:
预编译SQL 语句,效率高。
安全,避免SQL注入 。
可以动态的填充数据,执行多个同构的 SQL 语句。
private static boolean login2(String username, String password) {
Connection conn = null;
PreparedStatement prst = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///mydb1", "root", "123");
//PreparedStatement:预处理执行对象 ?为占位符
//好处: 1.安全性高,解决了sql注入问题
//2. 执行性能会更高
//3. 方便进行批处理操作
prst = conn.prepareStatement("select count(*) from user where username=? and password=?");
//参数1:对应第一个占位符? 下标从1开始
prst.setString(1,username);
prst.setString(2,password);
//获取结果集
//sql注入的隐患
rs = prst.executeQuery();
if(rs.next()){
int result = rs.getInt(1); //聚合函数只有一个字段
return result>0; //result不小于0,则返回true
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.closeAll(rs,prst,conn);
}
return false;
}