zoukankan      html  css  js  c++  java
  • Java学习之==>JDBC

    一、概述

    官方解释:

      JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的 Java API,可以为多种关系型数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,同时,JDBC也是个商标名。

    简单地说,JDBC 可做三件事:与数据库建立连接、发送 操作数据库的语句并处理结果。

    理解:

      JDBC就是一套访问数据库的规范(或者说是一组接口),提供数据库的协议标准,各个数据库厂商根据这套规范提供一套访问自己数据库的驱动。

    二、JDBC工作原理

    从图中可以看到 JDBC 的几个重要组成要素。最顶层是我们自己编写的Java应用程序(Java Application),Java 应用程序可以使用集成在JDK中的 java.sql 和 javax.sql 包中的JDBC API来连接和操作数据库。下面我们依次介绍JDBC的组成要素:

    1、JDBC API

      JDBC API 由Sun公司提供,提供了Java 应用程序与各种不同数据库交互的标准接口,如:Connection(连接)接口,Statement 接口,ResultSet(结果集)接口,PreparedStatement 接口等。开发者使用这些JDBC接口进行各种数据库操作。

    • DriverManager:此类管理数据库驱动程序列表。使用通信子协议将来自 Java 应用程序的连接请求与适当的数据库驱动程序匹配;
    • Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象;
    • Connection:该接口具有用于联系数据库的所有方法。连接对象通信上下文,即,与数据库的所有通信仅通过连接对象;
    • Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数;
    • ResultSet: 在使用 Statement 对象执行 SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据;
    • PreparedStatement:继承自 Statement 接口,主要用于执行预编译 SQL,除了具有 Statement 接口的所有功能,还另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值;

    2、JDBC Driver Manager

      JDBC Driver Manager(驱动程序管理器)由Sun公司提供,它是 JDBC 体系结构的支柱,负责管理各种不同的 JDBC 驱动,把 Java 应用程序连接到相应的 JDBC 驱动程序上,位于JDK的 java.sql 包中。

    3、JDBC Driver

      JDBC Driver 由各个数据库厂商或第三方中间件厂商提供,负责连接各种不同的数据库。例如,上图中,访问 MySQL 和 Oracle 时需要不同的JDBC驱动,这些 JDBC 驱动都实现了JDBC API 中定义的各种接口。在开发Java应用程序时,我们只需正确加载JDBC驱动,正确调用 JDBC API,就可以进行数据库访问了。

    三、JDBC 访问数据库的步骤

    我们先来看一下下面这段代码:

    public class App {
      private static final String URL = "jdbc:mysql://192.168.182.131:3306/cakes";
      private static final String USER_NAME = "root";
      private static final String PASSWD = "123456";
    
      public static void main(String[] args) throws Exception {
    
        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
    
        // 2.建立连接
        Connection connection = DriverManager.getConnection(URL, USER_NAME, PASSWD);
    
        // 3.建立Statement
        Statement statement = connection.createStatement();
    
        // 4.执行sql
        boolean execResult = statement.execute("insert into user(`name`,`passwd`) values('木木','123456')");
        // statement.executeUpdate返回的是影响的行数
        int effectRows = statement.executeUpdate("insert into user(`name`,`passwd`) values('大叔','888888')");
    
        System.out.println("execResult = " + execResult);
        System.out.println("effectRows:" + effectRows);
    
        // 5.资源关闭
        statement.close();
        connection.close();
      }
    }
    JDBC 示例

    执行结果如下:

    以上红色字体为警告信息,意思是:com.mysql.jdbc.Driver 这个驱动已经被弃用了,建议使用 com.mysql.cj.jdbc.Driver 这个驱动,更改后再次运行则没有这个警告信息了。

    所以标准代码如下:

    public class App {
      private static final String URL = "jdbc:mysql://192.168.182.131:3306/cakes";
      private static final String USER_NAME = "root";
      private static final String PASSWD = "123456";
    
      public static void main(String[] args) throws Exception {
    
        // 1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
    
        // 2.建立连接
        Connection connection = DriverManager.getConnection(URL, USER_NAME, PASSWD);
    
        // 3.建立Statement
        Statement statement = connection.createStatement();
    
        // 4.执行sql
        boolean execResult = statement.execute("insert into user(`name`,`passwd`) values('木木','123456')");
        // statement.executeUpdate返回的是影响的行数
        int effectRows = statement.executeUpdate("insert into user(`name`,`passwd`) values('大叔','888888')");
    
        System.out.println("execResult = " + execResult);
        System.out.println("effectRows:" + effectRows);
    
        // 5.资源关闭
        statement.close();
        connection.close();
      }
    }

    同时,因为我本地的 Gradle 工程引入的 mysql-connector-java 包的版本是 8.0.16,所以加载驱动那一步可以省略不写(5.x.x版本之后)。

    JDBC访问数据库基本步骤:

    1、加载驱动程序

    2、建立连接

    3、创建 Statement 对象

    4、执行 SQL 语句

    5、处理返回结果

    6、关闭资源,包括:ResultSet,Statement 和 Connection

    四、Statement 和 PreparedStatement

    我们先来看两段代码:

    public static void test01() {
    
      Connection connection = null;
      Statement statement = null;
    
      try {
        connection = DriverManager.getConnection(URL, USER_NAME, PASSWD);
    
        statement = connection.createStatement();
    
        String sql = "insert into user(`name`,`passwd`) values('木木','123456')";
    
        int executeNum = statement.executeUpdate(sql);
        System.out.println(String.format("成功插入%d条数据",executeNum));
    
      } catch (SQLException se) {
        se.printStackTrace();
      }finally {
        try {
          if (null != statement) {
            statement.close();
          }
          if (null != connection) {
            connection.close();
          }
        } catch (SQLException se) {
          se.printStackTrace();
        }
      }
    }
    Statement
    public static void test02() {
    
      Connection connection = null;
      PreparedStatement preparedStatement = null;
    
      try {
        connection = DriverManager.getConnection(URL, USER_NAME, PASSWD);
    
        String sql = "insert into user(`name`,`passwd`) values(?, ?)";
    
        preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,"jack");
        preparedStatement.setString(2,"112233");
    
        int executeNum = preparedStatement.executeUpdate();
        System.out.println(String.format("成功插入%d条数据",executeNum));
    
      } catch (SQLException se) {
        se.printStackTrace();
      }finally {
        try {
          if (null != preparedStatement) {
            preparedStatement.close();
          }
          if (null != connection) {
            connection.close();
          }
        } catch (SQLException se) {
          se.printStackTrace();
        }
      }
    }
    PreparedStatement

    PreparedStatment 接口继承自 Statement 接口,PreparedStatment 执行的是预编译 SQL,使用占位符,创建 PreparedStatment 对象时就会把 SQL语句作为参数参入。

    通常情况下,我们更建议使用 PreparedStatment,原因如下:

    1、提高了代码的可读性和可维护性

    虽然使用 PreparedStatement 来代替 Statement 会多几行代码,但避免了烦琐麻烦又容易出错的SQL语句拼接,提高了代码的可读性和可维护性。

    2、提高了SQL语句执行的性能

    创建 Statement 对象时不使用SQL语句做参数,不会解析和编译SQL语句,每次调用方法执行SQL语句时都要进行SQL语句解析和编译操作,即操作相同仅仅是数据不同。

    创建 PreparedStatement 对象时使用SQL语句做参数,会解析和编译该SQL语句,也可以使用带占位符的SQL语句做参数,在通过 setXxx ()方法给占位符赋值后执行SQL语句时无须再解析和编译SQL语句,直接执行即可。多次执行相同操作可以大大提高性能。

    3、提高了安全性

    PreparedStatement 使用预编译语句,传入的任何数据都不会和已经预编译的SQL语句进拼接,避免了SQL注入攻击。

    五、ResultSet

    使用 Statement 或 PreparedStatement 对象执行 SQL 查询后,会返回 ResultSet 结果集,作为一个迭代器,我们一般使用 next() 方法来判断把光标挪向下一行。

    public class UserDemo3 {
    
      private static final String URL = "jdbc:mysql://192.168.182.131:3306/cakes";
      private static final String USER_NAME = "root";
      private static final String PASSWD = "123456";
    
    
      public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
    
        try {
          connection = DriverManager.getConnection(URL, USER_NAME, PASSWD);
          String sql = "select * from user where name=?";
    
          preparedStatement = connection.prepareStatement(sql);
          preparedStatement.setString(1, "大叔");
    
          resultSet = preparedStatement.executeQuery();
    
          ArrayList<User> users = new ArrayList<>();
    
          while (resultSet.next()) {
            User user = User.of();
            user.setId(resultSet.getInt("id"));
            user.setName(resultSet.getString("name"));
            user.setPasswd(resultSet.getString("passwd"));
    
            users.add(user);
          }
          for (User user : users) {
            System.out.println(user);
          }
        } catch (SQLException e) {
          e.printStackTrace();
        } finally {
          try {
            if (null != resultSet) {
              resultSet.close();
            }
            if (null != preparedStatement) {
              preparedStatement.close();
            }
            if (null != connection) {
              connection.close();
            }
          } catch (SQLException e) {
            e.printStackTrace();
          }
        }
      }
    }
    ResultSet

    六、事物操作

    七、SQL 执行工具设计

    下面我们使用 JDBC 简单设计一款 SQL 执行工具

    public interface JdbcConsts {
    
      String URL = "jdbc:mysql://192.168.182.131:3306/cakes";
    
      String USER_NAME = "root";
    
      String PASSWD = "123456";
    
    }
    JdbcConsts 接口

    JdbcConsts 接口中存放的是数据库连接配置。

    public final class ConnectionHandler {
    
      private ConnectionHandler() {
      }
    
      public static ConnectionHandler of() {
        return new ConnectionHandler();
      }
    
      public Connection getConnection() {
        try {
          return DriverManager.getConnection(URL, USER_NAME, PASSWD);
        } catch (SQLException e) {
          throw new IllegalStateException(e);
        }
      }
    }
    ConnectionHandler 类

    ConnectionHandler 类封装了数据库连接这个操作。

    public abstract class AbstractDBHandler<T> {
    
      /**
       * @param sql SQL语句, update user set name=?,age=? where id=?
       * @param params [zhangsan,23,12]
       */
      public int modify(String sql, List<Object> params) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
          connection = ConnectionHandler.of().getConnection();
    
          // statement = connection.prepareStatement(sql);
          //
          // for (int i = 0; i < params.size(); i++) {
          //   statement.setObject(i + 1, params.get(i));
          // }
          statement = preparedStatement(sql,params,connection);
    
          int effectRows = statement.executeUpdate();
    
          System.out.println("effectRows = " + effectRows);
    
          return effectRows;
        } catch (SQLException e) {
          throw new IllegalStateException(e);
        } finally {
          release(null, statement, connection);
        }
      }
    
      public List<T> select(String sql, List<Object> params) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
          connection = ConnectionHandler.of().getConnection();
    
          // statement = connection.prepareStatement(sql);
          //
          // for (int i = 0; i < params.size(); i++) {
          //   statement.setObject(i + 1, params.get(i));
          // }
          statement = preparedStatement(sql,params,connection);
    
          resultSet = statement.executeQuery();
    
          List<T> result = new ArrayList<>();
          while (resultSet.next()) {
            // 抽象点,通过抽象方法来实现
            T t = mapping(resultSet);
            result.add(t);
          }
          return result;
        } catch (SQLException e) {
          throw new IllegalStateException(e);
        } finally {
          release(resultSet, statement, connection);
        }
      }
    
      private PreparedStatement preparedStatement(String sql,List<Object> params,
                                                  Connection connection) throws SQLException {
        PreparedStatement statement = connection.prepareStatement(sql);
        for (int i = 0; i < params.size(); i++) {
          statement.setObject(i + 1, params.get(i));
        }
        return statement;
      }
    
      protected abstract T mapping(ResultSet resultSet) throws SQLException;
    
      private void release(ResultSet resultSet, Statement statement, Connection connection) {
        try {
          if (null != resultSet) {
            resultSet.close();
          }
          if (null != statement) {
            statement.close();
          }
          if (null != connection) {
            connection.close();
          }
        } catch (SQLException e) {
          throw new IllegalStateException(e);
        }
      }
    }
    AbstractDBHandler 抽象类

    AbstractDBHandler 类中定了了 select() 和 modify() 两个方法,modify()用于执行增、删、改,select()用于执行查询。modify() 方法传入两个参数 sql 和 List<Object> params,具体执行的什么 SQL 和 该 SQL 应该传入什么值,全部在 Dao 中定义。select() 方法查询出的结果对于不同的表返回的是不一样的,所以,我们使用了一个抽象方法 mapping() 来抽象返回结果,具体我们查询的是哪张表,返回的是什么样的结果,全部在 Dao中定义,如下:

    public class UserDao extends AbstractDBHandler<User> {
    
      @Override
      protected User mapping(ResultSet resultSet) throws SQLException {
    
        User user = User.of();
        user.setId(resultSet.getInt("id"));
        user.setName(resultSet.getString("name"));
        user.setPasswd(resultSet.getString("passwd"));
    
        return user;
      }
    
      public int addUser(User user) {
        String sql = "insert into user(`name`,`passwd`) values(?,?)";
        List<Object> params = new ArrayList<>();
        params.add(user.getName());
        params.add(user.getPasswd());
        return modify(sql, params);
      }
    
      public int delUser(User user) {
        String sql = "delete from user where name=? and passwd=?";
        List<Object> params = new ArrayList<>();
        params.add(user.getName());
        params.add(user.getPasswd());
        return modify(sql, params);
      }
    
      public List<User> queryByName(User user) {
        String sql = "select * from user where name=? and passwd=?";
        List<Object> params = new ArrayList<>();
        params.add(user.getName());
        params.add(user.getPasswd());
        return select(sql, params);
      }
    }
    UserDao

    UserDao 继承自 AbstractDBHandler 类,当中定义了关于 User 表的 增、删、改、查等操作。其中,增、删、改调用的是 modify() 方法而查询调用的是 select() 方法。同时也定义了 User 表查询结果由三个字段组成:id,name,passwd。

    public class App {
    
      @Test
      public void testInsert() {
        UserDao userDao = new UserDao();
    
        User user = User.of("小芹", "s123456");
    
        userDao.addUser(user);
      }
    
      @Test
      public void testDelete() {
        UserDao userDao = new UserDao();
    
        User user = User.of("jack", "123456");
    
        userDao.delUser(user);
      }
    
      @Test
      public void testSelect() {
        UserDao userDao = new UserDao();
    
        User user = User.of("大叔", "111222");
    
        List<User> users = userDao.queryByName(user);
    
        System.out.println(users);
      }
    }

    测试类中只需要传入执行 增、删、改、查具体的数据就可以。

    以上,只是对针对 JDBC 的操作进行了简单的设计和封装,还有很多地方可以优化,这里就不再深入讲解。

  • 相关阅读:
    C#进行Visio二次开发之设备状态跟踪
    C#进行Visio二次开发之Shape的Data1、Data2、Data3的用处
    C#进行Visio二次开发之界面设计及架构设计
    Sqlserver常用函数例子说明
    香港生肖采集及规则分析软件
    使用NVelocity0.5实现服务器端页面自动生成
    C#进行Visio二次开发之判断图纸是否有设备
    C#进行Visio二次开发之图纸缩放操作的实现
    C#进行Visio开发的事件处理
    导线应力及弧垂计算相关资料
  • 原文地址:https://www.cnblogs.com/L-Test/p/11565938.html
Copyright © 2011-2022 走看看