编程语言和底层数据库的发展不协调,催生出了ORM框架,ORM框架可作为面向对象编程语言和数据库之间的桥梁,确切地说,MyBatis并不完全是一种ORM框架,它的设计思想和ORM相似,只是它允许开发人员直接编写SQL语句,使得访问数据库更 加灵活,更准确地说,它应该是一种“SQL Mapping”框架

对象/关系数据库映射(ORM)

ORM的全称是Object/Relation Mapping,即对象/关系数据库映射。 可以将ORM理解成一种规范,它概述了这类框架的基本特征,完成面向对象的编程语言到关系数据库的映射。当ORM框架完成映射后,程序员既可以利用面向对象程序设计语言的简单易用性,又可以利用关系 数据库的技术优势。因此可以把ORM当成应用程序和数据库的桥梁

当使用一种面向对象的编程语言来进行应用开发时,从项目一开始 就采用的是面向对象分析、面向对象设计、面向对象编程,但到了持久层数据库访问时,又必须重返关系数据库的访问方式,这是一种非常糟糕的感觉。于是人们需要一种工具,它可以把关系数据库包装成面向对象的模型,这个工具就是ORM

ORM框架是面向对象程序设计语言与关系数据库发展不同步时的 中间解决方案。随着面向对象数据库的发展,其理论逐步完善,最终面向对象数据库会取代关系数据库。只是这个过程不可一蹴而就,ORM 框架在此期间会蓬勃发展。但随着面向对象数据库的广泛使用,ORM 工具会逐渐消亡

对于时下所有流行的编程语言而言,面向对象的程序设计语言代表 了目前程序设计语言的主流和趋势,其具备非常多的优势。比如:

  • 面向对象的建模、操作
  • 多态、继承
  • 摒弃难以理解的过程,简单易用,易理解

但数据库的发展并未能与程序设计语言同步,而且关系数据库系统的某些优势也是面向对象语言目前无法比拟的。比如:

  • 大量数据查找、排序
  • 集合数据连接操作、映射
  • 数据库访问的并发、事务
  • 数据库的约束、隔离

面对这种面向对象语言与关系数据库系统并存的局面,采用ORM 就变成一种必然。只要依然采用面向对象程序设计语言,底层依然采用关系数据库,中间就少不了ORM工具。采用ORM框架之后,应用程序 不再直接访问底层数据库,而是以面向对象的方式来操作持久化对象 (例如创建、修改、删除等),而ORM框架则将这些面向对象的操作 转换成底层的SQL操作,如下图所示

微服务[v1.0.0][MyBatis入门]_sql


ORM工具的唯一作用就是:把对持久化对象的保存、修改、删除等操作,转换成对数据库的操作。由此,程序员可以以 面向对象的方式操作持久化对象,而ORM框架则负责将相关操作转换 成对应的SQL(结构化查询语言)操作


基本映射方式

ORM工具提供了持久化类和数据表之间的映射关系,通过这种映 射关系的过渡,程序员可以很方便地通过持久化类实现对数据表的操 作。实际上,所有的ORM工具大致都遵循相同的映射思路。ORM有如 下几条基本映射关系:

数据表映射类:持久化类被映射到一个数据表。程序使用这个持 久化类来创建实例、修改属性、删除实例时,系统自动会转换为对这个 表进行CRUD操作,如下图所示

微服务[v1.0.0][MyBatis入门]_sql_02


受ORM管理的持久化类(就是一个普通Java类)对应一个数据表,只要程序对这个持久化类进行操作,系统就可以将其 转换成对对应数据库表的操作


数据表的行映射对象(即实例):持久化类会生成很多实例,每 个实例就对应数据表中的一行记录。当程序在应用中修改持久化类的某 个实例时,ORM工具会将其转换成对对应数据表中特定行的操作。每个持久化对象对应数据表的一行记录的示意图,如下图所示

微服务[v1.0.0][MyBatis入门]_数据库_03

数据表的列(字段)映射对象的属性:当程序修改某个持久化对象的指定属性时(持久化实例映射到数据行),ORM会将其转换成对 对应数据表中指定数据行、指定列的操作。数据表的列被映射到对象属 性的示意图,如下图所示

