程序员应该将核心关注点放在业务上,而不应该将时间过多的浪费在CRUD中,多数的ORM框架都把增加、修改与删除做得非常不错了,然后数据库中查询无疑是使用频次最高、复杂度大、与性能密切相关的操作,我们希望得到一种使用方便,查询灵活的ORM框架,MyBatis可以满足这些要求,MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架,它也是SSM框架集成中的重要组成部分。
一、ORM
1.1、ORM简介
ORM可以解决数据库与程序间的异构性,比如在Java中我们使用String表示字符串,而Oracle中可使用varchar2,MySQL中可使用varchar,SQLServer可使用nvarchar。
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象与关系数据库相互映射。
没有ORM时我们是这样完成对象与关系数据库之间的映射的:
//将执行的sql String sql = "SELECT name, id, age, password FROM users"; //创建命令对象 preparedStatement = connection.prepareStatement(sql); //执行并获得结果集 resultSet = preparedStatement.executeQuery(); //遍历结果集,将数据库中的数据转换成Java中的对象 while(resultSet.next()){ String name = resultSet.getString("name"); int id = resultSet.getInt("id"); int age = resultSet.getInt("age"); String password = resultSet.getString("password"); User entity= new User(name,id,age,password); Users.add(entity); }
这种方案存在以下不足:
持久化层缺乏弹性。一旦出现业务需求的变更,就必须修改持久化层的接口
持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,都要修改持久化曾的相关程序代码,增加了软件的维护难度。
将和数据库交互(CRUD)的代码硬编码到JDBC程序中
实现见状的持久化层需要高超的开发技巧,而且编程量很大
对象模型和关系模型的转换非常麻烦
ORM(O/R Mapping:对象关系映射):
一种将内存中的对象保存到关系型数据库中的技术
负责实体域对象的持久化,封装数据库访问细节
ORM提供了实现持久化层的另一种模式,采用映射元数据(XML)来描述对象-关系的映射细节,使得ORM中间件能在任何一个Java应用的业务逻辑层和数据库之间充当桥梁。
ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。
Java典型的ORM中有:
hibernate:全自动的框架,强大、复杂、笨重、学习成本较高
Mybatis:半自动的框架(懂数据库的人 才能操作) 必须要自己写sql
JPA:JPA全称Java Persistence API、JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,是Java自带的框架
ORM的方法论基于三个核心原则:
· 简单:以最基本的形式建模数据。
· 传达性:数据库结构被任何人都能理解的语言文档化。
· 精确性:基于数据模型创建正确标准化了的结构。
1.2、ORM的概念
让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。你在DAL中写了很多的方法来读取对象数据,改变状态对象等等任务。而这些代码写起来总是重复的。
ORM解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。
将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。
因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。
1.3、ORM的优缺点
优点:
1.提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。
2.ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。
缺点:
牺牲程序的执行效率和会固定思维模式,降低了开发的灵活性。
从系统结构上来看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。
在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。ORM所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了ORM,程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。
在对对象做持久化时,ORM一般会持久化所有的属性,有时,这是不希望的。
但ORM是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。
二、MyBatis
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
2.1、MyBatis的特点
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
提供映射标签,支持对象与数据库的ORM字段关系映射
提供对象关系映射标签,支持对象关系组建维护
提供XML标签,支持编写动态sql。
2.2、MyBatis工作流程
(1)、加载配置并初始化
触发条件:加载配置文件
配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)、接收调用请求
触发条件:调用Mybatis提供的API
传入参数:为SQL的ID和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
(3)、处理操作请求 触发条件:API接口层传递请求过来
传入参数:为SQL的ID和传入参数对象
处理过程:
(A)根据SQL的ID查找对应的MappedStatement对象。
(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
(E)释放连接资源。
(4)、返回处理结果将最终的处理结果返回。
无论是用过的hibernate,mybatis,你都可以法相他们有一个共同点:
在java对象和数据库之间有做mapping的配置文件,也通常是xml 文件
从配置文件(通常是XML配置文件中)得到 SessionFactory
由SessionFactory 产生 Session
在Session中完成对数据的增删改查和事务提交等
在用完之后关闭Session
2.3、MyBatis架构
Mybatis的功能架构分为三层:
API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
2.4、MyBatis的主要成员如层次结构
主要成员:
Configuration:MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中
SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能
Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler:封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等
ParameterHandler:负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
TypeHandler:负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
MappedStatement:MappedStatement维护一条<select|update|delete|insert>节点的封装
SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql:表示动态生成的SQL语句以及相应的参数信息
层次结构:
更多请参考:《深入理解mybatis原理》 MyBatis的架构设计以及实例分析
2.5、学习资源
mybatis3中文帮助:http://www.mybatis.org/mybatis-3/zh/index.html
mybatis-spring:http://www.mybatis.org/spring/zh/index.html
MyBatis中国分站:http://www.mybatis.cn/
源代码:https://github.com/mybatis/mybatis-3/
三、MyBatis快速入门示例
3.1、在IDEA中创建项目
普通java项目或者是Maven项目都可以,如下图所示:
3.2、添加依赖
下载地址:https://github.com/mybatis/mybatis-3/releases
网盘下载:http://pan.baidu.com/s/1hrB1guo
【MyBatis】
mybatis-3.4.6.jar
【MYSQL驱动包】
mysql-connector-java-5.1.38-bin.jar
Maven POM
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo.mybatis01</groupId> <artifactId>MyBatis01</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!--MySql数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- JUnit单元测试工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> </project>
3、创建数据库和表,针对MySQL数据库
SQL脚本如下:
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `sex` enum('boy','girl','secret') DEFAULT 'secret', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
将SQL脚本在MySQL数据库中执行,完成创建数据库和表的操作,如下:
表中的数据如下:
3.3、添加Mybatis配置文件
在Resources目录下创建一个conf.xml文件,如下图所示:
conf.xml文件中的内容如下:
<?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"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/nfmall"/> <property name="username" value="root"/> <property name="password" value="uchr@123"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/studentMapper.xml"/> </mappers> </configuration>
解释
3.4、定义表所对应的实体类
Student实体类的代码如下:
package com.zhangguo.mybatis01.entities; public class Student { private int id; private String name; private String sex; public int getId() { return id; } public void setId(int 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; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; } }
3.5、定义操作Student表的sql映射文件
在resources目录下创建一个mapper目录,专门用于存放sql映射文件,在目录中创建一个studentMapper.xml文件,如下图所示:
studentMapper.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"> <!-- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的 例如namespace="com.zhangguo.mybatis01.dao.studentMapper"就是com.zhangguo.mybatis01.dao(包名)+studentMapper(studentMapper.xml文件去除后缀) --> <mapper namespace="com.zhangguo.mybatis01.dao.studentMapper"> <!-- 在select标签中编写查询的SQL语句, 设置select标签的id属性为selectStudentById,id属性值必须是唯一的,不能够重复 使用parameterType属性指明查询时使用的参数类型,resultType属性指明查询返回的结果集类型 resultType="com.zhangguo.mybatis01.entities.Student"就表示将查询结果封装成一个Student类的对象返回 Student类就是student表所对应的实体类 --> <!-- 根据id查询得到一个user对象 --> <select id="selectStudentById" resultType="com.zhangguo.mybatis01.entities.Student"> select * from student where id = #{id} </select> </mapper>
解释
参考:https://www.cnblogs.com/hellokitty1/p/5216025.html
3.6、在配置文件中注册映射文件
在配置文件conf.xml中注册studentMapper.xml映射文件
<?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"> <configuration> <environments default="development"> <environment id="development"> <!-- 配置数据库连接信息 --> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/nfmall"/> <property name="username" value="root"/> <property name="password" value="uchr@123"/> </dataSource> </environment> </environments> <mappers> <!-- 注册studentMapper.xml文件studentMapper.xml位于mapper这个目录下,所以resource写成mapper/studentMapper.xml--> <mapper resource="mapper/studentMapper.xml"/> </mappers> </configuration>
3.7、编写数据访问类
StudentDao.java,执行定义的select语句
package com.zhangguo.mybatis01.dao; import com.zhangguo.mybatis01.entities.Student; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; public class StudentDao { public Student getStudentById(int id){ //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件) InputStream stream=StudentDao.class.getClassLoader().getResourceAsStream("conf.xml"); //构建sqlSession的工厂 SqlSessionFactory ssf=new SqlSessionFactoryBuilder().build(stream); //使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件) //Reader reader = Resources.getResourceAsReader(resource); //构建sqlSession的工厂 //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //创建能执行映射文件中sql的sqlSession SqlSession session=ssf.openSession(); /** * 映射sql的标识字符串, * com.zhangguo.mybatis01.dao.studentMapper是studentMapper.xml文件中mapper标签的namespace属性的值, * selectStudentById是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL */ Student student=session.selectOne("com.zhangguo.mybatis01.dao.studentMapper.selectStudentById",1); return student; } public static void main(String[] args) { StudentDao dao=new StudentDao(); Student student=dao.getStudentById(1); System.out.println(student); } }
执行过程解释:
1、mybatis配置
SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
参考:https://www.cnblogs.com/selene/p/4604605.html
3.8、编写单元测试
import com.zhangguo.mybatis01.dao.StudentDao; import com.zhangguo.mybatis01.entities.Student; import org.junit.Test; import org.junit.Before; import org.junit.After; /** * StudentDao Tester. * * @author <Authors name> * @version 1.0 * @since <pre>09/24/2018</pre> */ public class StudentDaoTest { @Before public void before() throws Exception { } @After public void after() throws Exception { } /** * Method: getStudentById(int id) */ @Test public void testGetStudentById() throws Exception { StudentDao dao=new StudentDao(); Student student=dao.getStudentById(1); System.out.println(student); } }
测试结果:
3.9、修改junit测试类的生成位置
打开IntelliJ IDEA工具,Alt+Ctrl+S,打开设置窗口,点击进入Plugins.
从插件资源库中搜索JunitGenerator V2.0版本
安装此插件,重启IDEA就可以了。
现在可通过此工具自动完成test类的生成了,在需要进行单元测试的类中按 Ctrl + Shift + T
选中你要创建测试用例的方法即可。
IntelliJ IDEA JUnit Generator自动创建测试用例到指定test目录
1.打开File->Settings
2.搜索junit,找到JUnit Generator
3.Properties选项卡里的Output Path为测试用例生成的目录,修改为test目录:SOURCEPATH/../../test/java/{PACKAGE}/${FILENAME}
4.切换到JUnit 4选项卡,可以修改生成测试用例的模板,比如类名、包名等
------------------------------------------
测试类生成目录分析:
${SOURCEPATH}/test/${PACKAGE}/${FILENAME}
$SOURCEPATH/../../test/java/{PACKAGE}/${FILENAME}
对应的目录结构为
${SOURCEPATH}是到src/main/java这一层
../是退到上一层目录的意思,对着图理解一下
四、视频
五、示例
六、作业
1、请使用MyBatis完成一个用户管理的数据访问功能,要求实现,根据编号查询功能。