前言
初学ORM和JDBC还是2016年的10月15日,在宿舍里偷偷记下了一纸笔记。
转眼七年已过,从来没有手写过JDBC,数据库的连接都是通过框架和连接池就给完成了,ORM的工作框架也都顺手给完成了。
本篇文章就用七年后角度,再次学习JDBC和ORM。
JDBC
JDBC即Java数据库连接,是Java提供的一套连接数据库的接口规范,开发者可以通过模板代码来连接不同的数据库。这里就拿MySQL和oracle来举例,画了一个简单的图。
我们可以通过右边java.sql提供的JDBC流程,只要将对应数据库驱动注册到DriverManager中,就能实现对数据库的连接,然后获取连接对象、执行sql、返回结果集。这些在java.sql包里都提供了对应的类。
建表
安装数据库的文章之前有写过,这里直接建表。
一共三个字段,两个varchar字符串类型,一个int类型,然后插入数据。
代码实现
这里先定义一个和表字段一样的Java类。
这里就以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开始的。
运行输出结果:
数据表中的三条数据都被查询到并输出了出来。接下来就从头分析一下JDBC流程。
JDBC流程
1. 加载驱动
JDBC中通过Class.forName反射加载驱动类。com.mysql.cj.jdbc.Driver 是MySQL的驱动类,在MySQL的依赖包里,当没有添加MySQL依赖时,就会报错ClasNotFound的异常。
上图为MySQL驱动类源码,继承NonRegisteringDriver并实现了JDBC的Driver接口,在加载类的同时,也会调用static代码块,调用DriverManger的registerDriver方法,向DriverManger注册此驱动。
registeredDrivers是一个CopyOnWriteArrayList,即线程安全的List,记录着各个Driver的信息,addIfAbsent只有当这个driver不存在时,则会添加进去,保证每个数据库的驱动信息只会加载一次。
2. 数据库连接
DriverManger记录着所有驱动的信息,并通过getConnection() 连接数据库并返回Connection对象。代码如下:
aDriver就是存放在registeredDrivers中的Driver信息,这里指的就是MySQL驱动,然后调用此驱动的connect方法连接数据库,看看NonRegisteringDriver类中connect代码。
里面就是所有通过url、user、password信息连接MySQL数据库的逻辑实现,最后返回一个Connection对象。通过断点可以知道,最后返回的是MySQL驱动的ConnectionImpl类。
所以说,JDBC的DriverManger只是提供了管理Driver以及连接数据库的入口(connect方法),至于连接数据库的细节,需要每个数据库的驱动类自己实现细节,只要最后按照JDBC规范,返回一个Connection即可。
3. 执行查询SQL
使用连接对象createStatement方法创建一个Statement对象,即用来直行SQL的一个对象。通过断点可以看到,从MySQL数据库连接中返回的是MySQL实现的StatementImpl对象。
然后调用executeQuery()来执行查询SQL。
最后返回结果集ResultSet,其实返回的是MySQL驱动中自己实现的ResultSet子类ResultSetInternalMethods。
最后我们根据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对象。
我们来看一下输出结果:
结果和上面JDBC的一样,但是少了很多代码。学过JavaWeb的都知道,DButils会和DataSource一起使用,例如C3P0等,所以在QueryRunner直接传入DataSource即可,而不是一个connection。这样代码又少了很多。
与JDBC对比一下,ORM的作用体现在:
MyBatis
MyBatis更为简单,在Mapper文件中使用注解传入SQL,通过返回值关联Java类,直接调用对应的mapper函数,就能获取对相应的数据,实现ORM。
这个方法就是查询数据库中的building_info表,映射成Java的BuildingDataInfo类,每条数据对应一个BuildingDataInfo对象。
与DButils相比,MyBatis更为简单,不需要传入Handler,不需要传入数据源,只要调用提前定义好的方法,就能获取到ORM后的数据。
结语
上面就是我对JDBC和ORM的理解,理解不足之处请指正,期待共同学习和进步。