微服务[v1.0.0][MyBatis入门]_持久化_04

基于这种基本的映射方式,ORM工具可完成对象模型和关系模型 之间的相互映射。由此可见,在ORM框架中,持久化对象是一种媒 介,应用程序只需操作持久化对象,ORM框架则负责将这种操作转换 为底层数据库操作。这种转换对开发者透明,开发者无须关心内部细 节,从而将开发者从关系模型中解放出来,使得开发者能以面向对象的 思维操作关系数据库

流行的ORM框架简介

  • JPA:JPA本身只是一种ORM规范,并不是ORM产品。它是Java EE规范制定者向开源世界学习的结果。相对于其他开源 ORM 框架, JPA 的最大优势在于它是官方标准,因此具有通用性。如果应用程序是 面向JPA编程,那么应用程序就可以在各种ORM框架之间自由切换
  • Hibernate:目前最流行的开源ORM框架,已经被选为JBoss的持 久层解决方案。整个Hibernate项目也一并投入了JBoss的怀抱,而JBoss 又加入了Red Hat组织。因此,Hibernate属于Red Hat组织的一部分。 Hibernate灵巧的设计、优秀的性能,以及丰富的文档,都是其风靡全球 的重要因素
  • MyBatis(早期名称是iBATIS):Apache 软件基金组织的子项 目。与其称它是一种ORM框架,不如称它是一种“SQL Mapper”框架, 它是一种“半自动化”的ORM的实现,曾经在Java EE开发中扮演非常重 要的角色,但是其并不支持纯粹的面向对象操作,它允许开发人员直接 编写SQL语句,更加灵活

MyBatis概述

MyBatis是一个支持普通SQL查询、存储过程和高级映射的优秀持久层框架,它去掉了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装,MyBatis可以使用简单的XML或注解进行配置和原始映射,将Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

MyBatis作为持久层框架,其主要思想是将程序中的大量SQL语句剥离出来,配置在配置文件中,以实现SQL的灵活配置。这样做的好处是将SQL与程序代码分离,做到可以在不修改程序代码的情况下,直接 在配置文件中修改SQL

实际上,目前主流的ORM,无论Hibernate还是JPA,都对数据库结构提供了较为完整的封装,提供了从POJO(Plain Old Java Objects)到数据库表的全套映射机制,开发者只需定义好POJO到数据库表的映射 关系,即可通过Hibernate或者JPA 提供的方法完成持久层操作,甚至不需要熟练掌握SQL,Hibernate/JPA会根据制定的存储逻辑,自动生成对应的SQL并调用JDBC接口加以执行

大多数情况下(特别是对新项目、新系统的开发而言),这样的机制无往不利,但在一些特定的环境下, Hibernate这种一站式的解决方案却未必适合。例如:系统的部分或全部数据来自现有数据库,出于安全考虑,只对开发团队提供几条Select SQL(或存储过程)以获取所需数据,具体的表结构不予公开

开发规范中要求,所有牵涉业务逻辑部分的数据库操作,必须在数据库层由存储过程实现(就金融行业而言,工商银行、中国银行、交通 银行等商业银行都曾在开发规范中严格指定)

系统数据处理量巨大,性能要求极为苛刻,这往往意味着我们必须通过经过高度优化的SQL语句(或存储过程)才能达到系统性能设计指标。

面对这样的需求,Hibernate不再适合解决上述问题,甚至无法使用它。此时,直接使用JDBC进行数据库操作实际上也是不错的选择,只是拖沓的数据库访问代码、乏味的字段读取操作令人厌烦,而“半自动 化”的MyBatis,却正好解决了这个问题

这里的“半自动化”是相对Hibernate等提供了全面的数据库封装机制 的“全自动化”ORM实现而言的,“全自动”ORM 实现了POJO和数据库表之间的映射,以及 SQL 的自动生成和执行。而MyBatis的着力点,则 在于POJO与SQL之间的映射关系。也就是说,使用MyBatis提供的ORM机制,对业务逻辑实现人员而言,面对的是纯粹的 Java 对象,这 一点与通过Hibernate实现ORM而言基本一致。而对于具体的数据操

