JDBC基础--[JDBC概述,JDBC的搭建,PreparedStatement和Statement执行SQL语句,结果集处理]

Source


JDBC概述


Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。

由一组用Java语言编写的类和接口组成

对于数据库的操作无非就是增删改查的操作;java语言的开发者并不会直接进行数据库的操作;而是定义了一些接口,最终是由数据库的开发商去完成这些接口的具体实现.
所以也就可以访问不同的数据库.

JDBC API主要位于JDK中的java.sql包中(之后扩展的内容位于javax.sql包中),主要包括(斜体代表接口,需驱动程序提供者来具体实现):
DriverManager类:负责加载(管理)各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection).
Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。
Connection接口 :与特定数据库的连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。
Statement接口 :用以执行SQL查询和更新,向数据库发送SQL语句(针对静态SQL语句和单次执行)。
PreparedStatement接口:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。
CallableStatement:用以调用数据库中的存储过程。
ResultSet接口:接收查询结果;表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
SQLException:代表在数据库连接的建立和关闭和SQL语句的执行过程中发生了例外情况(即错误)。


JDBC搭建


首先;在项目栏新建文件夹;(一般命名为lib有library的意思)

在这里插入图片描述

将需要的数据库实现类的jar包复制到该文件夹;

在这里插入图片描述

1.导入jar包;
首先点击 File -> Project Structure(快捷键 Ctrl + Alt + Shift + s);找到左侧的选项Libraries;点击加号;点击Java

在这里插入图片描述

然后找到对应的项目存放地址;找到jar包,选择导入.

在这里插入图片描述

在这里插入图片描述


2.加载驱动;(在jdbc8之后,可以直接不写;会自动地加载)

这需要初始化驱动程序,这样就可以打开与数据库的通信信道。

方式1:

//java的反射机制,动态加载类;
Class.forName("com.mysql.cj.jdbc.Driver");

方式2:

DriverManager.registerDriver(new Driver());

在jdbc8之后,这些操作可以不用;会自动地加载驱动.


3.建立与数据库的连接

这里需要用到的是DriverManager类中的getConnection方法;代表一个物理连接的数据库.里面有三个参数;
url:ip和端口以及过滤字符编码,时区;
user:数据库用户名;
password:数据库密码.


返回的是Connection的对象,实际上是Connection实现类的对象;
//url的参数
jdbc:mysql://127.0.0.1(ip地址):3306(端口号)/(数据库名)?characterEncoding=utf8(字符编码过滤)&useSSL=false&serverTimezone=UTC(时区)

例如:

//建立与数据库studentworld_db的连接;
        String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
        try {
    
      
            //这里的Connection对象作为与数据库连接的通道;
            Connection connection=DriverManager.getConnection(url,"root","123456");
        } catch (SQLException throwables) {
    
      
            throwables.printStackTrace();
        }

若运行无异常;则说明连接成功.注意,一定要准确数据库名,用户名,密码


4.向数据库发送SQL语句 在发送结束后,要记得关闭通道


PreparedStatement和Statement


使用Statement接口


首先是创建对象Statement st=connection.createStatement( );
使用executeUpdate方法执行SQL语句

这个Statement接口的executeUpdate方法有int类型的返回值;
发送DDL语句时,返回的是数字0;
发送DML语句(增,删,改)时,返回的是操作的行数.

例如;向数据库studentworld_db的数据表t_course下插入数据;(使用DML语句)

public class Demo01 {
    
      
    public static void main(String[] args) {
    
      
        //建立与数据库的连接;
        String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
        try {
    
      
            //与数据库连接的通道;
            Connection connection=DriverManager.getConnection(url,"root","123456");
            //向数据库发送SQL语句;
            Statement st=connection.createStatement();
            st.executeUpdate("insert into t_course(cou_id,Cname) values(108,'数分'),(109,'英语')");
            //关闭数据库连接通道;
            st.close();
            connection.close();
        } catch (SQLException throwables) {
    
      
            throwables.printStackTrace();
        }
    }
}

