目录
1.DiverManager
(1)注册
(2)连接
2.Connection
(1)事务管理:
3.Statement
(1)执行DML、DDL
4.ResultSet
(1)代码测试
(2)案例:
5.PreparedStatement
(1) SQL注入演示:
(2)解决SQL注入
(3)原理(如何预编译,如何防止sql注入)
1.DiverManager
PS:本文以JDK11为标准
(1)注册
但是我们上一篇笔记里是这样获取驱动的:
Class.forName("com.mysql.jdbc.Driver");
这是为什么呢?,其实可以点到Driver的源码:
(注意一下新版本的idea推荐的是com.mysql.cj.jdbc.Driver,而不是老套的com.mysql.jdbc.Driver)
实质上这里有个静态代码块,而根据已学的知识,这个静态代码块会随着类的加载自动进行,不需要调什么方法,就可以注册上了。
不过5.0之后class这一行不写也没事,因为:
有对应文件然后读取,会自己做这个操作
(2)连接
2.Connection
(1)事务管理:
public class JDBCDemo3_Connection {
public static void main(String[] args) throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String p = "1234";
Connection conn = DriverManager.getConnection(url, username, p);
//3. 定义sql
String sql1 = "update account set money = 3000 where id = 1";
String sql2 = "update account set money = 3000 where id = 2";
//4. 获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
try {
// 开启事务
conn.setAutoCommit(false);
//5. 执行sql
int count1 = stmt.executeUpdate(sql1);//受影响的行数
//6. 处理结果
System.out.println(count1);
int i = 3/0;
//5. 执行sql
int count2 = stmt.executeUpdate(sql2);//受影响的行数
//6. 处理结果
System.out.println(count2);
// 提交事务
conn.commit();
} catch (Exception throwables) {
// 回滚事务
conn.rollback();
throwables.printStackTrace();
}
//7. 释放资源
stmt.close();
conn.close();
}
}
(2)获取对象——在其他知识点中融合体现
3.Statement
(1)执行DML、DDL
public class JDBCDemo4_Statement {
@Test
public void testDML() throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String p = "1234";
Connection conn = DriverManager.getConnection(url, username, p);
//3. 定义sql
String sql = "update account set money = 3000 where id = 1";
//4. 获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5. 执行sql
int count = stmt.executeUpdate(sql);//执行完DML语句,受影响的行数
//6. 处理结果
//System.out.println(count);
if(count > 0){
System.out.println("修改成功~");
}else{
System.out.println("修改失败~");
}
//7. 释放资源
stmt.close();
conn.close();
}
@Test
public void testDDL() throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String p= "1234";
Connection conn = DriverManager.getConnection(url, username, p);
//3. 定义sql
String sql = "drop database db2";
//4. 获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5. 执行sql
int count = stmt.executeUpdate(sql);//执行完DDL语句,可能是0
//6. 处理结果
//System.out.println(count);
System.out.println(count);
//7. 释放资源
stmt.close();
conn.close();
}
}
(2)执行DML
在resultset案例中体现.
4.ResultSet
(1)代码测试
public class JDBCDemo5_ResultSet {
@Test
public void testResultSet() throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String p= "1234";
Connection conn = DriverManager.getConnection(url, username, p);
//3. 定义sql
String sql = "select * from account";
//4. 获取statement对象
Statement stmt = conn.createStatement();
//5. 执行sql
ResultSet rs = stmt.executeQuery(sql);
//6. 处理结果, 遍历rs中的所有数据
// 6.1 光标向下移动一行,并且判断当前行是否有数据
while (rs.next()){
//6.2 获取数据 getXxx()
int id = rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println(id);
System.out.println(name);
System.out.println(money);
System.out.println("--------------");
}
//7. 释放资源
rs.close();
stmt.close();
conn.close();
}
}
(2)案例:
public static void main(String[] args) throws Exception {
//1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");//这个没办法我的Jar包就是老的,你要换一个jar包此案向,今天就算了
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///dbTest?useSSL=false";
String username = "root";
String p = "***";
Connection conn = DriverManager.getConnection(url, username, p);
//3. 定义sql
String sql = "select * from emp";
//4. 获取statement对象
Statement stmt = conn.createStatement();
//5. 执行sql
ResultSet rs = stmt.executeQuery(sql);
//创建数组
ArrayList emps = new ArrayList<>();
//6. 处理结果, 遍历rs中的所有数据
// 6.1 光标向下移动一行,并且判断当前行是否有数据
while (rs.next()){
//6.2 获取数据 getXxx()
int id = rs.getInt("id");
String name = rs.getString("ename");
double money = rs.getDouble("salary");
String date =rs.getString("joindate");
Emp emp = new Emp();
emp.setEname(name);
emp.setId(id);
emp.setJoindate(date);
emp.setSalary(money);
emps.add(emp);
//System.out.println("--------------");
}
for (Emp emp : emps) {
System.out.println(emp.toString());
}
//7. 释放资源
rs.close();
stmt.close();
conn.close();
}
5.PreparedStatement
(1) SQL注入演示:
@Test
public void testLogin_Inject() throws Exception {
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String p = "1234";
Connection conn = DriverManager.getConnection(url, username, p);
// 接收用户输入 用户名和密码
String name = "hfkjsfhskj";
String pwd = "' or '1' = '1";
String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";
System.out.println(sql);
// 获取stmt对象
Statement stmt = conn.createStatement();
// 执行sql
ResultSet rs = stmt.executeQuery(sql);
// 判断登录是否成功
if(rs.next()){
System.out.println("登录成功~");
}else{
System.out.println("登录失败~");
}
//7. 释放资源
rs.close();
stmt.close();
conn.close();
}
注意,这里为什么能登录上呢,我们把name pwd代入sql后来解析一下:
select * from tb_user where username = '123' and password = '' or '1' = '1'
首先不管你用户名输的对不对,and后面连接的password=''(空)肯定是错的,而后面Or的1=1肯定是对的,所以其实就是以前学过的逻辑表达式: (x&false)| true =true
即:select * from tb_user where true
——》就是全部都可以了呀
(2)解决SQL注入
// 接收用户输入 用户名和密码
String name = "zhangsan";
String pwd = "' or '1' = '1";
// 定义sql
String sql = "select * from tb_user where username = ? and password = ?";
// 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
// 执行sql
ResultSet rs = pstmt.executeQuery();
那他又是怎么解决的呢?
是因为在set的时候他会进行转义,比如'机器就明白你想传递一个单引号,那就/',保持他的原义,而不会和password里面语法上的单引号混淆,就会把pwd作为整体看待了。
(3)原理(如何预编译,如何防止sql注入)
日志上就很明显:
不开启就是这样:
开启有prepare的预编译:



