七年再回首,从JDBC到ORM

社区

前言

初学ORM和JDBC还是2016年的10月15日,在宿舍里偷偷记下了一纸笔记。 picture.image

转眼七年已过,从来没有手写过JDBC,数据库的连接都是通过框架和连接池就给完成了,ORM的工作框架也都顺手给完成了。

本篇文章就用七年后角度,再次学习JDBC和ORM。

JDBC

JDBC即Java数据库连接,是Java提供的一套连接数据库的接口规范,开发者可以通过模板代码来连接不同的数据库。这里就拿MySQL和oracle来举例,画了一个简单的图。

picture.image

我们可以通过右边java.sql提供的JDBC流程,只要将对应数据库驱动注册到DriverManager中,就能实现对数据库的连接,然后获取连接对象、执行sql、返回结果集。这些在java.sql包里都提供了对应的类。

建表

安装数据库的文章之前有写过,这里直接建表。

picture.image

一共三个字段,两个varchar字符串类型,一个int类型,然后插入数据。

picture.image

代码实现

这里先定义一个和表字段一样的Java类。

picture.image

这里就以MySQL为例,使用JDBC查询数据库。首先我们要引入MySQL的驱动依赖。

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

然后就根据JDBC接口规范开始开发代码。

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

public class JdbcDemo {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://175.27.xxx.xxx:3306/test";
        String user = "root";
        String password = "xxxxxxx";
        Connection conn = DriverManager.getConnection(url, user, password);
        Statement statement =  conn.createStatement();
        String sql = "select * from people";
        ResultSet rs = statement.executeQuery(sql);
        while (rs.next()){
            // System.out.println(rs.getString(1) + "-" + rs.getInt(2) + "-" + rs.getString(3));
            People people = new People();
            people.setName(rs.getString(1));
            people.setAge(rs.getInt(2));
            people.setPhone(rs.getString(3));
            System.out.println(people);
        }
        rs.close();
        statement.close();
        conn.close();
    }
}

手动对ResultSet遍历,获取每个字段的值,将每个字段set到新创建的People对象中。这里要注意的是:下标是从1开始的。

运行输出结果:

picture.image

数据表中的三条数据都被查询到并输出了出来。接下来就从头分析一下JDBC流程。

JDBC流程

1. 加载驱动

JDBC中通过Class.forName反射加载驱动类。com.mysql.cj.jdbc.Driver 是MySQL的驱动类,在MySQL的依赖包里,当没有添加MySQL依赖时,就会报错ClasNotFound的异常。

picture.image

上图为MySQL驱动类源码,继承NonRegisteringDriver并实现了JDBC的Driver接口,在加载类的同时,也会调用static代码块,调用DriverManger的registerDriver方法,向DriverManger注册此驱动。

picture.image

registeredDrivers是一个CopyOnWriteArrayList,即线程安全的List,记录着各个Driver的信息,addIfAbsent只有当这个driver不存在时,则会添加进去,保证每个数据库的驱动信息只会加载一次。

2. 数据库连接

DriverManger记录着所有驱动的信息,并通过getConnection() 连接数据库并返回Connection对象。代码如下:

picture.image

aDriver就是存放在registeredDrivers中的Driver信息,这里指的就是MySQL驱动,然后调用此驱动的connect方法连接数据库,看看NonRegisteringDriver类中connect代码。

picture.image

里面就是所有通过url、user、password信息连接MySQL数据库的逻辑实现,最后返回一个Connection对象。通过断点可以知道,最后返回的是MySQL驱动的ConnectionImpl类。

picture.image

所以说,JDBC的DriverManger只是提供了管理Driver以及连接数据库的入口(connect方法),至于连接数据库的细节,需要每个数据库的驱动类自己实现细节,只要最后按照JDBC规范,返回一个Connection即可。

3. 执行查询SQL

使用连接对象createStatement方法创建一个Statement对象,即用来直行SQL的一个对象。通过断点可以看到,从MySQL数据库连接中返回的是MySQL实现的StatementImpl对象。

然后调用executeQuery()来执行查询SQL。

picture.image

最后返回结果集ResultSet,其实返回的是MySQL驱动中自己实现的ResultSet子类ResultSetInternalMethods

picture.image

最后我们根据ResultSet提供的next来遍历数据集,通过从1开始的索引来获取每条数据中每个字段,手动赋值给People对象。

总结

至此我们可以知道,JDBC提供的Driver、Connection、Statement和ResultSet都是接口,里面所有方法都需要数据库驱动自己实现,MySQL驱动如此,Oracle驱动亦是如此。

ORM

ORM(Object Relational Mapping),对象关系映射,我理解的是就是JAVA类和数据库中表的映射,类字段和表字段的映射以及字段类型的映射。

上面连接MySQL返回结果集ResultSet之后,需要完成手动遍历、创建People对象、赋值等一系列操作,超级麻烦。所以ORM框架就开始出现了,上面这些操作就完全由框架内部处理。从JavaWeb学的DButils,到SSM框架中的MyBatis等都是ORM框架。

与JDBC相比,我们只需要输入SQL,定义好Java类,ORM框架就自动将数据库中查询到的数据,封装到Java类中返回给我们,然后我们从类对象中get数据字段即可。

DButils

引入依赖:

<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.8.1</version>
</dependency>

使用DButils完成ORM测试代码。

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;

public class DBUtilsDemo {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://175.27.xxx.xx:3306/test";
        String user = "root";
        String password = "xxxxxx";
        Connection conn = DriverManager.getConnection(url, user, password);
        String sql = "select * from people";
        QueryRunner queryRunner = new QueryRunner();
        List<People> peoples = queryRunner.query(conn, sql, new BeanListHandler<>(People.class));
        for (People people : peoples) {
            System.out.println(people);
        }
    }
}

从代码里可以看出,这里不会再返回原始的ResultSet数据集,而是queryRunner.query之后直接返回List<People>,这得益于BeanListHandler的内部逻辑,将每条数据都封装成People对象。

我们来看一下输出结果:

picture.image

结果和上面JDBC的一样,但是少了很多代码。学过JavaWeb的都知道,DButils会和DataSource一起使用,例如C3P0等,所以在QueryRunner直接传入DataSource即可,而不是一个connection。这样代码又少了很多。

与JDBC对比一下,ORM的作用体现在:

picture.image

MyBatis

MyBatis更为简单,在Mapper文件中使用注解传入SQL,通过返回值关联Java类,直接调用对应的mapper函数,就能获取对相应的数据,实现ORM。

picture.image 这个方法就是查询数据库中的building_info表,映射成Java的BuildingDataInfo类,每条数据对应一个BuildingDataInfo对象。

picture.image

与DButils相比,MyBatis更为简单,不需要传入Handler,不需要传入数据源,只要调用提前定义好的方法,就能获取到ORM后的数据。

结语

上面就是我对JDBC和ORM的理解,理解不足之处请指正,期待共同学习和进步。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论