作,Hibernate会自动生成SQL 语句,而MyBatis则并不会为程序员在运行期间自动生成SQL。具体的SQL需要程序员编写,然后通过映射配置文件,将SQL所需的参数及返回的结果字段映射到指POJO

相对Hibernate等“全自动”ORM机制而言,MyBatis以 SQL开发的工作量和数据库移植性上的让步,为系统设计提供了更大的自由空间。作为“全自动”ORM实现的一种有益补充,MyBatis的存在具有特别的意

MyBatis初探

MyBatis的用法非常简单,我们只要在Java项目中引入MyBatis框 架,就能以面向对象的方式操作关系数据库

下载与安装

​MyBatis​​​站点,即可在页面上找到下载的超链接,单击该超链接即可开始下载MyBatis的压缩包,或者直接点击​​mybatis-3.5.6​​直接下载3.5.6版本,解压缩刚下载的压缩包,mybatis-x.x.x的文件 夹,该文件夹下包含如下文件结构:

  • mybatis-x.x.x.jar:MyBatis的核心类库
  • mybatis-x.x.x.pdf:MyBatis的参考文档
  • mybatis-x.x.x-javadoc.jar:MyBatis的API文档
  • mybatis-x.x.x-sources.jar:MyBatis的源代码
  • lib:MyBatis所依赖的第三方jar包

将解压缩路径中的 mybatis-x.x.x.jar 包添加到应用程序的类加 载路径中,既可以通过添加环境变量的方式来添加,也可以使用Ant或 IDE工具来管理应用程序的类加载路径,如果直接在控制台编译使用了MyBatis API的类,则需要将mybatis-x.x.x.jar包位置添加到CLASSPATH里。如果使用Ant或者Eclipse 等IDE工具,则无须修改环境变量


由于 MyBatis 的底层依然是基于 JDBC 的,因此在应用程序中使用MyBatis执行持久化时同样少不了JDBC驱动,例如程序底层采用了MySQL数据库,因此还需要将MySQL数据库驱动添加到应用程序的类 加载路径中


Mybatis实操

新建一个项目MyBatisQs,为了让Web应用具有MyBatis支持的功 能,将 mybatis-x.x.x解压文件夹下mybatis-x.x.x.jar和lib文件夹下所有 MyBatis框架所依赖的第三方jar包复制到Web应用的lib文件夹下,也就是​​MyBatisQs\WebContent\WEB-INF\lib​​路径下,然后在MySQL数据库中创建一个新的数据库mybatis并新建一个tb_user表,建库见表语句大致如下

create database mybatis;
user mybatis;
DROP TABLE IF EXISTS tb_user;
CREATE TABLE tb_user(
ID INT(11) PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(18) DEFAULT NULL,
SEX CHAR(2) DEFAULT NULL,
AGE INT(11) DEFAULT NULL,
);

在所有的 ORM 框架中都有一个非常重要的媒介:PO(持久化对象),持久化对象的作用就是完成持久化操作,简单地说,就是通过该对象对数据库执行增、删、改的操作,以面向对象 的方式操作数据库;应用程序无须直接访问数据库,甚至无须理会底层数据库采用何种 数据库,这一切对应用程序完全透明,应用程序只需创建、修改、删除 持久化对象即可;与此同时,MyBatis则负责把这种操作转换为对指定

数据库表的操作

MyBatis中的PO是非常简单的,其是非侵入式的设计,完全采用普通的Java对象作为持久化对象使用。下面即是一个POJO(普通的、传统的Java对象)类,如下代码所示

package org.davieyang.domain;

import java.io.Serializable;


