使用之前需要在IDEA上连接mysql,然后导入jar包mysql-connector-java-8.0.20.jar,执行的基本步骤为:

  1. 注册驱动
  2. 获取连接对象
  3. 创建SQL语句
  4. 创建执行SQL语句的Statement对象
  5. 执行SQL语句
  6. 释放资源

6.0版本以上不需要手动加载驱动,可以直接使用

//1. 通过DriverManager来获得数据库连接
try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
     //2. 创建一个用于执行SQL的Statement对象
     Statement statement = connection.createStatement()){   //注意前两步都放在try()中,因为在最后需要释放资源!
    //3. 执行SQL语句,并得到结果集
    ResultSet set = statement.executeQuery("select * from 表名");
    //4. 查看结果
    while (set.next()){
        ...
    }
}catch (SQLException e){
    e.printStackTrace();
}
//5. 释放资源,try-with-resource语法会自动帮助我们close

URL表示所连接数据库的地址。这里的值是jdbc:mysql://localhost:3306/study,表示选择本地数据库系统的study数据库,选择其他数据库时就将study改为其它的数据库。

DriverManager

此类是驱动管理者的意思,通过该类来加载驱动Driver,通过语句Class.forName("com.mysql.cj.jdbc.Driver")来将Driver类加载进内存。

上述语句执行时,JVM会把该类加载,然后执行一次类内部的静态代码块:

// static code block in Class Driver
static {
    try {
        DriverManager.registerDriver(new Driver());
    } catch (SQLException var1) {
        throw new RuntimeException("Can't register driver!");
    }
}

执行该代码块会通过DriverManager.registerDriver(new Driver());来注册驱动。DriverManager注册时会将该Driver封装成DriverInfo并加入到DriverManager类的registeredDrivers中,这是一个线程安全的列表,表示已经加载的驱动。

    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    public static void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if (driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }
Connection对象

通过Mannager对象来获取数据库的连接对象Connection,需要提供三个参数url,user,password。

    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }
Statement对象和ResultSet对象

Statement是数据库语句对象,通过数据库连接对象来创建一个Statement对象,Statement对象用来执行数据库语句,并将执行结果返回到一个ResultSet中,本质是一个集合。

Statement statement = connection.createStatement();   //

ResultSet set = statement.executeQuery(String sql);       // 该方法执行sql语句

// Resultset的使用方法   返回的查询结果是一张表,set里的指针一开始指向表头
while (set.next()) {      // 每执行一次next函数,指针就会往下移动一行。
    // get函数有两种参数类型:int和string,分别表示所选列的下标和列名
    System.out.println(set.getInt(1));          // 选第一列,因第一列为整数,因此使用getInt
    System.out.println(set.getString("name"));    // 选择列名为 name 的那一列
    System.out.println(set.getString(3));        // 选择第三列,为字符串,因此用getString
}

// 批处理,一次想要执行多条数据库语句时,将多条语句合成为一条去执行会更快,数据库只用执行一次。
statement.addBatch("insert into user values ('f', 1234)");
statement.addBatch("insert into user values ('e', 1234)");   //添加每一条批处理语句
statement.executeBatch();   //一起执行

使用Statement对象执行时有个非常严重的问题那就是这里的SQL语句都是静态的,可能会导致SQL注入,出现安全问题。使用PreparedStatement对象可以解决上述问题,该对象是预编译SQL语句的对象,继承自Statement。

SQL注入

演示:


-- 先在数据库中新建user表
CREATE TABLE user(
    username varchar(255) NOT NULL,
    password varchar(255) NOT NULL,
    PRIMARY KEY(username, password)
    );

INSERT INTO user values(
    'Test', '123');

// 然后使用JDBC尝试连接数据库并登录为user
try( Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
    Statement statement = connection.createStatement();
    Scanner scanner = new Scanner(System.in);
    ) {

    ResultSet set = statement.executeQuery("SELECT * FROM user WHERE username='" + scanner.next() + "' and " + "password='" + scanner.next()+"';");
    while (set.next()) {
        System.out.println(set.getString("username"));
        System.out.println("登录成功");
    }
}catch (Exception e) {
    e.printStackTrace();
}

正确输入用户名和密码时会正确登录,但是当把密码改为111' or 1=1; -- 时,发现也可以正确登录,因为这里的特殊输入“骗过”了数据库系统。这就是SQL注入。

解决SQL注入的方法可以有以下几种:

  • 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。
  • 永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
  • 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
  • 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
  • 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
  • sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻击等。
使用PreparedStatement

一个PreparedStatement对象是预编译SQL语句的对象,即在创建时就要把sql语句写好。编写SQL语句时,不使用字符串拼接,而是使用?占位符来代替变量。然后在真正执行之前再给占位符赋值。

PrepareStatement对象在创建时就要指明要执行的SQL语句类型。然后用?代替变量,并用setString(int Index, String)方法来给占位符赋值,最后执行。


try( Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
             PreparedStatement statement = connection.prepareStatement("SELECT * FROM user WHERE username=? AND password=?");
             Scanner scanner = new Scanner(System.in);
                ) {

            statement.setString(1, scanner.nextLine());
            statement.setString(2, scanner.nextLine());
            ResultSet set = statement.executeQuery();
            while (set.next()) {
                System.out.println(set.getString("username"));
                System.out.println("登录成功");
            }
        }catch (Exception e) {
            e.printStackTrace();
        }

再次测试发现原来的输入已经不能“骗过”数据库了。

JDBC管理事务

和数据库管理事务一样的。

try ( Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
              Statement statement = connection.createStatement();
                ) {
            connection.setAutoCommit(false);       // 把连接的自动提交关掉
            Savepoint sp = connection.setSavepoint();  // 设置回滚点
            statement.executeUpdate("INSERT INTO user VALUES('a', 123)");
            statement.executeUpdate("INSERT INTO user VALUES('b', 123)");
            statement.executeUpdate("INSERT INTO user VALUES('c', 123)");

            connection.rollback(sp);       // 回滚到回滚点
            connection.commit();         // 手动提交
        }catch (Exception e) {
            e.printStackTrace();
        }
分类: JDBC

0 条评论

发表评论

邮箱地址不会被公开。