返回值为2;即插入了两行数据.

在这里插入图片描述

查看数据库这边,已经成功插入数据;

在这里插入图片描述


模拟数据库发送数据;
例如:向数据库studentworld_db的学生表t_student中传入数据;

使用Statement接口
注意要用连接符号把数据一个一个连接起来;字符串类型的用单引号,双引号,双加号包裹起来;数值类型的用双引号,双加号即可;

public class Demo02 {
    
      
    public static void main(String[] args) {
    
      
        //建立与数据库的连接;
        String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
        try {
    
      
            //与数据库连接的通道;
            Connection connection= DriverManager.getConnection(url,"root","123456");
            //向数据库发送SQL语句;
            Statement st=connection.createStatement();
            //模拟向数据库输入动态数据;
            //以数据库的t_student表为例;
            String name="大话王";
            String sex="男";
            String birthday="2018-5-7";
            String phone="12345665412";
            int gra_id=1805;
            java.util.Date date=new java.util.Date();
            java.sql.Timestamp res_time= new java.sql.Timestamp(date.getTime());
            int score=82;
            //向数据库发送数据;
            //注意要用连接符号把数据一个一个连接起来;;字符串类型的用单引号,双引号,双加号包裹起来;数值类型的用双引号,双加号即可;
            st.executeUpdate("INSERT INTO t_student(NAME,sex,birthday,phone,gra_id,res_time,score)" +
                    "VALUES('"+name+"','"+sex+"','"+birthday+"','"+phone+"',"+gra_id+",'"+res_time+"',"+score+")");
            //关闭数据库连接通道;
            st.close();
            connection.close();
        } catch (SQLException throwables) {
    
      
            throwables.printStackTrace();
        }
    }
}

在这里插入图片描述


使用PrepareStatement接口执行sql语句 (简单且安全性高这个接口还继承了Statement)


首先会预先地将SQL语句编译到PrepareStatement对象中;在sql语句中参数位置使用占位符(即?),一个占位符表示一个值;然后再向SQL语句中的占位符赋值;

在向占位符赋值时;可以使用set方法;来为各种参数赋值;

这些方法的参数parameterIndex 为int类型的,即占位符的位置(第几个 ?),
而且在传入数据时,可以直接将数据一个一个地赋值;不像Statement那样;需要注意使用符号分隔数据;

在这里插入图片描述

对占位符赋值结束后,还需要执行PrepareStatement接口的executeUpdate方法;

这个方法也是返回int类型的值;
若执行DDL语句则返回数字0;
若执行DML语句则返回操作的数据行数.


例如:使用PrepareStatement接口执行sql语句向数据库studentworld_db的数据表t_student中插入数据

public class Demo03 {
    
      
    public static void main(String[] args) {
    
      
        //建立与数据库的连接;
        String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
        try {
    
      
            //与数据库连接的通道;
            Connection connection= DriverManager.getConnection(url,"root","123456");
            //模拟向数据库输入动态数据;
            //以数据库的t_student表为例;
            String name="梅尔萨斯";
            String sex="女";
            String birthday="2014-1-6";
            String phone="12342365412";
            int gra_id=1805;
            Date date=new Date();
            java.sql.Timestamp res_time= new java.sql.Timestamp(date.getTime());
            int score=75;
            //向数据库发送SQL语句;这里会使用 ?(占位符);来表示一个值的位置;
            String sql="INSERT INTO t_student(NAME,sex,birthday,phone,gra_id,res_time,score)VALUES(?,?,?,?,?,?,?)";
            //预先将SQL语句编译到对象中;
            PreparedStatement pps=connection.prepareStatement(sql);
            //向占位符赋值;(注意数据类型);
            pps.setString(1,name);
            pps.setString(2,sex);
            pps.setObject(3,birthday);
            pps.setString(4,phone);
            pps.setInt(5,gra_id);
            pps.setTimestamp(6,res_time);
            pps.setInt(7,score);
            //执行方法executeUpdate;
            int res=pps.executeUpdate();
            System.out.println(res);
            //关闭数据库连接通道;
            pps.close();
            connection.close();
        } catch (SQLException throwables) {
    
      
            throwables.printStackTrace();
        }
    }
}