public class User implements Serializable{

private static final long serialVersionUID = 1L;

// 用户标识
private Integer id;
// 用户姓名
private String name;
// 用户性别
private String sex;
// 用户年龄
private Integer age;

// 无参数构造器
public User() {
super();
}
// 有参数构造器
public User( String name, String sex, Integer age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
// setter和getter方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}

这个类与普通的JavaBean没有任何区别。实际上,MyBatis直接采用了POJO(普通的、传统的Java对 象)作为持久化类,这就是MyBatis被称非低侵入式设计的原因。 MyBatis不要求持久化类继承任何父类,或者实现任何接口,这样可保证代码不被污染

对于MyBatis来说,现在还不理解持久化类User和数据库表之间的 对应关系,也不理解持久化类的属性与数据表的各个列之间的对应关 系。MyBatis是通过XML文件去完成持久化类和数据库表之间的映射关 系的,代码如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace指用户自定义的命名空间。 -->
<mapper namespace="org.davieyang.mapper.UserMapper">

<!--
id="save"是唯一的标示符
parameterType属性指明插入时使用的参数类型
useGeneratedKeys="true"表示使用数据库的自动增长策略
-->
<insert id="save" parameterType="org.fkit.domain.User" useGeneratedKeys="true">
INSERT INTO TB_USER(name,sex,age)
VALUES(#{name},#{sex},#{age})
</insert>


</mapper>

上面的XML配置中定义了一条insert语句,详细解释如下:

  • ​<mapper namespace="org.davieyang.mapper.UserMapper">​​​,为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包 名+SQL映射文件名,这样就能够保证namespace的值是唯一的,例如​​namespace=“org.davieyang.mapper.UserMapper”​​​就是​​org.davieyang.mapper(包名) +UserMapper (UserMapper.xml文件去除后缀)​
  • 在insert标签中编写了SQL插入语句,设置insert标签的id属性值为save,id属性值必须是唯一的,不能够重复
  • 使用parameterType属性指明插入时使用的参数类型
  • 使用useGeneratedKeys="true"表示使用数据库的自动增长策略,这需要底层数据库的支持
  • insert标签中只有一条标准的insert语句,用来向TB_USER表插入一条数据,#{name}表示取参数中的对象的name属性值

接下来,通过这个持久化类来完成对数据库的操作:插入一条数据,对于MyBatis来说,现在还不知道需要连接哪个数据库,以及连接 数据库时所用的连接池、用户名和密码等详细信息。这些信息对于所有

的持久化类都是通用的,MyBatis把这些通用信息称为根配置信息,根 配置信息需要使用配置文件指定,代码如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- XML 配置文件包含对 MyBatis 系统的核心设置 -->
<configuration>
<!-- 指定 MyBatis 所用日志的具体实现 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="mysql">
<!-- 环境配置,即连接的数据库。 -->
<environment id="mysql">
<!-- 指定事务管理类型,type="JDBC"指直接简单使用了JDBC的提交和回滚设置 -->
<transactionManager type="JDBC"/>
<!-- dataSource指数据源配置,POOLED是JDBC连接对象的数据源连接池的实现。 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- mappers告诉了MyBatis去哪里找持久化类的映射文件 -->
<mappers>
<mapper resource="org/davieyang/mapper/UserMapper.xml"/>
</mappers>
</configuration>
  • MyBatis根配置文件默认被命名为​​mybatis-config.xml​​,应用程序运行时需要先加载该文件,该文件第一行是XML文件声 明,指定该XML文件的版本和存储该文件所用的字符集
  • MyBatis配置文件的根元素是​​<configuration.../>​​​,根元素中有​​<settings.../>​​​子元素,该元素有很多子元素,本示例只是配置了日志信息,之后可以在控制台看到输出的SQL语句,其在调试中非常有用。根 元素中还有​​<environments.../>​​​子元素,用来配置MyBatis的环境,即连接的数据库,该子元素用于将 SQL映射应用于多种数据库中。每个数据库对应一个​​SqlSessionFactory​​​,可以配置多种环境,但只能为​​SqlSessionFactory​​实例选择一个环境,default属性表示选择的环境。
  • ​< environment.../>​​​子元素用于配置一个环境,​​<transactionManager.../>​​​子元素用来配置MyBatis当中的事务管理,JDBC属性表示直接简单使用JDBC的提交和回滚设置。​​<dataSource.../>​​​子元素用来配置数据源, MyBatis并不推荐采用DriverManager来连接数据库,而是推荐使用数据源来管理数据库连接,这样能保证最好的性能。该元素依次有很多​​< property.../>​​​子元素,这些​​<property.../>​​子元素用于配置MyBatis连接 数据库的必要信息,如连接数据库的驱动、URL、用户名、密码等信息


数据源是一种用来提高数据库连接性能的常规手段,数据源会负责维持一个数据库连接池,当程序创建数据源实例时,系统会一次性地创 建多个数据库连接,并把这些数据库连接保存在连接池中。当程序需要 进行数据库访问时,无须重新获得数据库连接,而是从连接池中取出一 个空闲的数据库连接,当程序使用数据库连接访问数据库结束后,无须 关闭数据库连接,而是将数据库连接归还给连接池即可。通过这种方式,就可避免频繁地获取数据库连接、关闭数据库连接所导致的性能下降



根元素中还有​​<mappers.../>​​​子元素,它可以支持多个​​<mapper.../ >​​​子元素,每个​​<mapper.../>​​子元素用于指定一个持久化配置文件


package org.fkit.test;

import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.fkit.domain.User;

public class MyBatisTest {

public static void main(String[] args) {
// 创建Session实例
SqlSession sqlSession = null;
try (// 读取mybatis-config.xml文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
){
// 初始化mybatis,创建SqlSessionFactory类的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(is);
sqlSession = sqlSessionFactory.openSession();
// 创建User对象
User user = new User("admin", "男", 26);
// 插入数据
sqlSession.insert("org.fkit.mapper.UserMapper.save", user);
// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 回滚事务
sqlSession.rollback();
e.printStackTrace();
}finally{
try {
// 关闭sqlSession
if(sqlSession != null) sqlSession.close();
} catch (Exception e) {
e.printStackTrace();
}
}

}

}

上面持久化操作的代码非常简单。程序先创建一个User对象,再使 用SqlSession的insert()方法来保存User对象即可,这是完全对象化的操作方式,可以说非常简单明了。当Java程序以面向对象的方式来操作 持久化对象时,MyBatis负责将这种操作转换为底层SQL操作。

执行持久化操作之前,为了查看控制台输出的SQL语句,需要加入 日志框架LOG4J的相关jar包,该jar包在mybatis-x.x.x解压文件夹下的lib 文件夹中可以找到。并在CLASSPATH下增加一个log4j.properties文件,如下代码所示

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.org.fkit.mapper.UserMapper=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] %m%n

运行MyBatisTest 类的main()方法,运行完成后,可以看到 mybatis数据库中的TB_USER表中包含了User实例对应的记录

微服务[v1.0.0][MyBatis入门]_Mybatis介绍_05

同时,在控制台可以观察到执行时的日志信息,其中包括了MyBatis所执行的SQL语句

DEBUG [main]==>Preparing:INSERT INTO TB_USER(name, sex, age) VALUES(?,?,?)
DEBUG [main]==>Parameters:admin(String), 男(String), 26(Integer)
DEBUG [main]==>Updates:1

开发者可以通过日志信息了解MyBatis的执行过程,正如上面程序中代码所示,在执行​​session.insert(" org.fkit.mapper.UserMapper.save",user)​​之前,先要获取SqlSession对象,PO只有在SqlSession的管理下才可完成数据库访问

为了使用 MyBatis进行持久化操作,通常需要执行如下操作步骤:

  • 开发持久化类PO和编写持久化操作的Mapper.xml,在其中定义要执行的SQL语句
  • 获取SqlSessionFactory
  • 获取SqlSession
  • 用面向对象的方式操作数据库
  • 关闭事务,关闭SqlSession


对PO的操作必须在SqlSession的管理下才能同步到数据库,SqlSession由SqlSessionFactory工厂产生,SqlSessionFactory是数据库编译后的内存镜像,通常一个应用对应一个SqlSessionFactory对象,SqlSessionFactory对象通过加载​​mybatis-config.xml​​配置文件生成



程序使用MyBatis添加了一条记录,对比MyBatis和JDBC两种操作方式,不难发现MyBatis的两个显著优点:

  • 只需要在Mapper.xml配置文件中编写SQL语句,在应用程序中就可 以采用OO方式来访问数据库
  • 在JDBC访问过程中大量的checked异常被包装成MyBatis的Runtime 异常,从而不再要求程序必须处理所有异常