zoukankan      html  css  js  c++  java
  • JavaWeb系列--JDBC

    总览图

    UXWbWT.png

    什么是JDBC

    JDBC的全称是Java数据库连接(Java Database Connectivity),它是一套用于执行SQL语句的Java API。应用程序可通过这套API连接到关系型数据库,并使用SQL语句来完成对数据库中数据的CRUD等操作。说白了就是用Java语言来操作数据库。

    JDBC原理

    早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动

    所以jdbc是不变的,但是驱动却有很多种。

    Demo

    准备数据

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for user
    -- ----------------------------
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user`  (
      `id` bigint(20) NOT NULL COMMENT '主键ID',
      `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
      `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
      `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
      `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
    
    -- ----------------------------
    -- Records of user
    -- ----------------------------
    INSERT INTO `user` VALUES (1, 'Jone', 18, 'test1@qq.com', '1230984');
    INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@qq.com', '5364363');
    INSERT INTO `user` VALUES (3, '吴芳吉', 28, 'test3@qq.com', '4564574');
    INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@qq.com', '4573452');
    INSERT INTO `user` VALUES (5, '李华', 24, 'test5@qq.com', '6546373');
    
    SET FOREIGN_KEY_CHECKS = 1;
    

    搭建Maven项目

    UqnojK.png

    JDBCDemo.java

    import org.junit.Test;
    
    import java.sql.*;
    
    /**
     * @Author: Luzy
     * @Date: 2020-07-22 11:04
     */
    public class JDBCDemo {
        private Connection conn = null;
        private Statement stat = null;
        private ResultSet rs = null;
    
        @Test
        public void query() {
            try {
            	//注册数据库驱动获取连接
                conn = JDBCUtil.getConnection();
                //获取传输器
                stat = conn.createStatement();
                //利用传输器发送SQL到数据库执行,并返回执行结果
                rs = stat.executeQuery("select * from user");
                //处理结果:将表中所有的记录输出在控制台
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    int age = rs.getInt("age");
                    String email = rs.getString("email");
                    System.out.println(id + " : " + name + " : " + age + " : " + email);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //释放资源
                JDBCUtil.close(conn, stat, rs);
            }
        }
    
    }
    
    

    JDBCUtil.java

    import java.sql.*;
    
    /**
     * @Author: Luzy
     * @Date: 2020-07-23 9:10
     * @Description: jdbc工具类
     */
    public class JDBCUtil {
        private static final String url = "jdbc:mysql://ip:port/user";
        private static final String name = "com.mysql.jdbc.Driver";
        private static final String username = "root";
        private static final String password = "123456";
    
    
        /**
         * 注册数据库驱动,获取连接
         **/
        public static Connection getConnection() throws ClassNotFoundException, SQLException {
            //1.注册数据库驱动
            Class.forName(name);
            //2.获取数据库连接
            return DriverManager.getConnection(url, username, password);
        }
    
        /**
         * 释放资源
         **/
        public static void close(Connection conn, Statement stat, ResultSet rs) {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    rs = null;
                }
            }
            if (stat != null) {
                try {
                    stat.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    stat = null;
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    conn = null;
                }
            }
        }
    }
    

    运行结果

    UqRxTf.png

    JDBC对象介绍

    DriverManager

    该类管理数据库驱动程序

    ​ 在JDBCUtil.java代码中可能出现的两种异常:

            //1.注册数据库驱动
            Class.forName(name);
            //2.获取数据库连接
            return DriverManager.getConnection(url, username, password);
    
    • ClassNotFoundException
      • 没有给出mysql的jar包
      • 类名称打错了,查看类名是不是com.mysql.jdbc.Driver
    • SQLException
      • 出现这个异常就是三个参数的问题,往往username和password一般不是出错,所以需要认真查看url是否打错

    Connection

    管理数据库建立的连接

    Connection最为重要的方法就是获取Statement:Statement stmt = con.createStatement();

    Statement

    负责将要执行的sql体局提交到数据库

    Statement最为重要的方法是:

    • int executeUpdate(String sql):

      执行更新操作,即执行insert、update、delete语句,其实这个方法也可以执行create table、alter table,以及drop table等语句,但我们很少会使用JDBC来执行这些语句

    • ResultSet executeQuery(String sql)

      执行查询操作,执行查询操作会返回ResultSet,即结果集

    • boolean execute()

      Statement还有一个boolean execute()方法,这个方法可以用来执行增、删、改、查所有SQL语句。该方法返回的是boolean类型,表示SQL语句是否有结果集!

    如果使用execute()方法执行的是更新语句,那么还要调用int getUpdateCount()来获取insertupdatedelete语句所影响的行数。

    如果使用execute()方法执行的是查询语句,那么还要调用ResultSet getResultSet()来获取select语句的查询结果。

    ResultSet

    执行sql查询语句返回的结果集

    ResultSet主要用于存储结果集,可以通过next()方法由前向后逐个获取结果集中的数据,如果想获取结果集中任意位置的数据,则需要在创建Statement对象时,设置两个ResultSet定义的常量,具体设置方式如下:

    Statement st = conn.createStatement(
     ResultSet.TYPE_SCROLL_INSENITIVE, 
     ResultSet.CONCUR_READ_ONLY
    );
    ResultSet rs = st.excuteQuery(sql);
    

    第一个参数:

    • ResultSet.TYPE_FORWARD_ONLY:不滚动结果集
    • ResultSet.TYPE_SCROLL_INSENSITIVE:滚动结果集,但结果集数据不会再跟随数据库而变化
    • ResultSet.TYPE_SCROLL_SENSITIVE:滚动结果集,但结果集数据会再跟随数据库而变化

    第二个参数:

    • CONCUR_READ_ONLY:结果集是只读的,不能通过修改结果集而反向影响数据库
    • CONCUR_UPDATABLE:结果集是可更新的,对结果集的更新可以反向影响数据库

    如果结果集是不可滚动的,那么只能使用next()方法来移动游标,而beforeFirst()、afterLast()、first()、last()、previous()、relative()方法都不能使用,如果想使用滚动的结果集,我们应该选择TYPE_SCROLL_INSENSITIVE!其实很少有数据库驱动会支持TYPE_SCROLL_SENSITIVE的特性!通常我们也不需要查询到的结果集再受到数据库变化的影响。通常可更新结果集这一“高级特性”我们也是不需要的!

    可以通过next()方法使ResultSet的游标向下移动,当游标移动到你需要的行时,就需要来获取该行的数据了,ResultSet提供了一系列的获取列数据的方法:

    ULHksU.png

    上面方法中,参数columnIndex表示列的索引,列索引从1开始,而不是0,这第一点与数组不同。如果你清楚当前列的数据类型,那么可以使用getInt()之类的方法来获取,如果你不清楚列的类型,那么你应该使用getObject()方法来获取

    ResultSet还提供了一套通过列名称来获取列数据的方法:

    ULHFMT.png

    PreparedStatement

    PreparedStatement对象可以对SQL语句进行预编译,预编译的信息会存储在该对象中。当相同的SQL语句再次执行时,程序会使用PreparedStatement对象中的数据,而不需要对SQL语句再次编译去查询数据库,这样就大大的提高了数据的访问效率。

    防止sql注入 、提高代码可读性可维护性、提高程序执行的效率

    SQL注入攻击

    由于后台的SQL语句是拼接而来的。其中的参数是由用户提交的,如果用户在提交参数时,在其中掺杂了一些SQL关键字或者特殊符号,就可能会导致SQL语句的语意发生变化。从而执行一些意外的操作。

    模拟登陆示例

    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.Statement;
    import java.util.Scanner;
    
    /**
     * @Author: Luzy
     * @Date: 2020-07-23 16:40
     */
    public class LoginTest1 {
    
        public static void main(String[] args) {
             Connection conn = null;
             Statement stat = null;
             ResultSet rs = null;
            Scanner scanner = new Scanner(System.in);
            System.out.println("please login first");
            System.out.println("please enter username:");
            String name = scanner.nextLine();
            System.out.println("please enter password:");
            String password = scanner.nextLine();
            try {
                conn = JDBCUtil.getConnection();
                stat = conn.createStatement();
                String sql = "SELECT * FROM user WHERE name= '"+name+"' and password = '"+password+"' ";
                System.out.println(sql);
                 rs = stat.executeQuery(sql);
                if (rs.next()) {
                    System.out.println("login success");
                }else{
                    System.out.println("login fail");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //6.释放资源
                JDBCUtil.close(conn, stat, rs);
            }
        }
    }
    
    

    运行之后控制台输入

    UXRYvQ.png

    UXRJgg.png

    如图所示,便是SQL注入的结果

    使用PreparedStatement防止SQL注入

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.Scanner;
    
    /**
     * @Author: Luzy
     * @Date: 2020-07-23 16:40
     */
    public class LoginTest2 {
    
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement preparedStatement = null;
            ResultSet rs = null;
            Scanner scanner = new Scanner(System.in);
            System.out.println("please login first");
            System.out.println("please enter username:");
            String name = scanner.nextLine();
            System.out.println("please enter password:");
            String password = scanner.nextLine();
            try {
                conn = JDBCUtil.getConnection();
                String sql = " SELECT * FROM user WHERE name = ? and password = ? ";
                preparedStatement = conn.prepareStatement(sql);
                preparedStatement.setString(1, name);
                preparedStatement.setString(2, password);
                rs = preparedStatement.executeQuery();
                if (rs.next()) {
                    System.out.println("login success");
                } else {
                    System.out.println("login fail");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //6.释放资源
                JDBCUtil.close(conn, preparedStatement, rs);
            }
        }
    }
    

    UXWUzD.png

    如图防止SQL注入成功

    参考博文

  • 相关阅读:
    Bootstrap学习
    Bootstrap学习
    Windows下Apache+Django+mod_wsgi的static和media问题处理
    Windows编译安装mod_wsgi,配合使用Django+Apahce
    Bootstrap学习
    Chapter 21_4 捕获
    Chapter 21_3 模式
    新发现的一些C函数
    Chapter 21_2 模式匹配函数
    Chapter 21_1 字符串函数
  • 原文地址:https://www.cnblogs.com/sanye613/p/13370210.html
Copyright © 2011-2022 走看看