Mybatis源码解析1—— JDBC

本文介绍了JDBC的基本操作及存在的问题,如资源浪费、sql硬编码等,引出了ORM框架的需求。Mybatis作为主流ORM框架,通过配置化SQL和对象映射解决了这些问题,提供了更高效、易维护的数据库操作方式。下篇将深入解析Mybatis的实现原理。

  在之前的文章中,我为大家介绍了 Mybatis 的详细用法,算是基础教程。 

  详细链接:Mybatis 基础教程

  言归正传,只懂基础可不行,接下来将给大家带来高阶的源码解析教程,从浅入深,通过源码解析,图例结合,抽丝剥茧,让大家看的不吃力,并且能够深刻理解 Mybatis 这个框架的底层实现原理,让大家学到的不仅仅是这个框架用法,而是通过这个框架理解其设计思想。

  

1、JDBC

  我相信所有开发者第一次与数据库打交道时,就是通过 JDBC 来实现的,第一次通过程序获取到数据库中的数据时,那种高兴,那种吃惊,反正我依稀历历在目。            

  什么是 JDBC:JDBC 是Java与数据库交互的API,用来规范客户端程序如何来访问数据库的应用程序接口。

  通常分为两组API:

  ①、面向Java应用程序开发人员的API,是一个标准的API,且不同数据库具有不同的实现。

  ②、面向数据库驱动开发人员,是前者的底层支持。

  因为大家都是Java开发人员,这里我们就介绍第一组API。

  JDBC API主要位于JDK中的java.sql包中(之后扩展的内容位于javax.sql包中)

  下面介绍几个关键的接口,注意这都是JDK内部提供的一些接口,具体实现得看具体的数据库驱动,比如MySQL,我们通常会在maven或gradle配置mysql-connectior-java ,下面接口的具体实现就在其中。

  ①、Driver:驱动程序,会将自身加载到DriverManager中去.

  ②、DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。

  ③、Connection:数据库连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。

  ④、Statement:用来执行SQL查询和更新(针对静态SQL语句和单次执行)。

  ⑤、PreparedStatement:用来执行包含动态参数的SQL查询和更新(预编译,在服务器端编译,允许重复执行以提高效率)。

  ⑥、CallableStatement:用来调用数据库中的存储过程。

  ⑦、ResultSet:用来存储数据库查询操作返回的结果。

  ⑧、SQLException:表示在数据库连接的建立、SQL语句的执行、关闭等过程中发生了异常。

2、完整的交互过程

  下面,我们通过JDBC来完成一次数据库查询操作,步骤如下:

  ①、加载数据库驱动

  ②、获取数据库连接(通过数据库URL,用户名,密码)

  ③、定义SQL语句

  ④、通过数据库连接,创建 Statement 对象或者 PreparedStatement 对象

  ⑤、通过 Statement 对象执行 SQL 语句,得到结果集 ResultSet 对象

  ⑥、读取 ResultSet 对象,转换成我们要的 JavaBean

  ⑦、关闭数据库连接、Statement、ResultSet等对象,释放相关资源

代码实现如下:

package com.itcoke.bean;

public class Person {

    private Long pid;

    private String pname;

    private Integer page;

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public Integer getPage() {
        return page;
    }

    public void setPage(Integer page) {
        this.page = page;
    }

    @Override
    public String toString() {
        return "Person{" +
                "pid=" + pid +
                ", pname='" + pname + '\'' +
                ", page=" + page +
                '}';
    }
}

package com.itcoke.jdbc;

