专业名词
说到 Java 访问数据库的方法,就会涉及到非常多的新词,比如:
- JDBC
- HikariCP
- HSQLDB
- DAO
- Hibernate
- JPA
- MyBatis
作为一个刚学 Java 不到一周的萌新,我看到这堆词的时候直接懵掉了,不知道从哪里开始学起,甚至自闭了几个小时。
所以,我准备先简单讲一下这几个词的概念。
- JDBC(Java DataBase Connection):Java 访问数据库的 API,只提供接口,底层驱动由 MySQL 等提供。绝大部分 Java 访问数据库的包都是基于 JDBC 进行二次开发
- HikariCP:一个数据库连接池(Connection Pool),用于和数据库建立高效、可复用的连接
- HSQLDB:一个 Java 编写的嵌入式数据库,类似于 C/C++ 的 SQLite,可用于测试或小型应用
- DAO(Data Access Object):一种编程模式/思想,对于每一个存储在数据库的类(如
User
)建立一个类,专门负责访问数据库、对数据库进行 CURD - ORM(Object-Relational Mapping):将对象和关系进行映射的过程。下面三个
Hibernate
JPA
MyBatis
其实都是在做 ORM - JPA(Java Persistence API):一个 ORM 标准 API,只提供接口,类似于 JDBC。
- Hibernate:一个 ORM 框架,能够自动将查询语句映射到 SQL,并将查询结果映射为 Java 对象(Python 的 Django 也提供了类似的 ORM 功能)
- MyBatis:一个 ORM 框架,可以将查询结果映射为 Java 对象,但还是需要手写 SQL
JDBC
JDBC:万物之源。
JDBC 的全称是 Java 数据库连接 (Java DataBase Connectivity),它是 Java 为关系数据库定义了一套标准的访问接口。
JDBC 是接口,这个接口意味着,虽然不同数据库的访问方法不一样,但是不同应用程序(包括不同框架)访问 JDBC 接口、数据库厂商为 JDBC 实现自己的接口,就可以连接应用程序和数据库了。
目前几乎所有 Java 程序都是使用 JDBC 访问数据库,包括 JetBrains 的所有 IDE 在链接数据库前,也需要下载对应数据库的 JDBC 驱动。
更详细的使用的教程可见JDBC编程 - 廖雪峰的官方网站。
HikariCP
HikariCP 是一个数据库连接池,负责自动管理数据库连接,提高性能。这个没什么好讲的,在高并发下,连接池是标配,Spring Boot 也优先选择 HikariCP 作为连接池。
HSQLDB
1 | List<User> users = userOrm.query(gender="M", grade=3); |
HSQLDB 其实和 SQLite 是类似的,都是一个小型的、嵌入式的、可以运行于内存或文件的数据库,不需要单独安装,只需要编译的时候将包导入即可。
HSQLDB 和 SQLite 的区别是,HSQLDB 是 Java 写的,而 SQLite 是 C 写的,所以 Java 项目导入 HSQLDB 很方便,而 C++ 项目导入 SQLite 会很方便。
HSQLDB 和 SQLite 都常用于开发、测试环境、中小型系统中。
需要注意的是,MySQL 默认使用可重复读,而 HSQLDB 2 不支持可重复读,默认是读提交 (reference)。所以 HSQLDB 会出现 A 更新但未提交后、B 事务申请读但是卡死的情况。
DAO
数据访问对象 (Data Access Object, DAO) 只是一种设计模式,不需要引入新的工具包。
如果不使用 DAO 的话,我们会把访问数据库的逻辑写到业务层里,如果业务逻辑变复杂,就很难管理。
因此,我们可以把数据访问层和控制层进行分离。
如果我们使用面向对象编写数据访问层,就有了数据访问对象 (Data Access Object, DAO) 了。
1 | public class UserDao { |
ORM
ORM(Object-Relational Mapping),对象关系映射,就是将 Java 原生对象和关系数据库的数据建立关系的一种思想。
纯 JDBC 是没有 ORM 的,所以需要手动取出 resultSet
中的每个字段,然后处理,非常麻烦。
下面是使用 JDBC 查询数据库,JDBC 没有 ORM,需要手动将对象字段映射为 SQL 查询条件、将 SQL 查询结果映射为对象。
1 | try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) { |
如果有 ORM(运用现有的 ORM 框架,或者手写 ORM)自动把 resultSet
映射到 Java 原生类,在开发上都会变得简单很多。
1 | // 理想的 ORM |
Java 的常见 ORM 框架有:
- JPA(Java Persistence API):一个 ORM 标准 API,只提供接口,类似于 JDBC。
- Hibernate:一个 ORM 框架,能够自动将查询语句映射到 SQL,并将查询结果映射为 Java 对象
- MyBatis:一个 ORM 框架,可以将查询结果映射为 Java 对象,但还是需要手写 SQL
下面会分别介绍。
JPA
JPA(Java Persistence API Java 持久层 API) 是 JavaEE 的 ORM 标准,大家可以用这个标准提供的接口以 ORM 的形式访问数据库。
值得注意的是,JPA 只是提供了接口,底层的实现不是 JPA 做的,可以使用 EclipseLink 或 Hibernate(没错,你不仅可以使用 Hibernate API 调用 Hibernate,也可以使用 JPA 调用)作为实现。
简单的操作用 JPA 写会非常简单,比如按 id 查询用户:
1 | public class User { |
但是复杂的操作看起来就很麻烦了,SELECT * FROM user WHERE email = ?
用 JPA 写:
1 | public User fetchUserByEmail(String email) { |
还不如写 SQL 呢。所以,一般来说不会选择这个 API 作为 ORM 的工具。
Hibernate
相较于 JPA,Hibernate API 就友好很多了。
上面的 SELECT * FROM user WHERE email = ?
用 Hibernate 可以这么写:
1 | public User fetchUserByEmail(String email) { |
相较 JPA 就简单很多了。
顺便一提,如果使用的是 Django 的 ORM,SELECT * FROM user WHERE email = ?
可以这么写:
1 | def fetchUserByEmail(email : str) -> User: |
可以看到,Hibernate 和 JPA 可以将查询条件自动转换为 SQL,同时自动将查询结果返回为 Java 对象,而 Hibernate 的语法较 JPA 简单了很多。
但是,Hibernate 为了兼容多种数据库,它使用 HQL 或 JPQL 查询,经过一道转换,变成特定数据库的 SQL,理论上这样可以做到无缝切换数据库,但这一层自动转换除了少许的性能开销外,给 SQL 级别的优化带来了麻烦。
此外,如果有大量表连接操作,不直接用 SQL 写的话,语法上也会非常麻烦。
所以,产生了另一种框架,需要手写 SQL,但是能够将结果自动转换为 Java 对象,MyBatis 就是这么一个 ORM 框架。
MyBatis
MyBatis 虽然需要手写 SQL,但是他的语法也相当简洁:
1 | public interface UserMapper { |
写好这个接口以后,MyBatis 甚至可以帮我们创建实现类,我们直接调用就行了:
1 | User user = userMapper.getByEmail(email); |
可见,MyBatis 的优势有:
- (相较于 JDBC)自动将 ResultSet 转化为 Java 对象
- (相较于 Hibernate 和 JPA)执行原生 SQL,不会有效率影响
- (相较于 JDBC)自动生成查询函数,语法简洁
Mybatis 的三种风格
Mybatis 也有三种风格:
- 注解风格,将 SQL 写在接口方法的注解里;
- XML 风格,将 SQL 语句单独放到一个 XML 文件里;
- MyBatis-Plus 插件,类似 Hibernate API,提供通用的 API,能方便地对单表增删查改。
这三种方案使用哪一种,就见仁见智了。如果使用注解风格,在看方法名的时候顺便就能看到实际的 SQL;使用 XML 风格实现了 SQL 层和 Java 代码的分离;MyBatis Plus 则是类似 Hibernate,可能会有效率问题。
1 | <!-- 使用 XML 配置的 MyBatis Mapper --> |
1 | // 使用注解配置的 MyBatis Mapper |
1 | // 使用 MyBatis Plus 配置的 MyBatis Mapper |
MyBatis Generator 自动生成 XML
没错,MyBatis 官方也提供了生成器,能根据数据库表,自动生成实体类 (User) 和映射类 (UserMapper)!
英文官网:http://mybatis.org/generator/index.html
参考:https://juejin.cn/post/6844903982582743048
1 | <!-- pom.xml --> |
1 | <!-- src/main/resources/mybatis-generator-config.xml --> |
目前 mybatis-generator-config.xml
只支持从 properties
读取数据,因此还要把 application.yaml
改回 application.properties
(很多在线工具):
1 | server.port=8080 |
然后在 Idea 侧边栏运行 Maven
- Plugins
- mybatis-generator:generate
即可。