在这里插入图片描述


Statement和PreparedStatement的区别


Statement:

  • 静态的SQL语句执行;每当操作一次就向数据库发送一次;
  • 需要将变量以字符串的形式拼接;书写麻烦;代码的可读性以及维护性太差.
  • 不可以防止SQL注入;安全性低.

PreparedStatement

  • 预先地将SQL语句编译到PreparedStatement对象中;可重复使用;效率高
  • 使用的是set方法为写好的占位符进行赋值;书写方便;代码的可读性以及维护性好.
  • 可防止SQL注入,安全性高.

SQL注入:

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
原理:
SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法。简单的说就是在post/getweb表单、输入域名或页面请求的查询字符串中插入SQL命令,最终使web服务器执行恶意命令的过程。可以通过一个例子简单说明SQL注入攻击。假设某网站页面显示时URL为http://www.example.com?test=123,此时URL实际向服务器传递了值为123的变量test,这表明当前页面是对数据库进行动态查询的结果。由此,我们可以在URL中插入恶意的SQL语句并进行执行。另外,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。

模拟SQL注入:
(1)使用Statement不能防止SQL注入:
例如想要通过危险的SQL语句条件删除数据表t_fortnitepe中的person角色"战术咸鱼"那一行;但是整个数据表都被删除数据了.

public class Demo04 {
    
      
    public static void main(String[] args) {
    
      
        //建立与数据库的连接;
        String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
        try {
    
      
            //与数据库连接的通道;
            Connection connection= DriverManager.getConnection(url,"root","123456");
           //模拟暴力的SQL注入,直接将数据值拼接到字符串中,将1==1 也当做person值了;
            String person="'战术咸鱼' or 1=1";
            //向数据库发送数据;
            Statement st=connection.createStatement();
            int res= st.executeUpdate("DELETE FROM t_fortnitepe WHERE person="+person);
            System.out.println(res);//获取被删除的行数;
            //关闭数据库连接通道;
            st.close();
            connection.close();
        } catch (SQLException throwables) {
    
      
            throwables.printStackTrace();
        }
    }
}

发现删除了10行数据,数据表被清空了.

在这里插入图片描述

在这里插入图片描述

把数据表的数据重新备份回去;
(2)PreparedStatement可以防止SQL注入:
例如想要通过带有危险的SQL语句条件删除数据表t_fortnitepe中的person角色"战术咸鱼"那一行;数据表的任何数据不会被删除.
在数据赋值给占位符时会进行预先处理;也就不会将危险的数据执行了;

public class Demo05 {
    
      
    public static void main(String[] args) {
    
      
        //建立与数据库的连接;
        String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
        try {
    
      
            //与数据库连接的通道;
            Connection connection= DriverManager.getConnection(url,"root","123456");
            //定义删除的条件;
            String person="'战术咸鱼' or 1=1";
            //预先将SQL语句编译到对象中;这里会使用 ?(占位符);来表示一个值的位置;
            PreparedStatement pps=connection.prepareStatement("DELETE FROM t_fortnitepe WHERE person=?");
            //向占位符赋值;
            pps.setString(1,person);
            //执行方法executeUpdate;
            int res=pps.executeUpdate();
            System.out.println(res);//获取被删除的行数;
            //关闭数据库连接通道;
            pps.close();
            connection.close();
        } catch (SQLException throwables) {
    
      
            throwables.printStackTrace();
        }
    }
}

发现删除0行数据;数据表的数据都在

在这里插入图片描述

