MyBatis
![MyBatis logo](https://i-blog.csdnimg.cn/blog_migrate/e5e284a5f4f1e06469dd69661aeaa353.png)
简介(来自维基百科)
MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由Apache Software Foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
功能概况
与其他的对象关系映射框架不同,MyBatis并没有将Java对象与数据库表关联起来,而是将Java方法与SQL语句关联。MyBatis允许用户充分利用数据库的各种功能,例如存储过程、视图、各种复杂的查询以及某数据库的专有特性。如果要对遗留数据库、不规范的数据库进行操作,或者要完全控制SQL的执行,MyBatis是一个不错的选择。
与JDBC相比,MyBatis简化了相关代码:SQL语句在一行代码中就能执行。MyBatis提供了一个映射引擎,声明式的把SQL语句执行结果与对象树映射起来。通过使用一种内建的类XML表达式语言,或者使用Apache Velocity集成的插件,SQL语句可以被动态的生成。
MyBatis与Spring Framework和Google Guice集成,这使开发者免于依赖性问题。
MyBatis支持声明式数据缓存(declarative data caching)。当一条SQL语句被标记为“可缓存”后,首次执行它时从数据库获取的所有数据会被存储在一段高速缓存中,今后执行这条语句时就会从高速缓存中读取结果,而不是再次命中数据库。MyBatis提供了基于 Java HashMap 的默认缓存实现,以及用于与OSCache、Ehcache、Hazelcast和Memcached连接的默认连接器。MyBatis还提供API供其他缓存实现使用。
MyBatis入门
更详细内容参考官方教程
使用JDBC操作数据库的弊端
使用JDBC操作数据库的流程见附件word文档
通常使用JDBC操作数据库的代码如下
![](https://i-blog.csdnimg.cn/blog_migrate/6f53f0c7ec8161cfc272c2b6c9bd0375.png)
弊端如下
- 数据库连接资源的创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
- SQL语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。
- 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
- 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
MyBatis的架构
其中Mapper1.xml、Mapper2.xml...Mappern.xml是SQL映射文件(一般数据库中有多少个表就有多少个映射文件)
MyBatis配置
- SqlMapConfig.xml,此文件作为MyBatis的全局配置文件,配置了mybatis的运行环境等信息
- mapper.xml文件即SQL映射文件,文件中配置了操作数据库的SQL语句。此文件需要在SqlMapConfig.xml中加载
- 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
- 由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行
- MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器
- Mapped Statement也是MyBatis一个底层封装对象,它包装了MyBatis配置信息及SQL映射信息等,mapper.xml文件中一个SQL对应一个Mapped Statement对象,SQL的id即是Mapped Statement的id
- Mapped Statement对SQL执行输入参数进行定义,包括HashMap、基本类型、POJO,Executor通过Mapped Statement在执行SQL前将输入的java对象映射至SQL中,输入参数映射就是JDBC编程中对PreparedStatement设置参数
- Mapped Statement对SQL执行输出结果进行定义,包括HashMap、基本类型、POJO,Executor通过Mapped Statement在执行SQL后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程
快速入门
通常MyBatis由Maven构建,此处快速入门是手动构建的方式
环境搭建
步骤
- 创建项目,导入依赖jar包
- 加入核心配置文件和log4j的配置文件
- 创建POJO
- 创建SQL映射文件
- 加载映射文件
- 在核心配置文件中加载映射文件
导入依赖jar包
首先在GitHub上下载所需版本的MyBatis
其目录结构如下
![](https://i-blog.csdnimg.cn/blog_migrate/5434e14be5c9746afbadf1f3388a86ab.png)
导入
MyBatis的核心包
与lib目录下的
MyBatis的依赖包
以及
数据库驱动包
到项目中
如下
![](https://i-blog.csdnimg.cn/blog_migrate/161acb6514fb97b05230aa793fae925f.png)
创建资源文件夹并在其中创建配置文件(log4j.properties和SqlMapConfig.xml文件)
*IDEA中创建名为config的资源目录/文件夹
![](https://i-blog.csdnimg.cn/blog_migrate/ffb7e1d7fb2330522590d7cf679c49e4.png)
加入log4j.properties
内容如下
# Global logging configuration log4j.rootLogger=DEBUG, stdout # 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
MyBatis默认使用log4j作为输出日志工具
加入SqlMapConfig.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> <!-- 和spring整合后 environments配置将废除 --> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理 --> <transactionManager type="JDBC" /> <!-- 数据库连接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> </configuration>
SqlMapConfig.xml是mybatis核心配置文件,配置文件内容为数据源、事务管理
创建POJO
POJO类作为mybatis进行sql映射使用,po类通常与数据库表对应
![](https://i-blog.csdnimg.cn/blog_migrate/2b25167bff6083ec95f3b2d66c645889.png)
创建SQL映射文件
在config目录下创建sqlmap目录并创建sql映射文件User.xml
内容如下
此处未编写任何SQL语句,
日后操作User表
的SQL语句将编写在此处
<?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:命名空间,用于隔离sql,还有一个很重要的作用 --> <mapper namespace="test"> </mapper>
其中命名空间是用于区分不同的映射文件的同名SQL语句的
如命名空间为test,若SQL功能名称为findUserById,引用方式应该为user.findUserById
此时项目目录结构如下
![](https://i-blog.csdnimg.cn/blog_migrate/ce04521ffd795fdf045572d9d079ab66.png)
在SqlMapConfig文件中引入SQL映射文件
创建测试类测试环境搭建情况
完整测试代码见这个部分最末
往User.xml中添加SQL语句
如下
测试根据id查询单个User
测试代码如下
package com.zella.test; import com.zella.mybatis.pojo.User; 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.junit.Test; import java.io.InputStream; public class MyBatisTest { @Test public void testMyBatis() throws Exception { // 加载核心配置文件 String resource = "sqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); // 创建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); // 创建SqlSession SqlSession session = sqlSessionFactory.openSession(); // 执行SQL语句 User user = session.selectOne("test.findUserById", 10); System.out.println(user); } }
运行结果如下
说明框架基本搭建成功
测试根据用户名模糊查询对应用户列表
映射文件新增
注意
![](https://i-blog.csdnimg.cn/blog_migrate/28e8691a27b7dec51d0b308444a366ba.jpeg)
若为'%#{v}%'则实际SQL语句为'%'xxx'%',语法错误,所以字符串拼接需要使用${v}
实际上此处应该用'%${value}%'测试才不报错
但是使用$会有SQL注入的问题,可以用以下的方式使用#{v}
即用双引号包围%符来使用#{v},可以防止SQL注入
测试插入数据
映射文件如下
另外测试代码需要手动提交事务
测试插入用户后返回id
业务上可能需要在插入用户后返回i:
如:需要同时插入用户和订单,订单数据需要用到用户id,而
用户id是插入表后才生成的
希望插入数据后更新POJO对象的id属性,即在插入User后更新User的id属性,然后就可以直接通过user.getId()来获取id值了
可以通过在映射文件添加selectKey标签来定义返回id,如下
测试代码如下
执行结果
![](https://i-blog.csdnimg.cn/blog_migrate/b7eb6bae4ecf502517085156769086f4.png)
测试更新用户
映射文件如下
测试代码
测试删除用户
映射文件如下
![](https://i-blog.csdnimg.cn/blog_migrate/97789dab2e06672dd9a36c2c56247f53.png)
测试代码
使用MyBatis解决了JDBC操作数据库的弊端
- 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题
- 解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接
- SQL语句写在代码中造成代码不易维护,实际应用SQL变化的可能较大,SQL变动需要改变Java代码
- 解决:将SQL语句配置在XXXXmapper.xml文件中与Java代码分离
- 向SQL语句传参数麻烦,因为SQL语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应
- 解决:MyBatis自动将Java对象映射至SQL语句,通过statement中的parameterType定义输入参数的类型
- 对结果集解析麻烦,SQL变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成POJO对象解析比较方便
- 解决:MyBatis自动将SQL执行结果映射至Java对象,通过statement中的resultType定义输出结果的类型
*MyBatis和Hibernate的不同
MyBatis
和hibernate不同,它
不完全是一个ORM框架
,因为MyBatis
需要程序员自己编写SQL语句
。mybatis可以通过XML或注解方式灵活配置要运行的
SQL
语句,并将Java对象和SQL语句映射生成最终执行的SQL,最后将SQL执行的结果再映射生成
Java
对象。
MyBatis
学习门槛低,简单易学,程序员直接编写原生态
SQL
,可严格控制
SQL
执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是
MyBatis无法做到数据库无关性
,如果需要实现支持多种数据库的软件则需要自定义多套
SQL
映射文件,工作量大。
Hibernate对象/关系映射能力强,
数据库无关性好
,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用
Hibernate
开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
使用MyBatis开发Dao层
使用MyBatis开发Dao,通常有两种方式
- 原始/传统Dao开发方法
- Mapper动态代理开发方法
MyBatis的主要API的使用范围
- SqlSession中封装了对数据库的操作,如:查询、插入、更新、删除等
- SqlSession通过SqlSessionFactory创建
- SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建
SqlSessionFactoryBuilder
- SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory创建的
- 所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量
SqlSessionFactory
- SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory
SqlSession
- SqlSession是一个面向用户的接口,sqlSession中定义了数据库操作方法
- 每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的
- 因此最佳的范围是请求或方法范围,绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中
- 每打开一个SqlSession使用完毕就要关闭它,通常把这个关闭操作放到finally块中以确保每次都能执行关闭
传统Dao开发方法
传统Dao开发方法需要程序员编写Dao接口及Dao实现类
步骤
- 编写映射文件
- 编写Dao接口
- 编写Dao接口实现类
- 测试
详细步骤见附件word文档
原始Dao开发方法存在的问题
- Dao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
- 调用SqlSession的数据库操作方法(如selectOne("xxx.xxx",id))需要指定statement的id,这里存在硬编码,不得于开发维护
此时可以考虑由程序反射生成重复的代码,仅编写特定的非重复代码
Mapper动态代理方式开发Dao
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法
Mapper接口开发需要遵循以下规范
符合以上四个规范就可以将Mapper接口与映射文件一一对应、绑定,可以使用Mapper动态代理的方式开发Dao了
测试
根据以上四个规范编写定义好映射文件和Mapper接口之后,就可以编写测试代码测试Mapper接口了
可见Mapper动态代理方式编写Dao十分简便易用
两个小细节
selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是
根据mapper接口方法的返回值决定
- 如果返回list则调用selectList方法
- 如果返回单个对象则调用selectOne方法
namespace
MyBatis官方推荐使用Mapper代理方法开发
Mapper
接口,程序员
不用编Mapper接口实现类
,使用Mapper代理方法时,输入参数可以使用POJO包装对象或Map对象,保证Dao的通用性
SqlMapConfig.xml配置文件
SqlMapConfig文件的标签(配置内容)
SqlMapConfig.xml中配置的内容和顺序如下:
- properties(属性)
- settings(全局配置参数)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境集合属性对象)
- environment(环境子属性对象)
- transactionManager(事务管理)
- dataSource(数据源)
- environment(环境子属性对象)
- mappers(映射器)
properties(属性)
SqlMapConfig.xml可以引用java属性文件中的配置信息
![](https://i-blog.csdnimg.cn/blog_migrate/3be18a3f1b065cfe5b6efb2fe7695d8f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c96b2ef260baeada7d8e8216beb594f0.png)
MyBatis将按照下面的顺序来加载属性:
- 在properties元素体内定义的属性首先被读取。
- 然后会读取properties元素中resource或url对应的properties文件加载的属性,它会覆盖已读取的同名属性
typeAliases(类型别名)
MyBatis默认支持的别名
别名 | 映射的类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
map | Map |
自定义别名
注意:
- 别名大小写不敏感
- 批量定义别名只需要定义父包,将扫描父包下所有类,包括子包内
- 但最好保证这个父包下所有类都是POJO
![](https://i-blog.csdnimg.cn/blog_migrate/6bc1dfda0860b589ea46b78dc9506bb5.png)
mappers(映射器)
映射器配置的几种方法
<mapper resource=""/>
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
<mapper class=""/>
使用mapper接口类路径
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:
这种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<package name=""/>(常用)
注册指定包下的所有mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:这此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
![](https://i-blog.csdnimg.cn/blog_migrate/49862cf38db0b6ff08b7887f276ac6e9.jpeg)
<mapper url=""/>
需要书写绝对路径,不推荐
MyBatis深入
输入映射和输出映射
Mapper.xml映射文件中定义了操作数据库的sql,每个SQL是一个statement,映射文件是MyBatis的核心
输入映射-parameterType
传递简单类型
使用#{}占位符,或使用${}进行SQL拼接
传递POJO
MyBatis使用OGNL表达式解析对象字段的值,#{}或者${}括号中的值为POJO属性名称
传递POJO包装对象
开发中通过可以使用
POJO传递查询条件。
查询条件可能是
综合的查询条件(如多表查询),不仅包括用户查询条件还包括其它的查询条件(比如查询用户信息的时候,将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
包装对象:
POJO类中的一个属性是另外一个POJO
示例详见附件
输出映射-resultType
简单输出类型
如查询条目数,resultType为int
![](https://i-blog.csdnimg.cn/blog_migrate/082ad0628aa872d9ad453233ace541f6.png)
输出POJO
略
输出POJO列表
略
输出resultMap类型
resultType可以指定将查询结果映射为POJO,但需要
POJO的属性名和SQL查询的列名一致方可映射成功
如果
SQL查询
字段名和POJO的
属性名
不一致,可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还需要将查询结果映射到
POJO对象中
resultMap可以实现将查询结果映射为复杂类型的
POJO,比如在查询结果映射对象中包括
POJO和list实现一对一查询和一对多查询
示例
Orders表字段名与POJO属性名称不一致,使用resultMap类型
首先定义POJO对象
![](https://i-blog.csdnimg.cn/blog_migrate/b65672a0410caf633615bd0c70614415.png)
编写Mapper接口并配置映射文件(不使用resultMap作为输出类型)
![](https://i-blog.csdnimg.cn/blog_migrate/ee8f6f2495ce8cf1a7b60c8f810f4902.png)
最后测试
发现userId为null
![](https://i-blog.csdnimg.cn/blog_migrate/abc7eee88e927771caf6241768cb32b5.jpeg)
由于上边的mapper.xml中sql查询列(user_id)和Order类属性(userId)不一致,所以查询结果不能映射到pojo中。
需要定义resultMap,把orderResultMap将sql查询列(user_id)和Order类属性(userId)对应起来
解决方案就是定义并使用resultMap作为输出类型
修改映射文件,定义并使用resultMap
![](https://i-blog.csdnimg.cn/blog_migrate/a2813fc11402be43db746837805f157a.png)
此后用同样的测试代码就可以得到需要的结果了
动态SQL
动态SQL是MyBatis提供的使用各种标签方法实现动态拼接SQL语句的功能
静态的SQL的弊端
其一:
当一条SQL有多个条件时,若传入的POJO提供部分条件,未传入的条件为空,将默认这个SQL语句为空的条件where子句为不满足,从而使SQL查询得不到需要的结果
详见附件day02 3.1
if标签
示例:
可以活用where 1 = 1 这个条件
![](https://i-blog.csdnimg.cn/blog_migrate/48eaf9c7a9d55032ba105b86b4492802.png)
where标签
上面活用小技巧虽然可以解决问题,但不够简介
可以使用where标签解决
where标签可以自动去掉第一个有效(非空)条件前的and
SQL片段
可以将重复的SQL语句提取出来,作为SQL片段,使用include标签引用,达到SQL抽取重用的效果
示例
例如可以将上文例子的id,username,birthday等属性/字段提取作为SQL片段
![](https://i-blog.csdnimg.cn/blog_migrate/6203ff3b91a11f0efe7b49471ebe25ad.png)
如果需要引用其他映射文件配置的SQL片段,需要加上对应的namespace
如
![](https://i-blog.csdnimg.cn/blog_migrate/94b8cb7d98f5fdba107cb940b340193f.jpeg)
foreach标签
可以向SQL传递
数组或
List对象或有这两种类型作为属性的
QueryVo/POJO,MyBatis可以通过foreach标签解析
示例
通过多个id查询用户信息
查询SQL:select * from user where id in (1,10,24)
首先需要修改POJO中QueryVo的代码,增加ids属性用于存储多个用户id(getter/setter)
![](https://i-blog.csdnimg.cn/blog_migrate/c3935badd44a8eb7ccccba2d3dd08912.png)
在映射文件中定义
测试代码如下
执行结果
注意
MyBatis在映射文件中是通过
类名而不是当前传入变量名来识别的,当
直接传递数组(或List)而不是传递QueryVo时,映射文件中参数类型应该是
array(或list)而不是变量名
原理:在传递User对象时,我们不直接使用User对象而是使用User对象内部的属性,如user.username,同理,直接传递数组时,使用的是数组内部的属性
关联查询
示例
![](https://i-blog.csdnimg.cn/blog_migrate/b10427fcc3a8a749b6751c0e993b7c9e.png)
在这种表的关系下,由用户查询订单是一对多关联查询,由订单查询用户是一对一关联查询
一对一查询
有多种方式使用一对一关联查询
方式一 创建新的POJO并使用resultType来作为输出类型
示例
由订单查询该订单对应用户
新建POJO如下
![](https://i-blog.csdnimg.cn/blog_migrate/3643fc59ccac55061f6e60bd679d1f26.png)
SQL如下
![](https://i-blog.csdnimg.cn/blog_migrate/e44fc949e884c9589b6a9065df9ec040.png)
Mapper接口方法
![](https://i-blog.csdnimg.cn/blog_migrate/8f5c1662b9d608977c97322904482bb2.png)
测试代码
执行结果
方式一
定义专门POJO作为输出类型,其中定义了SQL查询觉过集的所有字段,这种方法较为简单,企业中使用普遍
方式二
使用resultMap作为输出类型,定义专门的resultMap用于映射一对一的查询结果
首先改造POJO类
在Orders类中加入类型为User的属性,用于存储关联查询的用户信息,因为是一对一,所以使用单个User对象作为属性
![](https://i-blog.csdnimg.cn/blog_migrate/4e85fc6ca0b46dbb3a35594923e8df26.png)
修改映射文件,定义resultMap并使用
![](https://i-blog.csdnimg.cn/blog_migrate/0e0225004c68fd5a2f523e3a735312cf.png)
注意:关联查询不可以省略同名的属性/字段定义,否则无法映射
具体为当resultMap标签中出现association或collection标签时不可以省略同名属性/字段定义
最后测试,得到结果为
一对多查询
示例:查询所有用户信息以及用户关联的订单信息
用户信息对订单信息是一对多关系
映射文件如下(重要)
![](https://i-blog.csdnimg.cn/blog_migrate/85e62b3625cf67a0d034ed22a26a4810.png)
修改User类,增加Orders类属性
![](https://i-blog.csdnimg.cn/blog_migrate/e687ac03a9a7a3324fd1e3d3b28ceb8f.png)
注意上文修改Orders类增加了user属性,但若在这个一对多查询中,ordersList内的Orders对象中user属性是null的,应该避免出现NPE
测试结果如下
*总结resultMap通常的标签组成
![](https://i-blog.csdnimg.cn/blog_migrate/9b5b516ee90f05dd17b4ce02ea09c855.png)
而resultMap需要根据具体SQL来确定每个column的名称,若使用了别名,则使用别名
MyBatis整合Spring
整合思路
- SqlSessionFactory对象应该放到Spring容器中作为单例存在
- 传统Dao的开发方式中,应该从Spring容器中获得SqlSession对象
- Mapper代理形式中,应该从Spring容器中直接获得Mapper的代理对象
- 数据库的连接以及数据库连接池事务管理都交给Spring容器来完成
整合步骤
导入需要的依赖
- Spring的jar包
- MyBatis的jar包
- Spring+MyBatis的整合包。
- MySql的数据库驱动jar包。
- 数据库连接池的jar包
创建配置文件
- 数据库连接及连接池
- 事务管理(暂时可以不配置)
- SqlSessionFactory对象,配置到Spring容器中
- Mapper接口代理对象或者是Dao实现类配置到Spring容器中
整合步骤详见附件
整合完毕后继续查看下面的内容
Spring与MyBatis整合后Dao的开发
有两种Dao的开发方式
- 传统的Dao开发方式
- 使用Mapper代理的方式
- 直接配置Mapper代理
- 使用扫描包配置Mapper代理
传统Dao开发方式
通过编写Dao接口和其实现类来完成
Dao实现类
必须继承SqlSessionDaoSupport类来通过Spring注入SqlSessionFactory依赖
在applicationContext文件配置Dao的bean注入
![](https://i-blog.csdnimg.cn/blog_migrate/9e7aef9b18c3dd08d8c8d7fc93b334b1.png)
然后编写/配置POJO、Mapper映射文件和Mapper接口最后配置核心配置文件后,测试是否整合成功
详见附件
直接配置Mapper代理方式式开发Dao
这种方式通过在applicationContext文件中配置Mapper接口来提供接口的代理实现类
测试代码
注意测试向Spring获取对象时可以使用类.class类型也可以使用id来获取
package com.zella.mybatis.test; import com.zella.mybatis.mapper.UserMapper; import com.zella.mybatis.pojo.User; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.Date; import java.util.List; public class UserMapperTest { private ApplicationContext context; @Before public void setUp() { this.context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); } @Test public void testQueryUserById() { UserMapper userMapper = this.context.getBean(UserMapper.class); System.out.println(userMapper.queryUserById(1)); } @Test public void testQueryUserByUsername() { // 获取Mapper UserMapper userMapper = this.context.getBean(UserMapper.class); List<User> list = userMapper.queryUserByUsername("张"); for (User user : list) { System.out.println(user); } } @Test public void testSaveUser() { // 获取Mapper UserMapper userMapper = this.context.getBean(UserMapper.class); User user = new User(); user.setUsername("曹操"); user.setSex("1"); user.setBirthday(new Date()); user.setAddress("三国"); userMapper.saveUser(user); System.out.println(user); } }
使用扫描包形式配置Mapper方式开发Dao
这种方法通过在applicationContext文件配置扫描Mapper接口所在包来自动配置Mapper接口获取其代理类对象
这种方式的优点在于当Mapper接口很多时不再需要一个一个配置Mapper接口
![](https://i-blog.csdnimg.cn/blog_migrate/557a338073a8b42a2240baa923b39946.png)
注意:id就是Mapper类的类名
使用前一个方式的测试代码测试即可
MyBatis逆向工程
当数据库中表的数量很多时,手动编写POJO、映射文件和Mapper接口的工作就变得十分繁复
而MyBatis逆向工程可以方便的从数据库中将表自动映射到Java POJO类,并同时生成Mapper.xml和Mapper接口
详见附件,或自查网络资源
可以参考
*拓展内容
各种类型类的定义
PO与VO
![](https://i-blog.csdnimg.cn/blog_migrate/ec197f01fc73eb4275d4f6ee1cdbc570.png)
DO
domain object持久对象
就是从现实世界中抽象出来的有形或无形的业务实体。
PO
persistant object持久对象
最形象的理解就是一个PO就是数据库中的一条记录。
好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象。
BO
business object业务对象
主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。
比如一个简历,有教育经历、工作经历、社会关系等等。
我们可以把教育经历对应一个PO,工作经历对应一个PO,社会关系对应一个PO。
建立一个对应简历的BO对象处理简历,每个BO包含这些PO。
这样处理业务逻辑时,我们就可以针对BO去处理。
VO
Value Object值对象
View Object表现层对象
主要对应界面显示的数据对象。对于一个WEB页面,或者SWT、SWING的一个界面,用一个VO对象对应整个界面的值。
DTO
Data Transfer Object数据传输对象
主要用于远程调用等需要大量传输对象的地方。
比如我们一张表有100个字段,那么对应的PO就有100个属性。
但是我们界面上只要显示10个字段,
客户端用WEB service来获取数据,没有必要把整个PO对象传递到客户端,
这时我们就可以用只有这10个属性的DTO来传递结果到客户端,这样也不会暴露服务端表结构.到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO
POJO
plain ordinary java object 简单ava对象
个人感觉POJO是最参见最多变的对象,是一个中间对象,也是我们最常打交道的对象。
一个POJO持久化以后就是PO
直接用它传递、传递过程中就是DTO
直接用来对应表示层就是VO
DAO
data access object数据访问对象
这个大家最熟悉,和上面几个O区别最大,基本没有互相转化的可能性和必要.
主要用来封装对数据库的访问。通过它可以把POJO持久化为PO,用PO组装出来VO、DTO
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">