Mybatis简介
Mybatis是一个基于Java的持久层框架。- 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- 可以使用简单的 XML 或注解来配置和映射原生信息
开始
导包
首先要导入Mybatis的所需的jar包
- 通过Maven导入
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
- 手动下载导入
打开链接 http://github.com/mybatis/mybatis-3/releases 下载 MyBatis 所需要的包
创建xml配置文件
更多配置请前往官方文档查看
在resources文件夹下新建mybatis-config.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>
<!-- 导入配置文件 -->
<properties resource="db.properties">
<!-- 添加数据,若外部文件中存在则失效,如不存在则添加 -->
<property name="password" value="admin"/>
</properties>
<!-- 设置 -->
<settings>
<!-- 设置使用的日志实现
常用的有 SLF4J/LOG4J/Log4j2/STDOUT_LOGGING(标准输出日志 不需要配置,可以直接使用) -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启二级缓存 -->
<!-- <setting name="cacheEnabled" value="true"/> -->
<!-- 是否开启驼峰命名自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--当检测出未知列(或未知属性)时,如何处理,默认情况下没有任何提示,这在测试时很不方便,不容易找到错误。 NONE(默认值):不做任何处理;WARNING : 警告日志形式的详细信息;FAILING:映射失败,抛出异常和详细信息 -->
<setting name="autoMappingUnknownColumnBehavior" value="WARNING" />
</settings>
<!-- 配置实体类别名 -->
<typeAliases>
<!-- 方式1:为每个类设置别名 -->
<typeAlias type="top.raincither.pojo.User" alias="User"/>
<!-- 方式2:以包中的类名,且将首字母小写作为别名 -->
<!-- <package name="top.raincither.pojo"/> -->
</typeAliases>
<!-- 环境配置 default:默认配置 可配置多个环境 -->
<environments default="development">
<!-- 环境配置1 -->
<environment id="development">
<!-- 事务管理器的配置 JDBC/MANAGED -->
<!--
JDBC:这个配置直接简单使用了JDBC的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
MANAGED:这个配置几乎没做什么。一般不会使用。
-->
<transactionManager type="JDBC"/>
<!-- 数据源的配置 UNPOOLED/POOLED/JNDI -->
<!--
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接
POOLED- 数据库连接池
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用
-->
<dataSource type="POOLED">
<!-- 取出配置文件中导入的值 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<!-- 环境配置2 -->
<environment id="development2">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 在任意时间存在的活动连接(也就是正在使用)的数量 -->
<property name="poolMaximumActiveConnections" value="10" />
<!-- 任意时间存在的空闲连接数 -->
<property name="poolMaximumIdleConnections" value="5" />
<!-- 在被强制返回之前,池中连接被检查的时间 -->
<property name="poolMaximumCheckoutTime" value="20000" />
<!-- 这是给连接池一个打印日志状态机会的低层次设置,还有重新尝试获得连接,这些情况下往往需要很长时间(为了避免连接池没有配置时静默失败) -->
<property name="poolTimeToWait" value="20000" />
<!-- 发送到数据的侦测查询,用来验证连接是否正常工作,并且准备接受请求 -->
<property name="poolPingQuery" value="NO PING QUERY SET" />
<!-- 这是开启或禁用侦测查询。如果开启,你必须用一个合法的SQL语句(最好是很快速的)设置poolPingQuery属性 -->
<property name="poolPingEnabled" value="false" />
<!-- 这是用来配置poolPingQuery多少时间被用一次。这可以被设置匹配标准的数据库连接超时时间,来避免不必要的侦测 -->
<property name="poolPingConnectionsNotUsedFor" value="0" />
</dataSource>
</environment>
</environments>
<!-- 映射器 -->
<mappers>
<!-- 直接映射到相应的mapper文件 -->
<mapper resource="top/raincither/dao/UserMapper.xml"/>
<!-- 扫描包路径下所有xxMapper.xml文件 需要接口与xml在同一文件夹下-->
<package name="top.raincither.dao" />
<!-- 直接映射到相应的mapper接口 需要接口与xml在同一文件夹下, 一般使用注解用这个-->
<mapper class="top.raincither.dao.UserMapper" />
</mappers>
</configuration>
所导入的配置文件:
# JDBC 配置文件
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username = root
password = admin
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: 命名空间 -->
<!-- 命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时实现接口绑定 -->
<!-- top.raincither.dao.UserMapper为UserMapper接口的全限定名 -->
<mapper namespace="top.raincither.dao.UserMapper">
</mapper>
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
cache– 给定命名空间的缓存配置。开启二级缓存cache-ref– 其他命名空间缓存配置的引用。resultMap– 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。sql– 可被其他语句引用的可重用语句块。insert– 映射插入语句update– 映射更新语句delete– 映射删除语句select– 映射查询语句
一个简单查询的 select 元素 :
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
这个语句名(要实现的接口名)为 getUserById ,接受一个 int(或 Integer)类型的参数,并返回一个 User 类型的对象,其中的键是列名,值便是结果行中的对应值。 #{id} 是告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中。
id在命名空间中唯一的标识符,可以被用来引用这条语句。也就是接口中的方法名parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。如果传入的是基本类型,则可以不写resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
insert, update 和 delete 同理
结果映射(resultMap)
是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
此为个人简略笔记, 不想写的过于繁琐, 特意简化了非常非常非常多,详见官方文档
结果映射归根结底也是使数据库字段与实体类属性对应起来的操作。
(联表)查询时返回数据所对应的实体类中有的属性为数组, 列表 ,对象时或字段名与属性不一致时就要用到结果映射。
ResultMap 的属性
id当前命名空间中的一个唯一标识,用于标识一个结果映射。type要返回的类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。autoMapping如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。
ResultMap 的常用元素
result– 注入到字段或 JavaBean 属性的普通结果association– 一个复杂类型的关联;许多结果将包装成这种类型 也就是对象- 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
collection– 一个复杂类型的集合 (列表 集合 数组)- 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
列子
一个简单的结果映射 (一对一)
这是一个简单的实体类:
package top.raincither.pojo;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}N
public void setUsername(String username) {
this.username = username;
}N
public String getPassword() {
return password;
}p
public void setPassword(String password) {
this.password = password;
}
}
上面这个类有 3 个属性:id,username 和 Password。如果这些属性与数据库中字段名对应,则这样的一个实体类可以被映射到 ResultSet 。
<select id="getUsers" resultType="top.raincither.pojo.User">
select id, username, password
from user
where id = #{id}
</select>
如果与数据库字段名不一致,也可以在语句中设置列别名来完成匹配。而不用显式地配置 resultMap
<select id="getUsers" resultType="top.raincither.pojo.User">
select
user_id as "id",
user_name as "username",
user_password as "password"
from user
where id = #{id}
</select>
也可以显式使用外部的 resultMap
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="userName" column="user_name"/>
<result property="password" column="user_password"/>
</resultMap>
然后在引用它的语句中设置 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:
<select id="getUsers" resultMap="userResultMap">
select id, username, password
from user
where id = #{id}
</select>
示例一 (多对一)
如以下查询:
<select id="getStaff" resultMap="StaffMap">
select
b.id bid,
b.name bname,
s.id sid,
s.name sname
from boos b , staff s
where s.tid = t.id
</select>
staff表有 id name tid(外键: boos的id)
boos表有 id name
staff实体类
public class staff {
private int id;
private String name;
private Boos boos;
boos实体类
public class boos {
private int id;
private String name;
可以看到staff类里有三个属性有一个所属的boos对象,但我们查出了四个值,而且并不对应,这时就要用到结果映射。
<resultMap id="StaffMap" type="staff">
<!--映射基本类型属性 -->
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<!-- association: 映射对象 JavaType:要映射到的实体类完全限定名,或别名-->
<association property="boos" javaType="Boos">
<result property="id" column="bid"/>
<result property="name" column="bname"/>
</association>
</resultMap>
<select id="getStaff" resultMap="StaffMap">
select
b.id bid,
b.name bname,
s.id sid,
s.name sname
from boos b , staff s
where s.tid = t.id
</select>
示例二 (一对多)
staff实体类
public class staff {
private int id;
private String name;
boos实体类
public class boos {
private int id;
private String name;
private List<staff> staffs;
如上 boos 实体类内有属性为列表
<resultMap id="BoosMap" type="Boos">
<result property="id" column="bid"/>
<result property="name" column="bname"/>
<!-- collection: 映射列表 JavaType:要映射到的实体类完全限定名,或别名 ofType: 泛型-->
<collection property="staffs" javaType="ArrayList" ofType="Staff">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
<select id="getBoosById" resultMap="BoosMap">
select s.id sid, s.name sname, b.id bid, b.name tname
from staff s,
boos b
where b.id = s.tid
and b.id = #{id};
</select>
缓存
一级缓存
默认开启一级缓存
在同一个
SqlSession中,Mybatis会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个Map中, 如果后续的键值一样, 则直接从Map中获取数据不同的
SqlSession之间的缓存是不互通的, 也就是说 缓存只在当前SqlSession有效同一个
SqlSession, 可以通过配置flushCache = 'true使得在查询前清空缓存UPDATE,INSERT,DELETE语句默认会清空缓存
二级缓存
二级缓存需要在 mybatis-config.xml 中开启总开关 默认开启
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
还要在 *Mapper.xml 开启 默认是关闭的
<!-- 开启二级缓存 -->
<!-- <cache/> -->
<!-- 所有配置项 -->
<!--
eviction: 清除策略
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval: 刷新间隔 以毫秒为单位的合理时间量 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size: 引用数目 为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。
默认值是 1024。
readOnly: 只读 为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。
因此这些对象不能被修改。这就提供了可观的性能提升。
而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,
因此默认值是 false。
-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
需要手动设置启动二级缓存
二级缓存的作用域是同一个namespace下的mapper映射文件 多个SqlSession共享
- 由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。
增删改操作,无论是否进行提交sqlSession.commit(),均会清空一级、二级缓存,使查询再次从DB中select。
- 二级缓存的清空,实质上是对所查找key对应的value置为null
只在单表上使用二级缓存
- 如果一个表与其它表有关联关系,那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰,所以就有可能出现多个namespace中的数据不一致现象
在查询多于修改时使用二级缓存
- 在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。
动态Sql
对于比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。稍微不注意,由于引号,空格等缺失可能都会导致错误。
利用动态 SQL,可以彻底摆脱这种痛苦。
其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。
动态sql元素:
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
示例用数据库字段 :
user:
id name age city
if
if 如果条件为正则追加
根据 name 和 age 来查询数据。如果 name 为空,那么将只根据 age 来查询;反之如果 age 为空只根据 name 来查询
<select id="getUserByNameAndAge" resultType="User" parameterType="User">
select * from user where
<if test="name != null">
username=#{name}
</if>
<if test="age != null">
and age=#{age}
</if>
</select>
如果 age 为空那么查询语句为 select * from user where name=#{name} 这没问题 但当name为空时 查询语句为 select * from user where and age=#{age},这是错误的
这是不可行的,解决的方法就是 where
where
where 如果它包含的标签中有返回值的话,它就插入一个‘where’ , 此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
在 sql 片段中最好不要包括 where
<select id="getUserByNameAndAge" resultType="User" parameterType="User">
select * from user
<where>
<if test="name != null">
and username=#{name}
</if>
<if test="age != null">
and age=#{age}
</if>
</where>
</select>
这样当都为空时,就会查出所有数据
set
set 同理 如果它包含的标签中有返回值的话,它就插入一个‘set’ , 此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
<update id="updateUserById" parameterType="User">
select * from user
<set>
<if test="name != null">
username=#{name},
</if>
<if test="age != null">
age=#{age},
</if>
</set>
where id = #{id}
</update>
choose(when,otherwise)
choose(when,otherwise) 有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
<select id="getUserByChoose" resultType="User" parameterType="User">
select * from user
<where>
<choose>
<when test="id != null">
id=#{id}
</when>
<when test="name != null">
and name=#{name}
</when>
<otherwise>
and age=#{age}
</otherwise>
</choose>
</where>
</select>
也就是说,这里我们有三个条件,id,name,age,只能选择一个作为查询条件
当前两个都不为null的时候, 那么选择二选一(前者优先),如果都为null, 那么就选择 otherwise中的
如果前两个只有一个不为null, 那么就选择不为null的那个。
foreach
foreach java中有for, 可通过for循环, 同样, mybatis中有foreach, 可通过它实现循环
将一个 List 实例或者数组作为参数对象传给 MyBatis,MyBatis 会自动将它包装在一个 Map 中
<select id="get" resultType="User" parameterType="map">
select *
from user
where id in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
更多
更多详情,请见官方文档
注解开发
注解只适用于 较短sql , 而且不易维护 ,建议不用
//首先在mybatis-config.xml中 注册接口类
@select("select * from user")
List<User> getUserList();
- 本文链接:http://www.raincither.top/record/mybatis/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。