在这里插入图片描述



对于结果集的处理


注意使用方法executeQuery执行查询语句;
返回一个ResultSet 集合

ResultSet res=executeQuery( );

可以将ResultSet对象获取到;然后转化到Java中自定义的类的对象;一个对象就可以存储一行数据记录.若有多行结果集数据,可以将所有对象存到一个集合中.

  • ResultSet中的next( )方法;可以判断查询出的结果集中是否还有数据;如果有就返回true;指向下一行数据.
  • ResultSet中的get***( )方法可以获取到查询的结果集的数据.

例如,查询1801班的学生;

public class Demo06 {
    
      
    public static void main(String[] args) {
    
      
        //建立与数据库的连接;
        String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
        try {
    
      
            //与数据库连接的通道;
            Connection connection= DriverManager.getConnection(url,"root","123456");
            //定义查询的条件;
            int gra_id=1801;
            //这里会使用 ?(占位符);来表示一个值的位置;
            String sql="SELECT stu_id,name,sex,phone,gra_id,res_time,score FROM t_student WHERE gra_id=?";
            //预先将SQL语句编译到对象中;
            PreparedStatement pps=connection.prepareStatement(sql);
            //向占位符赋值;
            pps.setInt(1,gra_id);
            //使用方法executeQuery执行查询语句;
            ResultSet res=pps.executeQuery();
            //可以将数据表的数据装到自定义的集合中;
            List<Student> studentList=new ArrayList<>();
            //使用next方法判断结果集中是否还有数据;有就返回true,继续查下一行数据;
            while(res.next()){
    
      
                //每查询到一行数据就新建一个对象;
                Student student=new Student();
                //使用get方法获取结果集中的数据,然后存入Student类的对象中;
                student.setStuID(res.getInt("stu_id"));
                student.setName(res.getString("name"));
                student.setSex(res.getString("sex"));
                student.setPhone(res.getString("phone"));
                student.setGraID(res.getInt("gra_id"));
                student.setResTime(res.getTimestamp("res_time"));
                student.setScore(res.getInt("score"));
                //将对象中的数据存入集合;
                studentList.add(student);
            }
            //遍历输出集合studentlist;
            for(Student list:studentList){
    
      
                System.out.println(list);
            }
            //关闭数据库连接通道;
            res.close();
            pps.close();
            connection.close();
        } catch (SQLException throwables) {
    
      
            throwables.printStackTrace();
        }
    }
}

自定义的学生类;

public class Student {
    
      
    //属性;
    private  int stuID;
    private  String name;
    private  String sex;

    private  String phone;
    private  int graID;
    private  Date resTime;
    private  int  score;
    //get,set方法;
    public int getStuID() {
    
      
        return stuID;
    }

    public void setStuID(int stuID) {
    
      
        this.stuID = stuID;
    }

    public String getName() {
    
      
        return name;
    }

    public void setName(String name) {
    
      
        this.name = name;
    }

    public String getSex() {
    
      
        return sex;
    }

    public void setSex(String sex) {
    
      
        this.sex = sex;
    }


    public String getPhone() {
    
      
        return phone;
    }

    public void setPhone(String phone) {
    
      
        this.phone = phone;
    }

    public int getGraID() {
    
      
        return graID;
    }

    public void setGraID(int graID) {
    
      
        this.graID = graID;
    }

    public Date getResTime() {
    
      
        return resTime;
    }

    public void setResTime(Date resTime) {
    
      
        this.resTime = resTime;
    }

    public int getScore() {
    
      
        return score;
    }

    public void setScore(int score) {
    
      
        this.score = score;
    }
    //toString方法;

    @Override
    public String toString() {
    
      
        return "Student{" +
                "stuID=" + stuID +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", phone='" + phone + '\'' +
                ", graID=" + graID +
                ", resTime=" + resTime +
                ", score=" + score +
                '}';
    }
}

结果:

在这里插入图片描述