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注入成功

参考博文

posted @ 2020-07-24 09:38  你在谁的风景里a  阅读(271)  评论(0编辑  收藏  举报