当前位置 博文首页 > 黑米面包派:【Mybatis源码解析】- JDBC连接数据库的原理和操作

    黑米面包派:【Mybatis源码解析】- JDBC连接数据库的原理和操作

    作者:黑米面包派 时间:2021-04-28 18:15

    JDBC连接数据库的原理和操作

    JDBC即Java DataBase Connectivity,java数据库连接;JDBC 提供的API可以让JAVA通过API方式访问关系型数据库,执行SQL语句,获取数据;常见关系型数据库如Oracle、MySQL、SQLServer等;对于非关系型数据库如Redis、mongonDB等就显得无力;关系型数据库最典型的数据结构是表,易于维护,灵活使用(使用表结构,支持SQL语言的简单及复杂表及多表之间的查询),非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合(文档型、键值对型、列式数据库、图形数据库),相对于关系型数据库(读写性能比较差,效率低,灵活度低、高并发处理显得鸡肋);非关系型数据库优点多多,速度快、高拓展、低成本(就数据结构复杂,其他都比较简单,学习难度就有点大)。
    JDBC是一种规范,它提供的接口,是一套完整的、可移植的访问底层数据库的程序。

    JDBC连接数据库步骤

    1. 在开发环境中加载指定的数据库驱动。例如在mysql使用环境中,则需要下载mysql对应的驱动程序mysql-connector-java-xxx.java
    2. 在应用中加载驱动。例如使用Class.forName()的方式将驱动类加载到内存中;
    3. 创建数据库连接对象。在接口Driver中定义了相应的连接方式;
    4. 创建Statement对象。Statement类的主要是用于执行静态 SQL语句并返回它所生成结果的对象。通过Connection对象的createStatement()方法可以创建一个Statement对象。例如:Statement statament = connection.createStatement()
    5. 调用Statement对象的相关方法执行相对应的 SQL 语句;
    6. 关闭数据库连接:使用完数据库或者不需要访问数据库时,通过Connection.close() 方法及时关闭数据连接。

    示例

    1、数据准备

    现在mysql数据库中创建person表,并插入需要的数据

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for blog
    -- ----------------------------
    DROP TABLE IF EXISTS `person`;
    CREATE TABLE `person` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
        `mobile` varchar(11) COLLATE utf8mb4_general_ci DEFAULT NULL,
        `age` int(2) DEFAULT NULL,
        `email` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
        PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
    
    -- ----------------------------
    -- Records of blog
    -- ----------------------------
    BEGIN;
    INSERT INTO `person` VALUES (1, 'wujiwen',  '13011111111', 20, 'jiwenwu@outlook.com');
    INSERT INTO `person` VALUES (2, 'mybatis', '13100000000', 10, 'service@mybatis.com');
    COMMIT;
    
    SET FOREIGN_KEY_CHECKS = 1;
    

    2、引入mysql驱动

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    

    3、使用JDBC连接数据库

    按照上面的步骤,我们先将特定厂商Mysql的驱动加载到内存中,然后通过Driver实例获取数据库连接

    private final String url = "jdbc:mysql://127.0.0.1:3306/mybatis";
    private final String user = "root";
    private final String password = "passw0rd";
    private final String mysqlDriver = "com.mysql.cj.jdbc.Driver";
    
    public Connection getConnectionByProperties() throws Exception {
    	  Properties properties = new Properties();
    	  properties.put("user",user);
    	  properties.put("password",password);
    	  // 加载驱动实例
    	  Driver driver = ((Driver) Class.forName(mysqlDriver).newInstance());
    	  // 获取链接
    	  return driver.connect(url,properties);
    }
    
    public Connection getConnectionByUserPassword() throws Exception {
    	  // 加载驱动实例
    		Class.forName(mysqlDriver);
    		// 通过DriverManager获取
    	  return DriverManager.getConnection(url, user, password);
    }
    

    当使用Class.forName(mysqlDriver)加载驱动类到内存中的时候,同时会执行这个驱动类中的静态代码块,创建一个Driver实例;然后调用DriverManager 进行注册DriverDriverManager 作为 Driver 的管理器,它在第一次被调用的时候,它会被加载到内存中,然后执行其定义的静态代码段,在静态代码段中,有一个 loadInitialDrivers() 静态方法,用于加载配置在jdbc.drivers 系统属性内的驱动DriverDriverManager来统一管理操作Driver和获取Connection对象;

    4、增删改查

    列举最简单的sql操作示例

    public int insert(Connection connection) throws SQLException {
            try {
                Statement statement = connection.createStatement();
                return statement.executeUpdate("insert into `person` (nickname,mobile,age,email) values ('insert', '13100000000', 14, 'insert@mybatis.com');");
            }catch (Exception e){
                try {
                    connection.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            }finally {
                connection.close();
            }
            return 0;
        }
    
        public int update(Connection connection) throws SQLException {
            try {
                Statement statement = connection.createStatement();
                return statement.executeUpdate("update `person` set mobile = '12000000000' where id = 4;");
            }catch (Exception e){
                try {
                    connection.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            }finally {
                connection.close();
            }
            return 0;
        }
    
        public int delete(Connection connection) throws SQLException {
            try {
                Statement statement = connection.createStatement();
                return statement.executeUpdate("delete `person`  where id = 4;");
            }catch (Exception e){
                try {
                    connection.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            }finally {
                connection.close();
            }
            return 0;
        }
    
        public List<Person> listPerson(Connection connection) throws SQLException {
            List<Person> personList = new ArrayList<>();
            try {
                Statement statement = connection.createStatement();
                ResultSet resultSet = statement.executeQuery("select  * from `person`;");
                while (resultSet.next()){
                    Person person = new Person();
    								// 指定列名方式获取
                    Integer id = resultSet.getInt("id");
    								// 指定列索引方式获取,startIndex = 1
                    String nickname = resultSet.getString(2);
                    String mobile = resultSet.getString("mobile");
                    Integer age = resultSet.getInt(4);
                    String email = resultSet.getString("email");
                    person.setId(id);
                    person.setNickname(nickname);
                    person.setMobile(mobile);
                    person.setAge(age);
                    person.setEmail(email);
                    personList.add(person);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                connection.close();
            }
            return personList;
        }
    

    JDBC简化方案

    原生方式不仅硬编码多而且非常的不灵活,对于结果的封装处理也比较麻烦。这时候也出现了一些比较好用的简化操作方案,例如常见的Hibernate,DBUtils,Mybatis。本文将简单的介绍一下DBUtils是如何简化数据的查询和操作的。

    DBUtils

    该组件严格上说不是一款持久化框架,更多的可以理解成一款简化操作工具集合,将原生JDBC的很多动态化操作进行了封装,方便了使用

    核心功能介绍

    • QueryRunner类,提供了对sql语句的各种api
    • ResultSetHandler接口,用于定义查询操作后,如何对结果进行封装
    • DbUtils工具类,里面封装了很多关闭资源及处理事务的方法

    示例

        private final String url = "jdbc:mysql://127.0.0.1:3306/mybatis";
        private final String user = "root";
        private final String password = "passw0rd";
        private final String mysqlDriver = "com.mysql.cj.jdbc.Driver";
    
        public Connection getConnection() throws Exception {
            Class.forName(mysqlDriver);
            return DriverManager.getConnection(url, user, password);
        }
    
        public int insert(Connection connection) throws SQLException {
            QueryRunner runner = new QueryRunner();
            String sql = "insert into `person` (nickname,mobile,age,email) values (?,?,?,?);";
            Object[] params = {"dbutils", "12000000000", 10, "dbutils@dbutils.com"};
            return runner.update(connection, sql, params);
        }
    
        public int update(Connection connection) throws SQLException {
            QueryRunner runner = new QueryRunner();
            String sql = "update `person` set mobile = '00000000000' where id  = ?";
            Object params = 5;
            return runner.update(connection,sql,params);
        }
    
        public int delete(Connection connection) throws SQLException{
            QueryRunner runner = new QueryRunner();
            String sql = "delete `person` where id  = ?";
            Object params = 5;
            return runner.update(connection,sql,params);
        }
        // 查询单个对象
        public Person getPerson(Connection connection) throws SQLException {
            QueryRunner runner = new QueryRunner();
            String sql = "select * from `person` where id = ? ";
            Object params = 1;
            return runner.query(connection,sql,new BeanHandler<>(Person.class),params);
        }
        // 查询集合对象
        public List<Person> listPerson(Connection connection) throws SQLException {
            QueryRunner runner = new QueryRunner();
            String sql = "select * from `person`";
            return runner.query(connection,sql,new BeanListHandler<>(Person.class));
        }
    

    这时候我们会发现动态sql的编写也比较简单了,参数的设置比较灵活,特别是对于查询结果的封装变得容易多了。

    但是还有很多不足的地方,例如如果将nickname修改成username,这种情况就无法准确的对应的bean结果集。我们也可以对其进行改造,添加相应的别名方式处理。

    相对于Hibernate的庞大和繁琐,我们更推荐Mybatis去处理。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

    后续将简单演示mybatis是如何处理这些问题的,并了解这背后的相关原理及实现方式。

    bk