import com.itcoke.bean.Person;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class JDBCUtils {
    //MySQL数据库驱动,注意多了一个cj,com.mysql.jdbc.Driver和mysql-connector-java 5一起用
    public static String driverClass = "com.mysql.cj.jdbc.Driver";
    //MySQL用户名
    public static String userName = "root";
    //MySQL密码
    public static String passWord = "root1234";
    //MySQL URL
    public static String url = "jdbc:mysql://localhost:3306/mybatis-study";
    //定义数据库连接
    public static Connection conn = null;
    //定义声明数据库语句,使用 预编译声明 PreparedStatement提高数据库执行性能
    public static PreparedStatement ps = null;
    //定义返回结果集
    public static ResultSet rs = null;

    public static List<Person> selectPersonByName(String pname){
        List<Person> personList = new ArrayList<>();

        try {
            // 1、加载数据库驱动
            Class.forName(driverClass);
            // 2、获取数据库连接
            conn = DriverManager.getConnection(url,userName,passWord);
            // 3、定义 sql 语句,?表示占位符
            String sql = "select * from person where pname=?";
            // 4、获取预编译处理的statement
            ps = conn.prepareStatement(sql);
            //设置sql语句中的参数,第一个为sql语句中的参数的?(从1开始),第二个为设置的参数值
            ps.setString(1, "itcoke");
            // 5、向数据库发出 sql 语句查询,并返回结果集
            rs = ps.executeQuery();
            while(rs.next()){
                // 6、读取ResultSet,转换成我们要的对象
                Person person = new Person();
                person.setPid(rs.getLong("pid"));
                person.setPname(rs.getString("pname"));
                personList.add(person);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            // 7、关闭数据库连接
            if(rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(ps!=null){
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return personList;
    }

    public static void main(String[] args){
        List<Person> personList = JDBCUtils.selectPersonByName("itcoke");
        System.out.println(personList);
    }
}

  PS:数据库名称为 mybatis-study,表为 person

  

3、缺点分析

  ①、问题一:每执行一次增删改查,就要建立连接,关闭连接,频繁的获取连接和关闭连接,会造成数据库资源浪费,影响数据库性能。

  设想解决:使用数据库连接池管理数据库连接

  ②、问题二:将 sql 语句硬编码到程序中,如果sql语句修改了,那么需要重新编译 Java 代码,不利于系统维护

  设想解决:将 sql 语句可配置化,比如设置到 xml 文件中,这样即使 sql 语句变化了,我们也不需要对 Java 代码进行修改

  ③、问题三:在 PreparedStatement 中设置参数,对占位符设置值都是硬编码在Java代码中,不利于系统维护

  设想解决:将 sql 语句以及占位符和参数都配置到 xml 文件中

  ④、问题四:从 resultset 中遍历结果集时,对表的字段存在硬编码,不利于系统维护

  设想解决:将查询的结果集自动映射为 Java 对象

  ⑤、问题五:重复性代码特别多,包括建立建立,加载驱动等

  设想解决:抽取封装公共代码

  ⑥、问题六:没有缓存,如果存在数据量大,且频繁查询的情况,这种方式性能特别低

  设想解决:集成缓存框架去操作数据库

  ⑦、问题七:sql 的移植性不好,如果换个数据库,那么sql 语句可能要重写

  设想解决:在 JDBC 和 数据库之间插入第三方框架,用第三方去生成 sql 语句,屏蔽数据库的差异

4、ORM框架

  为了解决上面这些缺点,ORM(Object Relational Mapping,对象-关系映射)框架应运而生。

  ORM 模型就是数据库的表和Java对象的映射关系模型,它主要解决数据库数据和Java对象的相互映射,通过映射关系,我们可以简单而迅速的把数据库数据转换成Java对象,从而让开发人员无需对数据库相关知识深入了解,便可以操作数据库数据。

  当前市面上比较主流的ORM框架包括 Hibernate、Mybatis、Spring JDBC 等等,各有优缺点,这里可乐不做详细介绍,本系列文章主要是介绍当前使用最广泛的 ORM框架——Mybatis。

  其更轻量、更可控的特性,比较适合当前主流业务,特别是大数据量、高并发的场景。

5、小结

  本文我们介绍了JDBC直接操作数据库的一些问题,引申出解决此类问题的ORM框架,其中 Mybatis 是其中的佼佼者,

  那么为什么 Mybatis 能够解决 JDBC 直接操作数据库的痛点?它又是如何实现的呢?

  不要走开,我们下篇文章更精彩!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员可乐、

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值