JDBC编程技术

1. 层次结构图

Java应用程序
JDBC驱动程序
Oracle本地驱动
SQLServer本地驱动
MySQL本地驱动
Oracle
SQLServer
MySQL

2. JDBC编程技术基本步骤

  1. 将驱动程序导入工程,程序中加载驱动;
  2. 创建连接对象Connection;
  3. 在连接对象上创建命令对象(Statement、PreparedStatement、CallableStatement);
  4. 执行SQL语句。

注意:不同版本的驱动jar包,在连接代码中有稍微的差别。

3.在IDEA中连接数据库代码

3.1 连接MySQL

SQLHelper代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SQLHelper {
    
    

    //创建数据库连接、预处理和结果集对象
    private static Connection conn = null;
    private static PreparedStatement pre = null;
    private static ResultSet rs = null;
    
    //加载驱动
    static {
    
    
        String driver = "com.mysql.cj.jdbc.Driver";
        try {
    
    
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        }
    }
    
    /**
     * 建立连接
     */
    private static void buildConnection() {
    
    
        //数据库配置信息
        String url = "jdbc:mysql://localhost:3306/db_name?serverTimezone=GMT";
        String user = "root", password = "123456";
        try {
    
    
            conn = DriverManager.getConnection(url, user, password);
            System.out.println("数据库连接成功");
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
    
    /**
     * 执行查询SQL语句,返回查询结果集
     *
     * @param sql SQL语句
     * @return 结果集对象
     */
    public static ResultSet executeQuery(String sql) {
    
    
        buildConnection();
        try {
    
    
            pre = conn.prepareStatement(sql);
            rs = pre.executeQuery();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        //使用后需要在调用处手动关闭连接
        return rs;
    }

    /**
     * 执行更新语句
     *
     * @param sql 更新语句
     * @return 是否更新成功
     */
    public static boolean executeUpdate(String sql) {
    
    
        buildConnection();
        int r = 0;
        try {
    
    
            pre = conn.prepareStatement(sql);
            r = pre.executeUpdate();
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
        closeConnection();
        return r != 0;
    }

    /**
     * 执行单个语句,返回结果对象
     *
     * @param singleSql 单个SQL语句
     * @return 结果对象
     */
    public static Object executeSingleQuery(String singleSql) {
    
    
        buildConnection();
        Object obj = null;
        try {
    
    
            pre = conn.prepareStatement(singleSql);
            rs = pre.executeQuery();
            if (rs.next()) {
    
    
                obj = rs.getObject(1);
            }
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
        closeConnection();
        return obj;
    }

   /**
     * 关闭连接
     */
    public static void closeConnection() {
    
    
        try {
    
    
            if (conn != null && !conn.isClosed()) {
    
    
                conn.close();
            }
            System.out.println("数据库连接已关闭");
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
}

注意:上述连接代码使用的为mysql-connector-java-8.0.15.jar版本的驱动jar包。如果使用的是版本6以后的jar包,则不需要更改上述代码任何信息,只需把url中的db_name改成自己的数据库名称、userpassword也做出相应改变即可。但如果使用的为版本5以后版本6以前的jar包,那么上述代码中要更改两处信息。如下:

String driver = "com.mysql.cj.jdbc.Driver";
更改为:
String driver = "com.mysql.jdbc.Driver";

String url = "jdbc:mysql://localhost:3306/db_name?serverTimezone=GMT";
更改为:
String url = "jdbc:mysql://localhost:3306/db_name";

3.2 连接SQL Server

import java.sql.*;

public class SQLHelper {
    
    
    
    //创建数据库连接、预处理和结果集对象
    private static Connection conn = null;
    private static PreparedStatement pre=null;
    private static ResultSet rs=null;

    //静态代码块,用于加载驱动和建立连接
    static {
    
    
        String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
        String url = "jdbc:sqlserver://127.0.0.1:1433;database=db_name";
        String user = "geshu", password = "123456";
        try {
    
    
            //加载驱动
            Class.forName(driver);
            //建立连接
            long startTime = System.currentTimeMillis();
            conn = DriverManager.getConnection(url, user, password);
            long endTime = System.currentTimeMillis();
            System.out.println("建立连接耗时:" + (endTime - startTime) + " ms");
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
    }

    /**
     * 执行sql语句,返回结果集
     *
     * @param sql SQL语句
     * @return 结果集对象
     */
    public static ResultSet executeQuery(String sql) {
    
    
        try {
    
    
            pre = conn.prepareStatement(sql);
            rs = pre.executeQuery();
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
        return rs;
    }

    /**
     * 执行更新语句
     *
     * @param sql 更新语句
     * @return 是否更新
     */
    public static boolean executeUpdate(String sql) {
    
    
        int r = 0;
        try {
    
    
            pre = conn.prepareStatement(sql);
            r = pre.executeUpdate();
			conn.close();
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
        return r != 0;
    }

    /**
     * 执行select语句,返回唯一值
     *
     * @param singleSql select语句
     * @return 结果对象
     */
    public static Object executeSingleQuery(String singleSql) {
    
    
        Object object = null;
        try {
    
    
            pre = conn.prepareStatement(singleSql);
            rs = pre.executeQuery();
            if (rs.next()) {
    
     //如果结果集不为空集
                object = rs.getObject(1);
            }
            conn.close();
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
        return object;
    }
}

4. PreparedStatement对象

Statement结构图:

statement结构图

Java提供了 Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedStatement 一般用来执行带IN参数或不带参数的查询语句,而 CallableStatement则是用于存储过程。

我们应优先使用PreparedStatement命令对象,因而在本文介绍一下PreparedStatement。

4.1 PreparedStatement是什么

PreparedStatement是java.sql包下面的一个接口,继承了Statement,并与之在两方面有所不同。通过调用connection.preparedStatement(sql)方法可以获得PreparedStatment对象。数据库系统会对sql语句进行预编译处理,预处理语句将被预先编译好,这条预编译的sql查询语句能在将来的查询中重用,因此它比Statement对象生成的查询速度更快。

有人主张,在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement。也就是说,减少使用Statement。

4.2 PreparedStatement的优势

  1. PreparedStatement可以写动态参数化的查询

假如数据库中有一张表模式为people(name, age),则向该表中插入数据可用如下代码:

String sql = insert into people values (?,?);
PreparedStatement pre = connection.preparedStatement(sql);
pre.setString(1,"张三");
pre.setInt(2,21);
pre.executeUpdate();

分析:?为占位符,一个占位符只允许有且仅有一个值(所以这也是PreparedStatement的一点局限性)。设置参数值(占位符值)时要根据参数的类型选择不同的set方法,值得注意的是:占位符的索引位置从1开始而不是0,如果填入0会导致java.sql.SQLException invalid column index异常。

  1. PreparedStatement可读性和维护性更强

正如上述代码所示,PreparedStatement可以使用参数进行执行sql语句,所以在一些复杂执行语句情况下,使用PreparedStatement对象可以更清晰的阅读与维护代码。

  1. PreparedStatement可以提高性能

因为预编译语句有可能被重复调用,所以预编译语句在被DB编译器编译后的执行代码会被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个函数)就会得到执行。这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配,那么在任何时候就可以不需要再次编译而可以直接执行,所以它的性能更高。而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配。比如:

 insert into people values('李四','22');
 insert into people values('王五','23');

即使是相同操作,但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义,事实上没有数据库会对普通语句编译后的执行代码缓存。

当然并不是所有预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果,以保存有更多的空间存储新的预编译语句。

  1. PreparedStatement更安全

先来了解一下普通Statement中的安全隐患。

(1)免密登录

假如我们有如下SQL代码:

String sql="select * from users where account='"+account+"' and pwd='"+password+"'";

如果我们输入account和pwd如下:

String account = "1' OR '1'='1";
String pwd = "1' OR '1'='1";

最终执行的SQL语句就变成了如下代码:

select * from users
where account = '1' OR '1' = '1' and pwd = '1' OR '1' = '1';

所以会出现where子句条件恒为真值,因此可以达到无账号密码就可以登录成功。

(2)删除数据库

如果我们把account和pwd输入如下:

String account = "';drop table users; -- ";
String pwd = "密码内容被注释";

最终执行的SQL语句就变成了如下代码:

select * from users where account = ''; drop table users; -- 密码内容被注释

这样虽然用户没有登录,却把数据库中的users表给删除了。

★使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。

注意

  • PreparedStatement与java.sql.Connection对象是关联的,一旦你关闭了connection,PreparedStatement也没法使用了。
  • 不支持预编译SQL查询的JDBC驱动,在调用connection.prepareStatement(sql)的时候,它不会把SQL查询语句发送给数据库做预处理,而是等到执行查询动作的时候(调用executeQuery()方法)才把查询语句发送个数据库,这种情况和使用Statement是一样的。

猜你喜欢

转载自blog.csdn.net/weixin_43653599/article/details/105384433