Mybatis简介
Mybatis是一个基于Java的持久层框架。
MyBatis 最初是apache的一个开源项目iBatis(iBATIS一词来源于internet和abatis的组合)。
Java项目持久层框架有很多,比较著名的有mybatis、hibernate等。
mybatis有以下特点:
* 轻量级持久层框架、易学、学习成本低
* 自己写sql语句,易于控制和优化
* 更适合大项目
* 企业的新项目越来越多的使用mybatis,是未来的趋势
ORM思想
ORM(Object Relation Mapping)对象关系映射,是一种思想,主要包含三种对应关系:
* 一个pojo类 ←→ 一张数据库表
* pojo类中的一个字段 ←→ 数据库表中的一列
* pojo类的一个对象 ←→ 数据库表中的一行数据
ORM思想是所有持久层框架的基本思想。
mybatis结构和工作过程
普通JDBC操作方式
* 准备好sql语句(比如select语句)和查询参数
* 拿到Connection、PreparedStatement对象
* 执行查询并获得ResultSet对象
* 处理ResultSet并封装成List对象并返回
Mybatis操作方式
主要结构
mybatis工作流程
* SqlSessionFactoryBuilder使用全局配置文件构建出SqlSessionFactory对象
* 使用SqlSessionFactory创建出SqlSession对象
* 使用SqlSession获得mapper接口的对象
* 调用mapper接口的某个方法,mybatis自动从对应的映射文件中找到对应的sql语句
* 自动使用JDBC执行此sql语句并得到ResultSet结果集
* 自动根据ORM思想把结果集处理成List对象并返回
通过这样的流程,通过对JDBC代码、处理结果集代码的封装,使得开发人员减少了90%以上的
编码量,并且使得代码结构简单清晰,sql语句易于管理维护。
mybatis-config.xml全局配置文件
指定日志实现。
把mybatis日志信息打印出来有助于理解和学习mybatis。
在全局配置文件中指定使用log4j2作为日志实现。
<environments>环境管理
即事务管理器和数据源管理
事务管理
mybatis有两种事务管理方式:JDBC、Managed
* JDBC方式即使用JDBC管理事务,由开发人员手动控制事务的commit、rollback
* sqlSessionFactory.openSession()时可以传入true设置成自动提交,默认是非自动提交
* MANAGED方式即把事务交给容器管理,比如交给spring
* 一般使用时都是mybatis和spring进行整合,使用MANAGED方式管理事务。单独学习时需要
使用JDBC方式。
数据源管理
mybatis有三种数据源管理方式:UNPOOLED、POOLED、JNDI
* UNPOOLED就是不使用数据库连接池,每次需要数据库连接都重新创建,使用后再关闭
* POOLED就是使用数据库连接池
* 无论是UNPOOLED还是POOLED方式,学习阶段都可以只简单的指定driver、url、username、
password这四个基本的连接参数。
* JNDI就是使用在JDNI上下文中维护的数据库连接池,不需要mybatis管理维护
* 一般使用时都是mybatis和spring进行整合,数据源在spring中进行管理维护。单独学习
时才由mybatis管理维护。
给类起别名
给类起别名的目的是简化书写,在配置文件中使用短的别名代替长的全类名(别名不是必须的)
在全局配置文件中使用<typeAliases>管理用户自定义的别名,另外mybatis已经对经常使用的类
起了别名mybatis预定义别名。
用户自定义别名
直接指定类的别名
给某个包(及子包)下的所有普通类自动起别名:(不会给匿名类、内部类、接口起别名)
别名为类的简单名称的全部小写形式,比如com.rupeng.pojo.User的别名是user,别名在使用时
忽略大小写,即使用User、uSer、usEr和使用user的效果是一样的。
另外,配置文件使用的一些值如UNPOOLED、POOLED、JDBC等也是别名
注意:mapper的命名空间和别名是不同的概念,不能混用。
管理映射配置文件
使用<mappers>管理映射配置文件
直接指定配置文件路径; 自动加载某个包(及子包)下所有配置文件
映射配置文件
每个pojo类都会对应一个映射配置文件,一般命名为XxxMapper.xml,主要用来管理那些操作对应
数据库表的sql语句。
每个映射文件都有一个命名空间,用来在全局唯一标识此映射文件,一般使用对应mapper接口的全
类名作为命名空间,比如com.rupeng.mapper.UserMapper。
管理insert等更新语句
每条insert语句写在一个<insert>中
<insert>的id属性值要求在当前映射文件中是唯一的,和命名空间配合使用就可以在全局唯一标识
此sql语句。
<insert>的parameterType属性指定对应mapper接口方法的参数类型,实际上不指定时mybatis也
可以自动识别。mybatis会从这个参数对象中以适当的方式取出数据并赋值给sql语句的占位符
当错误的把pojo类型指定为了map时,mybatis也可以正确识别。
mybatis默认使用PreparedStatement执行这里的sql语句,sql语句中的每个#{xxx}就是一个占位
符,即insert into T_Users(name,age) values(?,?)。
#{xxx} 是OGNL表达式语言的语法形式,和EL表达式相似。当parameterType为pojo类时,mybatis
可以自动从pojo对象中取出xxx字段的值并赋值给对应的占位符;为map时也相似。
当parameterType为基本类型及其包装类型、字符串等类型时(一般此时只有一个占位符),
#{xxx}中的xxx可以随意命名。
insert语句执行成功后会返回影响的行数,行数是简单的整数,所以<insert>不需要指定返回值
类型。对应的mapper接口的方法返回值类型可以是int、Integer、long、Long、void等。
另外,<insert>中写update、delete等语句也可以执行成功,但不推荐这么做。应该使用
<update>管理update语句;<delete>管理delete语句。
<update>、<delete>的用法、特点和<insert>非常相似
管理select查询语句
<select>的id、parameterType属性以及OGNL表达式的用法和<insert>一样
select查询结果集的处理
* <select>的resultType属性指定要把ResultSet结果集的每行数据处理成哪个pojo类的对象,
最终mybatis会返回一个List对象,包含结果集处理成的所有pojo对象。所以对应mapper
接口的方法的返回值类型需要是List,比如List<User>。
* resultType也可以指定为map,会返回List<Map>对象
* 如果结果集中只有一条数据,并且mapper接口的方法返回值类型是pojo类,这时mybatis不
会返回list,而是直接把这个pojo类对象返回。
如何保证pojo类的字段名和数据库表的列名一致
<resultMap>的autoMapping属性
此属性用来指定当使用<resultMap>时,如果列名和字段名相同时是否可以自动映射,默认没有
指定具体值,默认行为是如果<resultMap>中没有使用<association>或者<collection>子元素时
会自动映射,否则就不会。也可以设置autoMapping为true强制自动映射。
管理表之间的关联关系
* 一对一:比如公民和身份证,一个公民只有一个身份证,一个身份证只属于一个公民
* 一对多:比如问题和回答,一个问题可以被回答多次,一个回答只属于一个问题
* 多对多:比如角色和权限,一个角色可以用拥有多个权限,一个权限也可属于多个角色
这三类关联关系在pojo类中可表示为has-a、has-many
has-a
可以使用<resultMap>的子元素<association>管理has-a的情况。
has-many
可以使用<resultMap>的子元素<collection>管理has-many的情况。
<association>和<collection> 自己之间、相互之间还可以进行嵌套,甚至可以引用其他的
<resultMap>,但这会导致配置文件很复杂,使用时或者项目设计时应该避免大量出现这种情况。
动态sql语句
在条件查询中,查询条件可以有多个,查询条件的组合更多,这样在写条件查询的sql语句时就很
痛苦。为了解决这个问题,mybatis提供了动态sql语句功能,可以在运行时动态的决定使用哪些
条件进行查询。
<if>标签的判断条件为true时,标签体的查询条件就会被使用。test属性支持比较运算符和逻辑
运算符。
<where>标签可自动判断最终标签体内是否有查询条件,如果有就会在查询条件前插入where单词,
并可自动去除第一个查询条件前面多余的and、or。
一般来说一条这样的动态查询sql语句可满足多种查询情况,如果有不能满足的情况,就针对这种
情况专门写sql语句。
可重用sql片段
可以使用<sql>定义任意的sql语句片段,然后使用<include>引用此片段
可重用sql片段在映射文件很复杂的情况下很有用(但一般都会避免映射文件复杂化)。
mybatisAPI
mybatis底层实现类有很多,但用户会使用到的很少,并且用法都很明确:
* SqlSessionFactoryBuilder 推荐生命周期为方法级别
* SqlSessionFactory 推荐生命周期为应用程序级别(全局级别、单例)
* SqlSession 推荐生命周期为线程级别(方法级别)
* mapper接口 推荐生命周期为线程级别(方法级别)
pagehelper 分页、排序插件
pagehelper是第三方的mybatis分页、排序插件,以十分优雅简单的方式实现了mybatis分页、
排序操作。
在同一个线程中,在mapper接口方法执行之前的任何位置调用分页、排序方法即可。插件会自动
识别数据库类型,自动给sql语句添加分页或者排序语句。
每次分页、排序只对最近的下一次查询有效。
mapper接口返回的list实际上被包装成了Page对象(Page继承了ArrayList),使用PageInfo包装
返回的list,通过PageInfo可获得各种分页信息。
mybatis缓存
mybatis有两种缓存:一级缓存和二级缓存
一级缓存
* session级别的缓存
* 不可禁用
* select语句的结果将被缓存
* 当同一个session的两次查询生成的最终sql语句和参数值完全一样时第二次查询才会使用
第一次查询的一级缓存。
* insert等更新语句会清空一级缓存
二级缓存
mybatis的二级缓存是命名空间级别的缓存,即同一个mapper接口执行过的所有查询语句以及查询
结果都会被缓存到此命名空间下。
只需要在映射文件使用<cache/>就会开启此命名空间的二级缓存,默认会对此命名空间下的所有
的<select>使用二级缓存(如果指定某个<select>的useCache属性为false,此sql语句将不会
使用二级缓存)。
insert等更新语句会清空所属命名空间下的所有二级缓存
执行查询时,mybatis会先在二级缓存中查找,如果没找到再在一级缓存中查找,如果还没找到
才会查询数据库,并会把查询结果存放到一级缓存和二级缓存中。
对于有关联关系的两张表,可以使用<cache-ref namespace="xxx"/>的方式共享同一个命名空间
的二级缓存。但如果表较多,并且关联关系很复杂,很可能会导致频繁的清空缓存反而使性能
下降;另外如果一个大项目的两个子系统共用同一个数据库,这时也不能使用二级缓存。所以
mybatis的二级缓存适合简单的独立项目。
spring整合mybatis
主要就是把数据库连接池、事务交给spring管理,并且把SqlSessionFactory、mapper接口配置成
bean。
使用的时候直接把mapper接口注入到service类,作为dao使用。
有一点大家可能会迷惑,mapper接口的生命周期原本推荐的是线程级别,但在和spring整合时,
mapper接口的生命周期被提升成了应用程序级别,这不会出问题吗?
不用担心,首先mapper接口的代理类中没有共享数据,不会出现数据的线程安全问题;另外每个
不同的事务下调用mapper接口的方法时,底层都会使用不同的SqlSession对象,也就是说数据库
访问也没问题,所以整合时可以放心使用。