第21章 JDBC

作者: yangsg | 来源:发表于2019-04-15 15:04 被阅读480次

    1. 基础知识

    Java连接数据库,是通过操作系统完成相应的连接,这种连接方式被称为桥连接。以操作系统为桥梁(比如在windows系统中利用数据源ODBC),连接Java程序和关系型数据库。局限性:应用程序不可跨平台。

    JDBC推出后,可以利用Java程序直接访问关系型数据库。

    • Java提供了访问数据库的一系列的接口声明
    • 各个数据库厂商提供这些接口的实现类,通过这些实现类可以访问对应数据库(驱动)
    • 通过连接发送SQL语句,数据库给出执行结果

    访问网络中某个数据库,需要以下信息

    • IP地址
    • 数据库的端口号
    • 数据库的账号和密码
    • 数据库名
      这些内容组成的“连接字符串”

    2. 连接MySQL数据库

    2.1 前置准备
    • 打开MySQL数据库服务
    • 在Java工程中,导入MySQL的驱动,让其参与编译
      (1)将jar包复制工程的任意位置,建议新建一个lib的文件夹
      (2)让jar包参与编译


      右键工程,点击properties

    点击“Java Build Path”,打开“Libraries”选项卡,点击右侧的“Add JARs”


    操作示范

    找到,并选择刚刚复制的jar包文件,点击OK


    找到并选中

    点击OK后,可以看到mysql的驱动已经参与到了编译环境中


    操作示范
    2.2 查询的示例

    创建连接各个数据库的连接的代码,格式都是固定的
    基本思路:

    1. 通过Class.forName("")加载驱动类
    //1.加载驱动
    try {
        Class.forName("com.mysql.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    
    1. 声明连接字符串和账号密码
    //2.配置连接字符串
    String url ="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8";
    String username = "root";
    String password = "root";
    
    1. 利用DriverManager创建数据库连接(Connection)
    //3.创建数据库连接
    Connection conn = null;
    try {
        conn = DriverManager.getConnection(url,username,password);
    } catch (SQLException e) {
        e.printStackTrace();
    }   
    

    4.声明SQL语句

    //4.声明SQL语句
    String sql = "select empno,ename,hiredate from emp";
    

    5.加载和执行SQL语句

    //5.加载SQL语句
    Statement s = conn.createStatement();
    //6.执行SQL语句,得到查询结果(结果集对象)
    ResultSet rs = s.executeQuery(sql);
    

    6.遍历结果集

    while(rs.next()) {
    //每执行一轮循环,遍历出查询结果的一行数据
        int empno = rs.getInt("empno"); //取得整数类型的数据
        String ename = rs.getString("ename"); //取得文本类型的数据
        Date birthday = rs.getDate("hiredate"); //取得日期类型的数据
        System.out.println(empno+"\t"+ename+"\t"+birthday);
    }
    

    7.关闭数据库连接

    //8.关闭数据库连接
    try {
        conn.close();
    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    

    完整示例

    public class Test {
    
        public static void main(String[] args) {
            //1.加载驱动
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //2.配置连接字符串
            String url ="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8";
            String username = "root";
            String password = "root";
            
            //3.创建数据库连接
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(url,username,password);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }   
            //4.声明SQL语句
            String sql = "select empno,ename,hiredate from emp";    
            try {
                //5.加载SQL语句
                Statement s = conn.createStatement();
                //6.执行SQL语句,得到查询结果(结果集对象)
                ResultSet rs = s.executeQuery(sql);
                //7.遍历结果集
                while(rs.next()) {
                    //每执行一轮循环,遍历出查询结果的一行数据
                    int empno = rs.getInt("empno");
                    String ename = rs.getString("ename");
                    Date birthday = rs.getDate("hiredate");
                    
                    System.out.println(empno+"\t"+ename+"\t"+birthday);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                //8.关闭数据库连接
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    2.3 DML的示例

    DML操作不需要遍历结果集,使用executeUpdate(sql)方法来完成DML操作,其返回结果为int类型,表示DML操作影响的数据行数。

    public class Test2 {
    
        public static void main(String[] args) {
            // 1.加载驱动
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 2.配置连接字符串
            String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8";
            String username = "root";
            String password = "root";
    
            // 3.创建数据库连接
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(url, username, password);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            // 4.声明SQL语句
            String sql = "insert into emp values (1015,'HAHA','CLERK',null,null,5000,0,10)";
            try {
                // 5.加载SQL语句
                Statement s = conn.createStatement();
                // 6.执行SQL语句,得到的结果表示此次DML操作影响了a行数据
                int a = s.executeUpdate(sql);//增删改
                System.out.println(a);
                
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                // 8.关闭数据库连接
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    
    2.4 预加载的示例

    因为用户可以输入,输入了一个SQL片段后可能对原有的SQL语句逻辑造成破坏,从而达到非法的目的
    数据库表

    image.png
    登录程序
    public class Test1 {
    
        public static void main(String[] args) {
            //1.加载驱动
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //2.连接字符串
            String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8";
            String username = "root";
            String password = "root";
            
            //3.创建数据库连接
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(url, username, password);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            //4.声明SQL语句 
            System.out.println("请输入账号:");
            Scanner sc1 = new Scanner(System.in);
            String lname = sc1.nextLine();
            
            System.out.println("请输入密码:");
            Scanner sc2 = new Scanner(System.in);
            String lpass = sc2.nextLine();
            
            String sql = "select * from haha where lname = '"+lname+"' and lpass = '"+lpass+"'";
            System.out.println(sql);
            
            //5.加载和执行SQL语句
            try {
                Statement s = conn.createStatement();
                ResultSet rs = s.executeQuery(sql);
                if(rs.next()) {
                    System.out.println("登录成功");
                }else {
                    System.out.println("登录失败");
                }       
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }   
        }
    }
    

    在输入admin/123或者haha/456的情况可以正常登录
    如果输入错误的账号和密码是无法正确登录
    但:如果用户输入的账号是:admin,密码是:' or 1=1 and lname='admin
    执行的SQL语句变成了

    select * from haha where lname = 'admin' and lpass = '' or 1=1 and lname='admin'
    

    逻辑被破坏,也能够正常登录成功,这种情况就是SQL注入
    SQL注入带来了很大的系统风险,也是经常被黑产所利用,在编写程序需要控制SQL注入的问题。

    利用预加载的statement来解决SQL注入问题。
    预加载的statement的特点(与Statement相比)

    • 更安全:防止SQL注入
    • 更高效:在SQL服务器提供了预先的编译,对于同种结构的SQL语句执行效率更高

    将要输入至SQL的参数,使用英文"?"替代
    将刚才有问题的程序更换如下

    public class Test1 {
    
        public static void main(String[] args) {
            //1.加载驱动
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //2.连接字符串
            String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8";
            String username = "root";
            String password = "root";
            
            //3.创建数据库连接
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(url, username, password);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            //4.声明SQL语句 
            System.out.println("请输入账号:");
            Scanner sc1 = new Scanner(System.in);
            String lname = sc1.nextLine();
            
            System.out.println("请输入密码:");
            Scanner sc2 = new Scanner(System.in);
            String lpass = sc2.nextLine();
            
            String sql = "select * from haha where lname = ? and lpass = ?";
            System.out.println(sql);
            
            //5.加载和执行SQL语句
            try {
                PreparedStatement pst = conn.prepareStatement(sql);
                //参数绑定:根据实际的数据类型进行设置
                pst.setString(1, lname);
                pst.setString(2, lpass);
                
                ResultSet rs = pst.executeQuery();
                if(rs.next()) {
                    System.out.println("登录成功");
                }else {
                    System.out.println("登录失败");
                }       
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }   
        }
    }
    

    建议所有的JDBC统一使用PreparedStatement完成

    2.5 批处理的示例(了解)

    利用批处理,对一批SQL语句统一的执行,主要用于大量数据的新增操作中,节省时间。

    Statement st = conn.createStatement();
    for(int i = 0; i < 99; i++) {
        st.addBatch(sql); //将sql添加到批处理中
    }
    st.executeBatch(); //执行批次
    

    优势:执行的速度较快
    劣势:执行同样或者类似SQL

    相关文章

      网友评论

        本文标题:第21章 JDBC

        本文链接:https://www.haomeiwen.com/subject/pxeuwqtx.html