课程笔记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
在这里插入图片描述

今日总结:

今日学习主要是:jdbc,以及如何打包jdbc为jar包

所以啥是jdbc呢?

简单来说就是java连接数据库来进行java程序的操作,将操作后的数据存储在数据库中

  1. 导入jar包
  2. 找到驱动 Driver
  3. 获取连接 Connection
  4. 获取操作 Statement
  5. 查询结果 ResultSet
  6. 释放资源

多看看jdbc工具类这一小节,要看懂,不懂本博主有注释,多看,最主要理解,别背!!!

最后说一下,这一天博主的心路历程

  今日博主以身试“法”,改动了mysql的my.ini文件,也就是mysql的配置文件,然后不知道删除了啥,(大概搞了一上午,数据库没有删除干净,重复删除安装mysql数据库)
  mysql服务启动不了,删除,重装后,发现以前删除的mysql版本没有删除干净(本博主的mysql版本8.0->5.7->5.5),
  最后感谢川哥帮我弄好了。
  浪客川(这个是他csdn博客名->java大佬级别)。 
  
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