课程笔记Day29
-
JDBC快速入门
-
JDBC相关API
-
SQL注入问题
-
JDBC工具类
第一章 JDBC快速入门
第01节 基础理论
1、JDBC介绍
2、JDBC步骤
操作步骤
1. 导入jar包
2. 找到驱动 Driver
3. 获取连接 Connection
4. 获取操作 Statement
5. 查询结果 ResultSet
6. 释放资源
第02节 案例代码
1、准备数据
-- 1. 创建数据库
DROP DATABASE IF EXISTS mydb08;
CREATE DATABASE IF NOT EXISTS mydb08;
USE mydb08;
-- 2. 创建表
DROP TABLE IF EXISTS student;
CREATE TABLE IF NOT EXISTS student(
sid INT PRIMARY KEY AUTO_INCREMENT,
sname VARCHAR(20) NOT NULL,
sage INT
);
-- 3. 插入数据
INSERT INTO student VALUES (NULL,'定浩',18);
INSERT INTO student VALUES (NULL,'郭龙',19);
INSERT INTO student VALUES (NULL,'黄杰',17);
-- 4. 查询结果
SELECT * FROM student;
2、代码实现
//JDBC的快速入门
public class Demo {
//启动的顺序: C->S->R
public static void main(String[] args) throws Exception {
String username = "root";
String password = "root";
String url = "jdbc:mysql://localhost:3306/mydb08";
String sql = "SELECT * FROM student";
//----------------------
//找到驱动的对象
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理者,获取到连接 Connection
Connection conn = DriverManager.getConnection(url, username, password);
//通过连接 conn 获取到操作的对象 Statement
Statement stat = conn.createStatement();
//执行SQL语句,查询数据库
ResultSet resu = stat.executeQuery(sql);
//循环遍历结果集,获取到结果集当中的数据
while (resu.next()) { //判断是否还存在下一行的数据,如果存在,则进入循环
//获取到指定的数据, 通过列名获取到指定的数据值
int id = resu.getInt("sid");
String name = resu.getString("sname");
int age = resu.getInt("sage");
System.out.println(id + "\t" + name + "\t" + age);
}
//释放资源,关闭的顺序是相反的。R->S->C
resu.close();
stat.close();
conn.close();
}
}
第二章 JDBC相关API
第01节 DriverManager类
底层代码 DriverManager.getConnection(url,username,password)
public static Connection getConnection(String url,String user, String password) throws SQLException {
//1. 创建了 Properties的对象 info
java.util.Properties info = new java.util.Properties();
//2. 判断用户名是否为空, 如果不为空,则存放到properties当中
if (user != null) {
info.put("user", user);
}
//3. 判断密码是否为空, 如果不为空,则存放到properties当中
if (password != null) {
info.put("password", password);
}
//4. 调用自己的方法 getConnection(三个参数方法)
return (getConnection(url, info, Reflection.getCallerClass()));
}
发现问题:
底层会将 username 和 password 进行 Properties 的封装
底层实现
//构造方法,私有化修饰了
private DriverManager(){}
//静态代码块,只要类加载的时候,都会加载静态代码块,加载静态代码块的之后,才会调用 getConnection()方法
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
//调用下面的方法
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
}
return null;
}
});
//这里,什么时候驱动为null呢?就是上面出现异常之后为空。
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
//底层在遍历驱动
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
//核心代码:底层会加载所有的驱动,通过ClassLoader去加载驱动
Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
注意事项:
Class.forName("com.mysql.jdbc.Driver");
可以省略不写的,在高版本的JDBC当中,找驱动的底层会自动完成。
底层代码当中,会有扫描驱动的操作。(底层在遍历驱动)
Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());
第02节 Connection 接口
1、核心方法
1. Statement createStatement() //获取到操作数据的对象 Statement 接口对象
2. PreparedStatement prepareStatement(String sql) //预置操作数据的对象 PreparedStatement 接口对象
-------------------
3. void setAutoCommit(boolean autoCommit) //是否开启自动提交事务,如果传递值是 false 表示手动开启事务,默认true
4. void commit() //提交事务的操作,多组SQL语句执行,没有问题的情况下,则提交
5. void rollback() //回滚事务的操作,当SQL语句执行过程当中,出现异常的时候,则回滚
2、事务处理
//目标:学习JDBC当中的事务处理方式
@SuppressWarnings("all")
public class Test01 {
public static void main(String[] args) {
String username = "root";
String password = "root";
String url = "jdbc:mysql://localhost:3306/mydb08";
String sql = "INSERT INTO student VALUES (3,'佳佳',18)";
Connection conn = null;
Statement stat = null;
//----------------------
try {
//通过驱动管理者,获取到连接 Connection
conn = DriverManager.getConnection(url, username, password);
//****[1]开启事务手动提交****
conn.setAutoCommit(false);
//************************
//获取到操作数据的对象
stat = conn.createStatement();
//执行SQL语句
//执行增删改 采用的方法是 executeUpdate 返回的结果是影响的行数
int count = stat.executeUpdate(sql);
//****[2]执行成功,则提交事务****
conn.commit();
//************************
System.out.println(count > 0 ? "执行成功" : "执行失败");
} catch (SQLException e) {
e.printStackTrace();
//****[3]如果出现了异常,需要事务的回滚****
try {
conn.rollback();
System.out.println("事务回滚执行了...");
} catch (SQLException e1) {
e1.printStackTrace();
}
//************************
}finally {
//进行资源的释放
if (stat!=null){
try {
stat.close();
System.out.println("资源释放stat...");
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
System.out.println("资源释放conn...");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
第03节 Statement 接口
1、核心方法
1. void addBatch(String sql) //可以用于批量执行SQL语句
2. int[] executeBatch() //与上面的方法搭配使用
3. int executeUpdate(String sql) //执行更新操作。增、删、改 的SQL语句,需要使用此方法,返回的是影响行数
4. ResultSet executeQuery(String sql) //执行查询的方法。返回的是结果集 ResultSet
2、方法演示
//目标:学习Statement的常用方法
@SuppressWarnings("all")
public class Test02 {
String username = "root";
String password = "root";
String url = "jdbc:mysql://localhost:3306/mydb08";
Connection conn = null;
Statement stat = null;
ResultSet resu = null;
@Before
public void start() throws Exception {
//通过驱动管理者,获取到连接 Connection
conn = DriverManager.getConnection(url, username, password);
//通过conn去获取到操作数据库的对象 stat
stat = conn.createStatement();
}
@Test
public void testAddBatch() throws Exception{
String sql1 = "INSERT INTO student VALUES (NULL,'佳佳1',18)";
String sql2 = "INSERT INTO student VALUES (NULL,'佳佳2',18)";
//将SQL语句批量的添加进去
stat.addBatch(sql1);
stat.addBatch(sql2);
//执行操作
int[] array = stat.executeBatch();
System.out.println(Arrays.toString(array));
}
@Test
public void testExecuteUpdate() throws Exception {
String sql = "INSERT INTO student VALUES (NULL,'佳佳',18)";
//executeUpdate 执行的是增删改
int count = stat.executeUpdate(sql);
System.out.println("count = " + count);
Assert.assertEquals(1, count);
}
@Test
public void testExecuteQuery() throws Exception {
String sql = "SELECT * FROM student";
//执行的是查询
resu = stat.executeQuery(sql);
//循环查找
while (resu.next()) {
int id = resu.getInt("sid");
String name = resu.getString("sname");
int age = resu.getInt("sage");
System.out.println(id + "\t" + name + "\t" + age);
}
}
@After
public void end() throws Exception {
//释放资源,关闭的顺序是相反的。R->S->C
if (resu != null) {
resu.close();
}
if (stat != null) {
stat.close();
}
if (conn != null) {
conn.close();
}
}
}
第04节 ResultSet接口
1、核心方法
1. boolean next() //判断是否还存在下一行的数据,可以作用在while循环判断和if判断语句当中
2. int getInt(int columnIndex) //获取到int类型的数据,参数是查询结果集的第几个索引
3. int getInt(String columnLabel) //获取到int类型的数据,参数是需要查询的列的名称
4. String getString(int columnIndex) //获取到String类型的数据,参数是查询结果集的第几个索引
5. String getString(String columnLabel) //获取到String类型的数据,参数是需要查询的列的名称
2、方法演示
//目标:学习ResultSet的常用方法
@SuppressWarnings("all")
public class Test03 {
String username = "root";
String password = "root";
String url = "jdbc:mysql://localhost:3306/mydb08";
Connection conn = null;
Statement stat = null;
ResultSet resu = null;
@Before
public void start() throws Exception {
//通过驱动管理者,获取到连接 Connection
conn = DriverManager.getConnection(url, username, password);
//通过conn去获取到操作数据库的对象 stat
stat = conn.createStatement();
}
@Test
public void testNext() throws Exception{
String sql = "SELECT * FROM student";
ResultSet resu = stat.executeQuery(sql);
boolean flag1 = resu.next();
System.out.println("flag1 = " + flag1);
boolean flag2 = resu.next();
System.out.println("flag2 = " + flag2);
boolean flag3 = resu.next();
System.out.println("flag3 = " + flag3);
boolean flag4 = resu.next();
System.out.println("flag4 = " + flag4);
}
@Test
public void testGetInt() throws Exception{
String sql = "SELECT sname,sid FROM student";
ResultSet resu = stat.executeQuery(sql);
while (resu.next()) {
int id1 = resu.getInt("sid");
//System.out.println(id1);
int id2 = resu.getInt(2);
System.out.println(id1+","+id2);
}
}
@Test
public void testGetString() throws Exception{
String sql = "SELECT sid,sname FROM student";
ResultSet resu = stat.executeQuery(sql);
while (resu.next()) {
String name1 = resu.getString("sname");
System.out.println("name1 = " + name1);
String name2 = resu.getString(2);
System.out.println("name2 = " + name2);
}
}
@After
public void end() throws Exception {
//释放资源,关闭的顺序是相反的。R->S->C
if (resu != null) {
resu.close();
}
if (stat != null) {
stat.close();
}
if (conn != null) {
conn.close();
}
}
}
第三章 SQL注入问题
第01节 数据准备
1、数据库准备
-- 1. 创建新表
DROP TABLE IF EXISTS t_user;
CREATE TABLE IF NOT EXISTS t_user(
uid INT PRIMARY KEY AUTO_INCREMENT,
uname VARCHAR(20),
upass VARCHAR(20)
);
-- 2. 插入数据
INSERT INTO t_user VALUES (NULL,'zhangsan','333');
INSERT INTO t_user VALUES (NULL,'lisi','444');
-- 3. 查询数据
SELECT * FROM t_user;
/*
需求:做一个登录的效果,如果输入用户名和密码,只有都是正确的才能登录成功
*/
SELECT * FROM t_user WHERE uname = 'zhangsan' AND upass = '333';
SELECT * FROM t_user WHERE uname = 'zhangsan' OR '1=1' AND upass = '随便写都可以的';
2、演示问题
//目标: 演示SQL注入的问题
@SuppressWarnings("all")
public class Test01 {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名: zhangsan' OR '1=1 ");
String nameStr = sc.nextLine();
System.out.println("请输入密码:");
String passStr = sc.nextLine();
String username = "root";
String password = "root";
String url = "jdbc:mysql://localhost:3306/mydb08";
String sql = "SELECT * FROM t_user WHERE uname = '" + nameStr + "' AND upass = " + passStr;
//通过驱动管理者,获取到连接 Connection
Connection conn = DriverManager.getConnection(url, username, password);
//通过连接 conn 获取到操作的对象 Statement
Statement stat = conn.createStatement();
//执行SQL语句,查询数据库
ResultSet resu = stat.executeQuery(sql);
//判断是否存在数据呢?
String message = "Sorry 登录失败,请检查账号和密码";
//只要存在下一条的记录,则表示登录成功
if (resu.next()) {
message = "登录成功:" + nameStr;
}
System.out.println("message = " + message);
//释放资源
resu.close();
stat.close();
conn.close();
}
}
第02节 解决问题
1、需要使用API
//1. 当我们获取到conn的对象之后,需要获取到预置语句的对象PreparedStatement
PreparedStatement prepareStatement(String sql)
//2. 提前定义好SQL语句,采用占位符,占据位置。最后去设置占位符的值
void setInt(int parameterIndex, int x) //参数1: 第几个问号, 参数2: 需要插入的值
void setString(int parameterIndex, String x) //参数1: 第几个问号,参数2: 需要插入的值
//3. 执行SQL的操作
int executeUpdate() //执行 增、删、改操作
ResultSet executeQuery() //执行 查询操作
2、解决问题
//目标: 解决SQL注入的问题
@SuppressWarnings("all")
public class Test02 {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名: zhangsan' OR '1=1");
String nameStr = sc.nextLine();
System.out.println("请输入密码:");
String passStr = sc.nextLine();
String username = "root";
String password = "root";
String url = "jdbc:mysql://localhost:3306/mydb08";
//预置的SQL语句,里面的变量由问号(英文) 去代替
String sql = "SELECT * FROM t_user WHERE uname = ? AND upass = ?";
//通过驱动管理者,获取到连接 Connection
Connection conn = DriverManager.getConnection(url, username, password);
//*********************
//通过连接 conn 获取到操作的对象 PreparedStatement
PreparedStatement stat = conn.prepareStatement(sql);
//设置参数值
stat.setString(1,nameStr);
stat.setString(2,passStr);
//执行SQL语句,查询数据库
ResultSet resu = stat.executeQuery();
//*********************
//判断是否存在数据呢?
String message = "Sorry 登录失败,请检查账号和密码";
//只要存在下一条的记录,则表示登录成功
if (resu.next()) {
message = "登录成功:" + nameStr;
}
System.out.println("message = " + message);
//释放资源
resu.close();
stat.close();
conn.close();
}
}
第四章 JDBC工具类
第01节 配置文件
位置: src/jdbc.properties
url=jdbc:mysql://localhost:3306/mydb08
username=root
password=root
第02节 工具类
//定义JDBC的工具类
@SuppressWarnings("all")
public class JDBCUtils {
private static String url = null;
private static String username = null;
private static String password = null;
private static Connection conn = null;
private static PreparedStatement stat = null;
private static ResultSet resu = null;
/**
* 静态代码块,只加载一次。
*/
static{
try {
//在反射章节讲过的类加载器,专门加载src下面的文件 properties,加载成为流对象
InputStream is = ClassLoader.getSystemResourceAsStream("jdbc.properties");
Properties pp = new Properties();
pp.load(is);
is.close();
url = pp.getProperty("url");
username = pp.getProperty("username");
password = pp.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 自定义的方法,用于获取数据库的连接对象 Connection
*
* @return
*/
public static Connection getConnection() {
try {
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 自定义的方法,用于操作 增删改数据
*/
public static int exeUpdate(String sql, Map<Integer, Object> map) {
//INSERT INTO 表名称 VALUES (NULL,?,?,?);
//map.put(1,1001);
//map.put(2,"张三");
//map.put(3,23);
int lineNumber = -1;
try {
PreparedStatement stat = getConnection().prepareStatement(sql);
//询问map集合到底有多少个数据
for (int i = 0; map != null && i < map.size(); i++) {
//得到索引值
int index = i + 1;
stat.setObject(index, map.get(index));
}
//执行SQL语句
lineNumber = stat.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return lineNumber;
}
/**
* 自定义的方法,用于操作
*/
public static ResultSet exeQuery(String sql) {
//调用下面的重载方法
return exeQuery(sql,null);
}
/**
* 自定义的方法,用于操作 查询数据
*/
public static ResultSet exeQuery(String sql, Map<Integer, Object> map) {
//SELECT * FROM 表名 WHERE 列名1=? AND 列名2 = ?;
try {
PreparedStatement stat = getConnection().prepareStatement(sql);
//询问map集合到底有多少个数据
for (int i = 0; map != null && i < map.size(); i++) {
//得到索引值
int index = i + 1;
stat.setObject(index, map.get(index));
}
//具体的查询操作
resu = stat.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return resu;
}
/***
* 释放资源
*/
public static void close() {
//判断
if (resu != null) {
try {
resu.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//判断
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//判断
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
第03节 测试类
//测试类
@SuppressWarnings("all")
public class Test {
public static void main(String[] args) throws Exception {
//测试添加数据的操作
String sql1 = "INSERT INTO t_user VALUES (NULL,'zhaoliu',666)";
int lineNumber = JDBCUtils.exeUpdate(sql1, null);
System.out.println("lineNumber = " + lineNumber);
System.out.println(lineNumber > 0 ? "成功" : "失败");
//2.释放资源
JDBCUtils.close();
System.out.println("------------");
//测试查询所有的操作
String sql2 = "SELECT * FROM t_user";
ResultSet resu1 = JDBCUtils.exeQuery(sql2);
while (resu1.next()){
int uid = resu1.getInt("uid");
String uname = resu1.getString("uname");
String upass = resu1.getString("upass");
System.out.println(uid+","+uname+","+upass);
}
//2.释放资源
JDBCUtils.close();
}
}
第04节 打jar包
操作步骤1
操作步骤2
操作步骤3
操作步骤4