1.1 什么是MyBatis
MyBatis 本是 Apache 的一个开源项目 iBatis,2010年这个项目由Apache Software Foundation迁移到 了Google Code,并且改名为MyBatis。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架,iBATIS提供的持久层框 架包括SQL Maps和Data Access Objects(DAOs)。
MyBatis是一个优秀的持久层框架,它对JDBC操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、 结果集检索等JDBC繁杂的过程代码。
MyBatis通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 Java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由MyBatis框架执行sql并将结果 映射成Java对象并返回。
1.2 MyBatis的优点
简单易学:
本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件 + 配置几个sql映射文件, 易于学习,易于使用。 通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
灵活:
Mybatis不会对应用程序或者数据库的现有设计强加任何影响,sql写在xml里,便于统一管理和优 化。 通过sql语句可以满足操作数据库的所有需求。
解除sql与程序代码的耦合:
通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测 试。 sql和代码的分离,提高了可维护性。
提供丰富且强大的标签:
提供映射标签,支持对象与数据库的orm字段关系映射。 提供对象关系映射标签,支持对象关系组建维护。 提供xml标签,支持编写动态sql。
1.3 JDBC编程存在的问题
ORM:对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与 关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映 射的元数据,将程序中的对象自动持久化到关系数据库中。
Java典型的ORM中间件有:Hibernate,Mybatis,speedframework。
ORM技术特点:
提高了开发效率,由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所 以我们实际可能已经不需要一个专用的、庞大的数据访问层。 ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。 那为什么我们在实际开发中都是用一些ORM框架而不用传统的JDBC进行开发呢? 接下来我们来看一下一 个JDBC程序的简单实现并分析其缺点,如下:
1.3.1 JDBC程序
@Override
public void insertUser(User user) {
Connection conn = DBUtil.getConnection();
PreparedStatement stmt = null;
String sql = "insert into sys_user(NAME,ACCT,PWD,CRTIME,UPTIME) values(?,?,?,now(),now())";
try {
stmt = conn.prepareStatement(sql);
stmt.setString(1, user.getName());
stmt.setString(2, user.getAcct());
stmt.setString(3, user.getPwd());
stmt.executeUpdate();
}catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.closeAll(conn, stmt, null);
}
}
1.3.2 JDBC编程步骤
1. 加载数据库驱动。 2. 创建并获取数据库连接Connection。 3. 创建执行SQL语句的PreparedStatement对象。 4. 设置SQL语句中的占位符参数。 5. 通过PreparedStatement执行Sql并获取结果集。 6. 对Sql执行结果进行解析处理。 7. 释放资源(Connection、Preparedstatement、ResultSet)。
1.3.3 JDBC问题总结如下
1. 数据库连接使用时就创建,不使用时便立即释放,从而对数据库进行频繁的操作,导致资源的浪 费、影响性能。 优化设想:使用数据库连接池管理数据库对象。
2. sql都是硬编码到Java程序中,如果改变sql,那么得重新编译Java代码,不利于系统后期的维护。 优化设想:将sql语句配置在xml中,即使改变sql也不用重新编译源代码。
3. 向PreparedStatement设置参数,也是硬编码到Java程序中,不利于后期的维护。 优化设想:将sql语句以及占位符和参数全部配置在xml中,改动也不需要重新编译源代码。
4. 从resultset遍历结果集数据时,也存在硬编码,不利于后期系统的维护。 优化设想:将查询的结果集自动映射成Java对象;
针对以上问题,顺其自然的就出现了许多优化JDBC的方案,也就是后期出现的ORM持久层框架,例如 Mybatis以及Hibernate等等;这也就是为什么在实际开发中都比较喜欢用ORM框架的原因了。 有了这个概念,那么接下来我们就开始对Mybatis进行学习...
1.4 MyBatis架构
1.4.1 整体架构
1.MyBatis配置:
mybatis-config.xml(名称不固定),此文件作为MyBatis的全局(核心)配置文件,配置了 MyBatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在mybatisconfig.xml中加载。
2、通过MyBatis环境等配置信息构造SqlSessionFactory,即会话工厂。
3、由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行。
4、MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行 器、一个是缓存执行器。
5、MappedStatement也是MyBatis一个底层封装对象,Mybatis将SQL的配置信息加载成为一个个 MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存 中。mapper.xml文件中一个sql对应一个MappedStatement对象,sql的id即是MappedStatement的 id。
6、MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、字符串类型、实体类 类型,Executor通过MappedStatement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是 JDBC编程中对PreparedStatement设置参数。
7、MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、字符串类型、实体类 类型,Executor通过MappedStatement在执行sql后将输出结果映射至Java对象中,输出结果映射过程 相当于JDBC编程中对结果的解析处理过程。 1. 加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL
1.4.2 原理分析
1. 加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加 载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配 置),存储在内存中。
2. SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、 JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根 据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
3. SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
4. 结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者 基本数据类型,并将最终结果返回。
1.4.3 vo、po、dto、bo、pojo、entity
VO:Value Object ,值对象。
通常用于业务层之间的数据传递,由new创建,由GC回收; 和PO一样也是仅仅包含数据而已,但应是抽象出的业务对象,可以和表对应,也可以不是。 另外一种说法:VO(View Object):VO是显示视图模型,视图对象,用于展示层,它的作用是把某 个指定页面(或组件)的所有数据封装起来。举例:展示层将DTO传送过来男性显示成帅哥(客户 端1),或者显示成靓仔(客户端2);将帅哥或者靓仔,转换成男性,以DTO形式请求服务端。
PO:Persistant Object,持久层对象。
PO是 ORM 框架中Entity,PO属性和数据库中表的字段形成一一对应关系; VO和PO,都是属性加上属性的get和set方法;表面看没什么不同,但代表的含义是完全不同的。 基本上持久对象生命周期和数据库密切相关。Entity:实体类对象。 实体,和PO的功能类似,和数据表一一对应,一个实体一张表。 注:关于区分POJO和entity,查了一些资料也没发现有特别大的差别,功能上来说差别不大,只能 从含义上大致区分一下。不过现在大都是用entity来作为一个表映射的类。
DTO:Data Transfer Object,数据传输对象。
在这里泛指用于展示层与服务层之间的数据传输对象。那么问题来了,为什么要用DTO呢? 为什么 不能直接用实体模型呢? 举个例子: 比如我们一张表有100个字段,那么对应的PO就有100个属性。 但是我们界面上只要显示10个字段。 客户端用service来获取数据,没有必要把整个PO对象传递到客户端。 这时我们就可以用只有这10个属性的DTO来传递结果到客户端,这样也不会暴露服务端表结 构。 DTO由此产生,一是能提高数据传输的速度(减少了传输字段),二能隐藏后端表结构。
DO:Domain Object,领域对象,作用于业务层与dao层之间。
service使用接收到的DTO数据传输对象构造或者重构DO对象,传递到dao层。
BO:Business Object,业务对象。
BO是封装业务逻辑的Java对象,通过调用DAO方法,结合PO,VO进行业务操作。 举个例子: 比如一个简历,有教育经历、工作经历、社会关系等。 我们可以把教育经历对应一个PO,工作经历对应一个PO,社会关系对应一个PO。 建立一个对应简历的BO对象处理简历,每个BO包含这些PO。 这样处理业务逻辑时,我们就可以针对BO去处理。
POJO:Plain Ordinary Java Object,简单无规则java对象。
一般只有属性字段,无参构造以及get和set方法,也是指那些没有从任何类继承、也没有实现任何 接口,更没有被其它框架侵入的Java对象。因此它特别灵活可扩展,可以实现让一个模型贯穿多个 层,简单来说可以理解成不包含业务逻辑的单纯用来存储数据的Java类。 可以转化为PO、DTO、VO。 一个POJO持久化以后就是PO; 直接用它传递、传递过程中就是DTO; 直接用来对应表示层就是VO;
1.5总结
1.5.1 parameterType和resultType
parameterType:指定输入参数类型,MyBatis通过OGNL从输入对象中获取参数值设置在Sql中。 resultType:指定输出结果类型,MyBatis将Sql查询结果的一行记录数据映射为resultType指定类型的 对象。
1.5.2 #{} 和 ${}
#{}:
#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型、实体类类型、HashMap;
#{}接收简单类型,#{}中可以写成value或其它名称;
#{}接收实体类对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属 性值。
${}:
${}表示一个拼接符号,有Sql注入风险,所以不建议使用${};
${}接收输入参数,类型可以是简单类型、实体类类型、HashMap;
${}接收简单类型,${}中只能写成value;
${}接收实体类对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属 性值。
一句话总结:
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类 型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或实体类属性值。如果 parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串,通过${}可以将parameterType传入的内容拼接在sql中且不进行jdbc类型转 换,${}可以接收简单类型值或实体类属性值,如果parameterType传输单个简单类型值,${}括号 中只能是value。
1.5.3 selectOne和selectList
selectOne表示查询出一条记录进行映射; 如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。 selectList表示查询出一个列表(多条记录)进行映射;
如果使用selectList查询多条记录,不能使用selectOne。
如果使用selectOne报错:org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4