ORM全称Object-Relational Mapping,中文翻译对象关系映射,主要实现程序对象到关系数据库数据的映射。
01 什么是ORM
对象关系映射(Object-Relational Mapping),对象为应用程序实例对象,关系为对象与数据库表数据关系。ORM框架作为数据库链接桥梁,通过持久化类与数据库表映射关系,解决面向对象与关系数据库互不匹配现象技术。

02 为什么需要ORM
如果不使用ORM框架开发应用程序,可能存在编写大量数据访问层冗余代码,用于操作数据库保存、删除和读取对象信息。相反,使用ORM则会大大减少重复性代码。
03 ORM演化历程
ORM经历纯JDBC、优化JDBC、Apache DBUtils、Spring JdbcTemplate、Hibernate、Mybatis、Spring Data JPA和MyBatis Plus几个阶段发展历程,ORM框架最终演化企业级应用框架Hibernate和Mybatis。

04 JDBC
4.1 原生JDBC
Java提供DriverManager使用SPI机制加载驱动类进行创建链接对象,数据库链接创建Statement进行SQL操作,获取到结果集ResultSet处理数据。需要注意,无用的链接需要及时关闭避免操作资源浪费。
原始JDBC操作存在一些缺陷,首先,需要大量冗余代码维护链接资源和结果集处理,代码臃肿难以维护。其次,存在很多SQL语句写在业务代码,导致代码耦合性太强,难以维护。
java
public class SqlMain {
public static void main(String[] args) throws Exception {
String jdbcUrl = "";
String jdbcUser = "";
String jdbcPwd = "";
String sql = "select * from user";
// 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPwd);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)
){
// 获取结果集
while (rs.next()) {
Integer id = rs.getInt("id");
String user = rs.getString("user_name");
String address = rs.getString("address");
System.out.printf("{id=%d, user=%s, address=%s}\n", id, user, address);
}
}
}
}
4.2 JDBC优化v1.0
根据JBC冗余特点,封装工具类优化重复代码和数据库链接资源管理。
java
public class DBUtils {
private static final String JDBC_URL = "";
private static final String JDBC_NAME = "";
private static final String JDBC_PASSWORD = "";
private static Connection conn;
// 提供获取数据库连接方法
public static Connection getConnection() throws Exception {
if (conn == null) {
try {
conn = DriverManager.getConnection(JDBC_URL, JDBC_NAME, JDBC_PASSWORD);
} catch (Exception e) {
throw new Exception(e);
}
}
return conn;
}
// 关闭资源
public static void close(Connection conn) {
close(conn, null);
}
public static void close(Connection conn, Statement sts) {
close(conn, sts, null);
}
public static void close(Connection conn, Statement sts, ResultSet rs) {
close0(conn);
close0(sts);
close0(rs);
}
private static void close0(AutoCloseable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
4.3 JDBC优化v2.0
封装DML操作方法,解决SQL耦合问题。
java
public class DBUtils {
// 数据库DML操作
public static Integer update(String sql, Object... params) throws Exception {
conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
int count = ps.executeUpdate();
close(conn, ps);
return count;
}
}
4.4 JDBC优化v3.0
最后引入反射和元数据技术,优化结果集处理代码冗余,以及业务耦合问题。
java
public class DBUtils {
// 查询方法封装
public static <T> List<T> query(String sql, Class<?> clazz, Object... parameter) throws Exception {
conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
if (parameter != null && parameter.length > 0) {
for (int i = 0; i < parameter.length; i++) {
ps.setObject(i + 1, parameter[i]);
}
}
ResultSet rs = ps.executeQuery();
// 获取表结构元数据
ResultSetMetaData metaData = ps.getMetaData();
// 数据结果
List<T> dataList = new ArrayList<>();
// 根据字段名称获取数据值, 封装数据到对象
while (rs.next()) {
Object o = clazz.newInstance();
int columnCount = metaData.getColumnCount();
for (int i = 1; i < columnCount + 1; i++) {
// 根据每列的名称获取对应的值
String columnName = metaData.getColumnName(i);
Object columnValue = rs.getObject(columnName);
setFieldValueForColumn(o, columnName, columnValue);
}
dataList.add((T) o);
}
return dataList;
}
// 根据字段名称设置对象属性值
private static void setFieldValueForColumn(Object o, String columnName, Object columnValue) {
Class<?> clazz = o.getClass();
String fileName = columnName;
// 驼峰处理
if (columnName.contains("_")) {
Pattern linePattern = Pattern.compile("_(\w)");
columnName = columnName.toLowerCase();
Matcher matcher = linePattern.matcher(columnName);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
}
matcher.appendTail(sb);
fileName = sb.toString();
}
try {
// 根据字段获取属性
Field field = clazz.getDeclaredField(fileName);
// 私有属性放开权限
field.setAccessible(true);
field.set(o, columnValue);
field.setAccessible(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
05 Apache DBUtils
Apache DbUtils库旨在简化JDBC使用,通过抽取JDBC资源清理,减少资源没有及时关闭导致链接泄露难以追踪问题。另外,提供自动实现ResultSet填充Java Bean属性,提供简洁干净数据结果处理逻辑。
Apache DBUtils官网链接地址:commons.apache.org/proper/comm...
5.1 初始化配置
Apache DButils提供QueryRunner,封装数据库增删改查获取QueryRunner。自定义封装数据库连接池管理(DataSource)初始化QueryRunner,初始化配置文件为druid.properties
。
groovy
implementation group: 'commons-dbutils', name: 'commons-dbutils', version: '1.8.1'
implementation group: 'com.alibaba', name: 'druid', version: '1.1.14'
properties
druid.username=
druid.password=
druid.url=
druid.minIdle=10
druid.maxActive=30
java
public class DruidUtils {
private static final String PROPERTY_PATH = "druid.properties";
private static DruidDataSource dataSource;
private static QueryRunner queryRunner;
public static void init() {
Properties properties = new Properties();
InputStream in = DruidUtils.class.getClassLoader().getResourceAsStream(PROPERTY_PATH);
try {
properties.load(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
dataSource = new DruidDataSource();
dataSource.configFromPropety(properties);
// 使用数据源初始化QueryRunner
queryRunner = new QueryRunner(dataSource);
}
public static QueryRunner getQueryRunner() {
check();
return queryRunner;
}
public static Connection getConnection() {
check();
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void close(Connection connection) {
try {
if (Objects.nonNull(connection) && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private static void check() {
if (Objects.nonNull(dataSource) || Objects.nonNull(queryRunner)) {
throw new RuntimeException("DataSource has not been init");
}
}
}
5.2 CURD操作
QueryRunner提供解决重复代码问题,ResultSet结果集通过ResultSetHandler处理,额外传入数据源是为了解决资源管理的问题。
java
public class TestCURD {
// 添加用户
@Test
public void addUser() throws SQLException {
DruidUtils.init();
QueryRunner queryRunner = DruidUtils.getQueryRunner();
String sql = "insert into t_user(user_name,real_name)values('aaa','bbbb')";
int i = queryRunner.update(DruidUtils.getConnection(), sql);
System.out.println(i);
}
// 查询所有用户信息
@Test
public void queryUser() throws Exception {
DruidUtils.init();
QueryRunner queryRunner = DruidUtils.getQueryRunner();
String sql = "select * from t_user";
List<User> list = queryRunner.query(sql, new ResultSetHandler<List<User>>() {
// 回调处理方法
@Override
public List<User> handle(ResultSet rs) throws SQLException {
List<User> list = new ArrayList<>();
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setRealName(rs.getString("real_name"));
user.setPassword(rs.getString("password"));
list.add(user);
}
return list;
}
});
for (User user : list) {
System.out.println(user);
}
}
// 通过ResultHandle实现类处理查询
@Test
public void queryUserUseBeanListHandle() throws Exception {
DruidUtils.init();
QueryRunner queryRunner = DruidUtils.getQueryRunner();
String sql = "select * from t_user";
// 不提供驼峰命名转换
List<User> list = queryRunner.query(sql, new BeanListHandler<User>(User.class));
for (User user : list) {
System.out.println(user);
}
}
}
06 Spring JDBC
Spring提供JdbcTemplate封装和简化JDBC操作,内部包含很多堕胎execute
、query
和update
操作数据库SQL处理方法。
6.1 初始化配置
Spring JdbcTemplate需要配置对应数据源,使用Spring IOC容器管理JdbcTemplate对象。
java
@Configuration
@ComponentScan
public class SpringConfig {
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("?");
dataSource.setPassword("?");
dataSource.setUrl("?");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
}
6.2 CURD操作
java
@Repository
public class UserDao {
@Autowired
private JdbcTemplate template;
public void addUser() {
int count = template.update("insert into t_user(user_name,real_name) values (?, ?)", "anan", "安安老师");
System.out.println("count = " + count);
}
public void query0() {
String sql = "select * from t_user";
List<User> list = template.query(sql, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setRealName(rs.getString("real_name"));
return user;
});
for (User user : list) {
System.out.println(user);
}
}
public void query1() {
String sql = "select * from t_user";
List<User> list = template.query(sql, new BeanPropertyRowMapper<>(User.class));
for (User user : list) {
System.out.println(user);
}
}
}
07 Hibernate
Hibernate全称为Object Relative DateBase Mapping,建立Java对象与关系数据库之间映射,实现直接存取Java对象。
Apache DBUtils和Spring JdbcTemplate虽然简化数据库操作,但是提供的能力还是比较简单,缺少缓存、事务管理和日志打印等高阶管理支持,所以实际开发并非直接使用上述技术,而是使用Hibernate和MyBatis这些专业ORM持久层框架。
7.1 项目结构
java
hibernate
|- src
|-- main
|-- java
|-- resources
|- build.gradle
|- setting.gradle
7.2 初始化配置
文件 | 文件路径 |
---|---|
setting.gradle | setting.gradle |
properties
rootProject.name = 'hibernate'
文件 | 文件路径 |
---|---|
build.gradle | build.gradle |
groovy
plugins {
id 'java'
}
group 'net.feiyu'
version '1.0-SNAPSHOT'
dependencies {
// spring boot pom
implementation platform('org.springframework.boot:spring-boot-dependencies:2.3.10.RELEASE')
// spring data jpa
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
// mysql
implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.11'
}
文件 | 文件路径 |
---|---|
hibernate.cfg.xml | resources/hibernate.cfg.xml |
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
com.mysql.cj.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://host:3306/hibernate?characterEncoding=utf8&serverTimezone=UTC
</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
7.3 XML映射
文件 | 文件路径 |
---|---|
hibernate.cfg.xml | resources/hibernate.cfg.xml |
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping resource="User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
文件 | 文件路径 |
---|---|
User.hbm.xml | resources/User.hbm.xml |
java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
'-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping>
<class name="com.feiyu.model.User" table="t_user">
<id name="id"/>
<property name="userName" column="user_name"/>
<property name="realName" column="real_name"/>
</class>
</hibernate-mapping>
java
public class XmlMain {
// Hibernate操作案例
public static void main(String[] args) {
Configuration configuration = new Configuration();
// 使用默认配置: hibernate.cfg.xml
configuration.configure();
// 创建Session工厂
SessionFactory factory = configuration.buildSessionFactory();
// 创建Session
Session session = factory.openSession();
// 获取事务对象
Transaction transaction = session.getTransaction();
// 开启事务
transaction.begin();
// O:对象
User user = new User();
user.setId(666);
user.setUserName("hibernate-1");
user.setRealName("持久层框架");
// 保存
session.save(user);
// 提交事务
transaction.commit();
// 关闭Session
session.close();
}
}
7.4 注解映射
Spring Data JPA提供JpaRepository
实现持久层框架统一封装,底层基于Hibernate进行处理。
文件 | 包路径 |
---|---|
User.java | com.feiyu.model |
java
@Data
@Entity
@Table(name = "t_user")
public class User {
@Id
@Column(name = "id")
private Integer id;
@Column(name = "user_name")
private String userName;
@Column(name = "real_name")
private String realName;
@Column(name = "password")
private String password;
@Column(name = "age")
private Integer age;
@Column(name = "i_id")
private Integer dId;
}
文件 | 包路径 |
---|---|
UserDao.java | com.feiyu.dao.UserDao |
java
public interface UserDao extends JpaRepository<User, Integer> {
}
7.5 总结
特点 | 内容 |
---|---|
优点 | 1. 根据数据库方言自动生成SQL,移植性好 2. 自动管理连接资源 3. 实现对象和关系型数据的完全映射,操作对象与操作数据库记录一致 4. 提供缓存机制 |
缺点 | 1. Hibernate API无法指定部分字段操作,比如get 、update 和save 方法 2. 自定生成SQL方式,如果要基于SQL做一些优化也是非常困难的。 3. 不支持动态SQL,比如无法基于表名、条件和参数实现自动生成SQL,也就我无法分表 |
08 MyBatis
MyBatis前身来自于ibatis,ibatis来源internet和abatis ['æbətɪs](障碍物)两个单词组合。2001开始研发ibatis,2004年捐赠给Apache,2010年正是更名为MyBatis。
MyBatis属于半自动ORM持久层框架,封装程度没有Hibernate那么高,解决Hibernate不能支持动态SQL和无法指定部分字段操作问题。主要添加自定义SQL、存储过程和高级映射支持,通过XML或者注解简单实现配置映射原始类型、接口和Java POJO(Plain Old Java Objects,普通Java对象)映射数据库记录,免除几乎所有JDBC代码、设置参数和获取结果集处理。
MyBatis官网地址:mybatis.org/mybatis-3/z...
特点 | 内容 |
---|---|
优点 | 1. 使用连接池对连接进行管理 2. SQL和代码分离,集中管理 3. 结果集映射 4. 参数映射和动态SQL 5. 重复SQL的提取 6. 缓存管理 7. 插件机制 |
09 MyBatis-Plus
MyBatis Plus增强原生MyBatis打造的高阶工具,在原生MyBatis功能基础上附加plus特有功能。
MyBatis Plus功能 | 内容 |
---|---|
通用CRUD | 业务接口Mapper继承BaseMapper,无需编写任何接口方法与配置文件,即可获得通用增删改查功能 |
条件构造器 | 通过实体包装类EntityWrapper拼接SQL语句,支持排序、分组查询等复杂SQL |
代码生成器 | 支持一系列策略配置与全局配置,相比MyBatis代码生成更好用 |
MyBatis Plus官方地址:mybatis.plus/guide