一、前言
MyBatis
是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
相对于 Hibernate, Mybatis
给开发者提供更便利的数据库查询实现,便于扩展和写出高效的 SQL 语句使他能完全替换旧有的 Hibernate 框架。
二、Mini-Demo
2.1 引入依赖
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--Mybatis框架-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
2.2 创建 Mybatis
全局配置文件
全局配置文件最重要有以下两项定义:
<environments>:
该标签表示环境配置,可以根据不同开发环境配置相应数据库连接信息
,事务管理等。<mappers>:
Mapper
映射文件路径。<?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="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
2.3 根据全局配置文件构造
SqlSessionFactory
String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
不使用 XML 构造 SqlSessionFactory
总结,可以为分如下几步:
- 构造 DataSource 数据源对象
- 构造 TransactionFactory 事务工厂
- 使用 Environment 类组合以上两个对象,并传入 Configuration 配置类
SqlSessionFactoryBuilder 根据配置类构造 SqlSessionFactory
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource(); TransactionFactory transactionFactory = new JdbcTransactionFactory(); Environment environment = new Environment("development", transactionFactory, dataSource); Configuration configuration = new Configuration(environment); configuration.addMapper(BlogMapper.class); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
2.4 根据
SqlSessionFactory
获取SqlSession
会话这一步根据工厂方法获取
SqlSession
会话连接,我们应该注意:SqlSession
为非线程安全。因此,它的作用域为Thread
。每个线程应该拥有自己的SqlSession
。SqlSession
是 Mybatis 框架暴露给开发者查询数据库的接口,包含增、删、改、查功能。SqlSession session = sqlSessionFactory.openSession(); SqlSession session = sqlSessionFactory.openSession(); try { // #1 直接传入statement HelloWorld helloWorld = session.selectOne("com.clarence.mapper.HelloMapper.selectWordById", 1); // #2 根据Class类型获取相应的Mapper代理,调用接口方法查询数据库连接 HelloMapper helloMapper = session.getMapper(HelloMapper.class); HelloWorld helloWorld = helloMapper.selectWordById(1); } finally { session.close(); }
2.5 小结
2.5.1 对象作用域
| 对象 | 使用域 | 描述 | | —- | —- | —- | |
SqlSessionFactoryBuilder
| 局部变量 | 建造者模式
根据 Mybatis 全局配置文件创建 SqlSessionFactory 工厂类 | |SqlSessionFacotry
| 全局
单例对象 | 工厂模式
创建 SqlSession Sql会话处理器 | |SqlSession
| 线程内部
非线程安全 | 获取 SQL 会话连接
提供增、删、改、查 等 SQL 接口 |
2.5.2 命名空间
在每个 Mapper 映射文件中,<Mapper>
标签都有一个 namespace
属性,它就是所谓的命名空间。
<?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="org.mybatis.example.BlogMapper">
</mapper>
一般为接口类的全限定名(比如 org.mybatis.example.BlogMapper
),一般一个 Mapper XML 映射文件与一个接口相对应。
在之前的版本,命名空间的作用并不大,是可选的,但现在,你必须指定命名空间。
三、XML 配置
Mybatis
全局配置属性并不多,更多详见configuration
(配置)
- properties(属性): 引用外部
properties
属性配置文件 - settings(设置): Mybatis 极为重要的设置,它会改变
Mybatis
的运行时行为。- 全局性缓存默认为开启(cacheEnabled=true)
defaultExecutorType
: 配置默认执行器。SIMPLE
: 普通的执行器,默认值REUSE
: 重用预处理语句(preparedStatement
)BATCH
: 执行器不仅重用语句还会执行指更新
mapUnderscoreToCamelCase
: 开启驼峰命名自动映射。默认为FALSE
logImpl
: 指定 Mybatis 所用日志的具体实现,未指定时自动查找- SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
proxyFactory
: 指定 Mybatis 创建可延迟加载对象所用到的代理工具- CGLIB
- JAVASSIST
- typeAliases(类型别名): 可为 Java 类型设置一个缩写名称。仅用于 XML 配置。降低冗余的全限定类名书写。
- 提供
<package>
标签方便批量处理。使用 Bean 的首字母小写的非限定类名作为它的别名。 - Java 基本类型拥有对象的别名
- 提供
- typeHandlers(类型处理器): Mybatis 在预处理语句 (PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将获取到的值以合适的方式转换为 java 类型。(从
3.4.5
开始,Mybatis 默认支持 JSR-310 日期和时间 API)- 你可以重写已有的类型处理器或创建自定义类型处理器来处理不支持或非标准的类型。
- 实现
org.apache.ibatis.type.TypeHandler
接口或继承org.apache.ibatis.type.BaseTypeHandler
抽象类,可选的将它映射到一个 JDBC 类型
- 实现
- 添加至全局配置文件中
- Mybatis 不会通过检测数据库元信息来决定使用哪种类型,所以必须在参数和结果映射中指明字段类型,以使其能够绑定到正确的类型处理器上。这是因为 Mybatis 直到语句被执行时才清楚数据类型。
- 你可以重写已有的类型处理器或创建自定义类型处理器来处理不支持或非标准的类型。
- objectFactory(对象工厂)
- plugins(插件): Mybatis 允许你在映射语句执行过程中的某一点进行拦截调用。包括
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)。如果使用 Spring + Mybatis 则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
JDBC
: 直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。MANAGED
: 几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
- dataSource(数据源): 有三种内建数据源。
UNPOOLED
: 每次请求时打开和关闭连接。POOLED
: 数据库连接池。JNDI
: EJB容器使用。
- transactionManager(事务管理器)。如果使用 Spring + Mybatis 则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
-
四、XML 映射文件
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。
4.1 配置元素
cache
– 该命名空间的缓存配置。cache-ref
– 引用其它命名空间的缓存配置。resultMap
– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。sql
– 可被其它语句引用的可重用语句块。insert
– 映射插入语句。update
– 映射更新语句。delete
– 映射删除语句。select
– 映射查询语句。4.2 Select 元素标签属性
| 属性 | 描述 | | :—- | :—- | | id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 | | parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 | | resultType | 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合
,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 | | resultMap | 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 | | flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 | | useCache | 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 | | timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 | | fetchSize | 这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。 | | statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 | | resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。 | | databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 | | resultOrdered | 这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false。 | | resultSets | 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
4.3 insert、update 和 delete
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">
<update
id="updateAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
<delete
id="deleteAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
属性 | 描述 |
---|---|
useGeneratedKeys | (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn | (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
4.4 获取自动生成的 ID
<!--单个-->
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
<!--批量-->
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>
4.5 不支持自动增长
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>
selectKey 元素的属性
属性 | 描述 |
---|---|
keyProperty | selectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn | 返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
resultType | 结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。 |
order | 可以设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。 |
statementType | 和前面一样,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 类型的映射语句,分别代表 Statement, PreparedStatement 和 CallableStatement 类型 |
4.6 sql
代码模板,定义可重用的 SQL 代码片段,以便在其它语句中使用。参数在加载的时候确定下来,并且可以在不同的 include 元素中定义不同的参数上
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>
4.7 参数
对于传入对象,则为复杂参数,拥有如下行为:
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
User 类型的参数对象传递到了语句中,会查找 id 、username 和 password 属性,然后将它们的值传入预处理语句的参数中。
和 MyBatis 的其它部分一样,几乎总是可以根据参数对象的类型确定 javaType,除非该对象是一个 HashMap
。这个时候,你需要显式指定 javaType
来确保正确的类型处理器(TypeHandler
)被使用。mode
属性允许设置 IN
、OUT
、INOUT
参数,将会修改参数对象的属性值,以便作为输出参数返回。
4.8 字符串替换 ${}
4.9 结果映射
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 JDBC 的 ResultSets 解放出来。
4.9.1 ResultType
<!-- mybatis-config.xml 中 -->
<typeAlias type="com.someapp.model.User" alias="User"/>
<!-- SQL 映射 XML 中 -->
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
这种情况下,Mybatis 会自动创建一个 ResultMap,再根据属性名称映射列到 JavaBeans 属性上。如果列名和属性名不能匹配,可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配。
4.9.2 ResultMap
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
4.9.3 高级结果映射
constructor
: 用于在实例化类时,注入结果到构造方法中- idArg: ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg: 将被注入到构造方法的一个普通结果
id
result
: 注入到字段或 JavaBean 属性的普通结果association
: 一个复杂类型的关联;许多结果将包装成这种类型- 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
collection
: 一个复杂类型的集合- 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
discriminator
: 使用结果值来决定使用哪个resultMap- case: 基于某些值的结果映射
- 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
- case: 基于某些值的结果映射
ResultMap 的属性列表
属性 | 描述 |
---|---|
id | 当前命名空间中的一个唯一标识,用于标识一个结果映射。 |
type | 类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。 |
autoMapping | 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。 |
id & result
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
这些元素是结果映射的基础。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。 id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用,以提高性能。尤其在进行缓存和嵌套结果映射(连接映射)的时候。
构造方法
<constructor>
<idArg column="id" javaType="int"/>
<arg column="username" javaType="String"/>
<arg column="age" javaType="_int"/>
</constructor>
<!--存在多个构造器-->
<constructor>
<idArg column="id" javaType="int" name="id" />
<arg column="age" javaType="_int" name="age" />
<arg column="username" javaType="String" name="username" />
</constructor>
关联
<association property="author" column="blog_author_id" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
</association>
关联嵌套 Select 查询
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id="selectAuthor" resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}
</select>
这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 概括地讲,N+1 查询问题是这样子的:
- 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
- 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。
关联的嵌套结果映射
<select id="selectBlog" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio
from Blog B left outer join Author A on B.author_id = A.id
where B.id = #{id}
</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<association property="author" column="blog_author_id"
javaType="Author" resultMap="authorResult"/>
</resultMap>
<resultMap id="authorResult" type="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</resultMap>
重要
id
元素在嵌套结果映射中扮演着非常重要的角色。你应该总是指定一个或多个可以唯一标识结果的属性。 虽然,即使不指定这个属性,MyBatis 仍然可以工作,但是会产生严重的性能问题。 只需要指定可以唯一标识结果的最少属性。显然,你可以选择主键(复合主键也可以)。
关联多结果集 (ResultSet)
从版本 3.2.3
开始,Mybatis 提供了另一种解决N + 1 查询问题的方法。
某些数据库允许存储过程返回多个结果集,或一次性执行多个语句,每个语句返回一个结果集。我们可以利用这个特性,在不使用连接的情况下,只访问数据库一次就能获得相关数据。
SELECT * FROM BLOG WHERE ID = #{id}
SELECT * FROM AUTHOR WHERE ID = #{id}
在映射语句中,必须通过 resultSets 属性为每个结果集指定一个名字,多个名字使用逗号隔开。
<select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">
{call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}
</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column="id" />
<result property="title" column="title"/>
<association property="author" javaType="Author" resultSet="authors" column="author_id" foreignColumn="id">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="bio" column="bio"/>
</association>
</resultMap>
集合
<collection property="posts" ofType="domain.blog.Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
集合嵌套结果映射
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>
-
鉴别器
有时候,一个数据为查询可能会返回多个不同的结果集,使用鉴别器就是来处理这类情况,很像 Java 语文中的
switch
语句:<resultMap id="vehicleResult" type="Vehicle"> <id property="id" column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultType="carResult"> <result property="doorCount" column="door_count" /> </case> <case value="2" resultType="truckResult"> <result property="boxSize" column="box_size" /> <result property="extendedCab" column="extended_cab" /> </case> <case value="3" resultType="vanResult"> <result property="powerSlidingDoor" column="power_sliding_door" /> </case> <case value="4" resultType="suvResult"> <result property="allWheelDrive" column="all_wheel_drive" /> </case> </discriminator> </resultMap>
4.10 缓存
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行
<cache />
基本上就是这样。这个简单语句的效果如下:
映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
这些属性可以通过 cache 元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
4.10.1 使用自定义缓存
<cache type="com.domain.something.MyCustomCache"/>
五、动态SQL
动态 SQL 是 MyBatis 的强大特性之一。有以下几类标签
<a name="jAlIi"></a>
## 5.2 choose、when、otherwise
```xml
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
5.3 trim、where、set
where
元素只会在子元素返回任何内容的情况下才插入 where
子句。
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
_prefixOverrides_
属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有_prefixOverrides_
属性中指定的内容,并且插入 prefix 属性中指定的内容。
5.4 foreach
5.5 动态 SQL 中的插入脚本语文
六、动态API
6.1 SqlSession
使用 MyBatis 的主要 Java 接口就是 SqlSession。你可以通过这个接口来执行命令,获取映射器示例和管理事务。
SqlSessionFactory 对象包含创建 SqlSession 实例的各种方法。而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或 Java 配置代码来创建 SqlSessionFactory。
当 Mybatis 与一些依赖注入框架(如 Spring 或者 Guice)搭配使用时,SqlSession 将被依赖注入框架创建并注入,所以你不需要使用 SqlSessionFactoryBuilder 或者 SqlSessionFactory,
6.2 事务隔离级别
事务隔离级别支持 JDBC 的五个隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),并且与预期的行为一致。
6.3 SqlSession 高级 API
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)
RowBounds 参数会告诉 MyBatis 略过指定数量的记录,并限制返回结果的数量。RowBounds 类的 offset 和 limit 值只有在构造函数时才能传入,其它时候是不能修改的。
数据库驱动决定了略过记录时的查询效率。为了获得最佳的性能,建议将 ResultSet 类型设置为 SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(换句话说:不要使用 FORWARD_ONLY)。
6.4 映射器注解
注解 | 使用对象 | XML 等价形式 | 描述 |
---|---|---|---|
@CacheNamespace | 类 | 为给定的命名空间(比如类)配置缓存。属性:implemetation、eviction、flushInterval、size、readWrite、blocking、properties。 | |
@Property | N/A | 指定参数值或占位符(placeholder)(该占位符能被 mybatis-config.xml 内的配置属性替换)。属性:name、value。(仅在 MyBatis 3.4.2 以上可用) | |
@CacheNamespaceRef | 类 | 引用另外一个命名空间的缓存以供使用。注意,即使共享相同的全限定类名,在 XML 映射文件中声明的缓存仍被识别为一个独立的命名空间。属性:value、name。如果你使用了这个注解,你应设置 value 或者 name 属性的其中一个。value 属性用于指定能够表示该命名空间的 Java 类型(命名空间名就是该 Java 类型的全限定类名),name 属性(这个属性仅在 MyBatis 3.4.2 以上可用)则直接指定了命名空间的名字。 | |
@ConstructorArgs | 方法 | 收集一组结果以传递给一个结果对象的构造方法。属性:value,它是一个 Arg 数组。 | |
@Arg | N/A | - - |
ConstructorArgs 集合的一部分,代表一个构造方法参数。属性:id、column、javaType、jdbcType、typeHandler、select、resultMap。id 属性和 XML 元素 |
@TypeDiscriminator | 方法 | 决定使用何种结果映射的一组取值(case)。属性:column、javaType、jdbcType、typeHandler、cases。cases 属性是一个 Case 的数组。 | |
@Case | N/A | 表示某个值的一个取值以及该取值对应的映射。属性:value、type、results。results 属性是一个 Results 的数组,因此这个注解实际上和 ResultMap 很相似,由下面的 Results 注解指定。 | |
@Results | 方法 | 一组结果映射,指定了对某个特定结果列,映射到某个属性或字段的方式。属性:value、id。value 属性是一个 Result 注解的数组。而 id 属性则是结果映射的名称。从版本 3.5.4 开始,该注解变为可重复注解。 | |
@Result | N/A | - - |
在列和属性或字段之间的单个结果映射。属性:id、column、javaType、jdbcType、typeHandler、one、many。id 属性和 XML 元素 |
@One | N/A | 复杂类型的单个属性映射。属性: select,指定可加载合适类型实例的映射语句(也就是映射器方法)全限定名; fetchType,指定在该映射中覆盖全局配置参数 lazyLoadingEnabled; resultMap(available since 3.5.5), which is the fully qualified name of a result map that map to a single container object from select result; columnPrefix(available since 3.5.5), which is column prefix for grouping select columns at nested result map. 提示 注解 API 不支持联合映射。这是由于 Java 注解不允许产生循环引用。 | |
@Many | N/A | 复杂类型的集合属性映射。属性: select,指定可加载合适类型实例集合的映射语句(也就是映射器方法)全限定名; fetchType,指定在该映射中覆盖全局配置参数 lazyLoadingEnabled resultMap(available since 3.5.5), which is the fully qualified name of a result map that map to collection object from select result; columnPrefix(available since 3.5.5), which is column prefix for grouping select columns at nested result map. 提示 注解 API 不支持联合映射。这是由于 Java 注解不允许产生循环引用。 | |
@MapKey | 方法 | 供返回值为 Map 的方法使用的注解。它使用对象的某个属性作为 key,将对象 List 转化为 Map。属性:value,指定作为 Map 的 key 值的对象属性名。 | |
@Options | 方法 | 映射语句的属性 | 该注解允许你指定大部分开关和配置选项,它们通常在映射语句上作为属性出现。与在注解上提供大量的属性相比,Options 注解提供了一致、清晰的方式来指定选项。属性:useCache=true、flushCache=FlushCachePolicy.DEFAULT、resultSetType=DEFAULT、statementType=PREPARED、fetchSize=-1、timeout=-1、useGeneratedKeys=false、keyProperty=””、keyColumn=””、resultSets=””, databaseId=””。注意,Java 注解无法指定 null 值。因此,一旦你使用了 Options 注解,你的语句就会被上述属性的默认值所影响。要注意避免默认值带来的非预期行为。 The databaseId(Available since 3.5.5), in case there is a configured DatabaseIdProvider, the MyBatis use the Options with no databaseId attribute or with a databaseId that matches the current one. If found with and without the databaseId the latter will be discarded. 注意:keyColumn 属性只在某些数据库中有效(如 Oracle、PostgreSQL 等)。要了解更多关于 keyColumn 和 keyProperty 可选值信息,请查看“insert, update 和 delete”一节。 |
- @Insert - @Update - @Delete - @Select |
方法 | - - - - |
每个注解分别代表将会被执行的 SQL 语句。它们用字符串数组(或单个字符串)作为参数。如果传递的是字符串数组,字符串数组会被连接成单个完整的字符串,每个字符串之间加入一个空格。这有效地避免了用 Java 代码构建 SQL 语句时产生的“丢失空格”问题。当然,你也可以提前手动连接好字符串。属性:value,指定用来组成单个 SQL 语句的字符串数组。 The databaseId(Available since 3.5.5), in case there is a configured DatabaseIdProvider, the MyBatis use a statement with no databaseId attribute or with a databaseId that matches the current one. If found with and without the databaseId the latter will be discarded. |
- @InsertProvider - @UpdateProvider - @DeleteProvider - @SelectProvider |
方法 | - - - - |
允许构建动态 SQL。这些备选的 SQL 注解允许你指定返回 SQL 语句的类和方法,以供运行时执行。(从 MyBatis 3.4.6 开始,可以使用 CharSequence 代替 String 来作为返回类型)。当执行映射语句时,MyBatis 会实例化注解指定的类,并调用注解指定的方法。你可以通过 ProviderContext 传递映射方法接收到的参数、”Mapper interface type” 和 “Mapper method”(仅在 MyBatis 3.4.5 以上支持)作为参数。(MyBatis 3.4 以上支持传入多个参数)属性:type、method。type 属性用于指定类名。method 用于指定该类的方法名(从版本 3.5.1 开始,可以省略 method 属性,MyBatis 将会使用 ProviderMethodResolver 接口解析方法的具体实现。如果解析失败,MyBatis 将会使用名为 provideSql 的降级实现)。提示 接下来的“SQL 语句构建器”一章将会讨论该话题,以帮助你以更清晰、更便于阅读的方式构建动态 SQL。 The databaseId(Available since 3.5.5), in case there is a configured DatabaseIdProvider, the MyBatis will use a provider method with no databaseId attribute or with a databaseId that matches the current one. If found with and without the databaseId the latter will be discarded. |
@Param | 参数 | N/A | 如果你的映射方法接受多个参数,就可以使用这个注解自定义每个参数的名字。否则在默认情况下,除 RowBounds 以外的参数会以 “param” 加参数位置被命名。例如 #{param1}, #{param2}。如果使用了 @Param(“person”),参数就会被命名为 #{person}。 |
@SelectKey | 方法 | 这个注解的功能与 |
|
@ResultMap | 方法 | N/A | 这个注解为 @Select 或者 @SelectProvider 注解指定 XML 映射中 |
@ResultType | 方法 | N/A | 在使用了结果处理器的情况下,需要使用此注解。由于此时的返回类型为 void,所以 Mybatis 需要有一种方法来判断每一行返回的对象类型。如果在 XML 有对应的结果映射,请使用 @ResultMap 注解。如果结果类型在 XML 的 |
@Flush | 方法 | N/A | 如果使用了这个注解,定义在 Mapper 接口中的方法就能够调用 SqlSession#flushStatements() 方法。(Mybatis 3.3 以上可用) |
@Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
@SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);
@Insert("insert into table2 (name) values(#{name})")
@SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);