MyBatis魔法堂:即学即用篇
一、前言
本篇内容以理解 MyBatis 的基本用法和快速在项目中实践为目的,遵循 Make it work,better and excellent 原则。 技术栈为 MyBatis3.2.7+log4j1.2.17+sqlite3+jdk1.7。
二、示例
示例代码功能:
学校人员管理系统,对象分别为学生、教师和班级,学生与班级为多对一关系,班级与教师为一对多关系。示例代码主要为操作学生对象。
1. 表结构
学生表(TStudentML)
字段 | 数据类型 | 备注 |
ID | Integer | 主键 |
Name | Text | 姓名 |
ClassID | Integer | 班级 ID |
班级表(TClassML)
字段 | 数据类型 | 备注 |
ID | Integer | 主键 |
Name | Text | 姓名 |
教师表(TTeacherML)
字段 | 数据类型 | 备注 |
ID | Integer | 主键 |
Name | Text | 姓名 |
ClassID | Integer | 班级 ID |
2. 项目目录结构
src |-- com | |-- entity | | |-- ETeacher | | |-- EStudent | | |-- EClass | |-- dao | |-- studentMapper.xml | |-- StudentMapper.java | |-- DBase.java | |-- DStudent.java |-- mybatis-config.xml |-- db.db |-- log4j.properties |-- config.properties
3. 数据库配置文件(config.properties)
driver=org.sqlite.JDBC
url=jdbc:sqlite:src/db.db
4. log4j.properties
log4j.rootLogger=TRACE, studout log4j.appender.studout=org.apache.log4j.ConsoleAppender log4j.appender.studout.layout=org.apache.log4j.PatternLayout log4j.appender.studout.layout.ConversionPattern=[%5p]%t -%m%n
5. 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 配置文件,后面内容则通过 ${属性名} 来引用属性值
在使用实例化 SqlSessionFactory 时,还可以通过 new SqlSessionFactoryBuilder.build(config.xml 的 InputStream 实例, Properties 实例) 来设置属性值。
优先级从高到低是:
1. 通过 build 方法入参设置
2. 通过 resource 引入的属性
3. 通过 property 标签设置的属性
--> <properties resource="config.properties"></properties>
<!-- 强制指定 MyBatis 使用 log4j 作为日志日志框架,若不指定那么当部署到如 Tomcat 等应用容器时,会被容器设置为使用 common-logging 来记录日志 --> <settings> <setting name="logImpl" value="LOG4J"/> </settings>
<!-- 设置自定义 JAVA 类型的别名,否则在映射文件中的 resultType、parameterType 等特性值就需要填写全限定类名才有效 --> <typeAliases>
<!--
这时对于包下的类,在映射文件中的 resultType、parameterType 等特性值,我们只需写类名或首字母小写的类名
当自定义 JAVA 类配合 @Aliase("别名") 使用时,只需写别名即可,且不区分大小写
MyBatis 对 JAVA 原生类型定义了内置别名:
`int`,`long` 等就是 `_int`,`_long`
`Integer`,`String` 就是 `int`,`string`
--> <package name="com.entity"/> </typeAliases> <environments default="dev">
<!-- 运行环境配置 --> <environment id="dev">
<!--
type 属性用于指定事务管理器类型
JDBC:使用 JDBC 的提交和回滚设置,依赖从数据源获取的连接来管理事务范围。
MANAGED:让容器(如 Spring)来管理事务的生命周期。默认情况会关闭连接,
若不想关闭连接则需要如下配置:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
--> <transactionManager type="JDBC"></transactionManager>
<!--
type 属性用于指定连接池类型
UNPOOLED:连接用完就关闭,不放到连接池
POOLED:连接用完则放在连接池
--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <!-- 虽然 sqlite 不用填写 username 和 password,但这两个节点必须保留,否则将报错 --> <property name="username" value=""/> <property name="password" value=""/> </dataSource> </environment> </environments>
<!-- 向 MyBatis 注册映射信息 --> <mappers> <mapper resource="com/dao/studentMapper.xml"></mapper> </mappers> </configuration>
6. 实体类
// 教师实体类 public class ETeacher{ private int id; private String name; // 省略各种 setter 和 getter....... }// 班级实体类
public class EClass{
private int id;
private String name;
// 省略各种 setter 和 getter.......
}// 学生实体类
public class EStudent{
private int id;
private String name;
private EClass myClass;
private List<ETeacher> teachers;
// 省略各种 setter 和 getter.......
}
7. 映射接口
public interface StudentMapper{ /** * 通过姓名获取学生信息(不含教师、班级信息) * @param name 学生姓名 * @return */ EStudent getJustStudentInfo(String name);/
- 通过姓名模糊查询学生信息(不含教师、班级信息)
- @param name 学生姓名
- @return
*/
List<EStudent> getJustStudentInfos(String name);/
- 通过姓名和班级名称获取学生信息(含教师、班级信息)
- @param studentName 学生姓名
- @param className 班级名称
- @return
*/
EStudent getStudentInfo(String studentName, String className);/
- 新增学生信息
- @param student
- @return
*/
void addStudent(EStudent student);/
- 删除学生信息
- @param id
- @return
*/
void delStudent(int id);
}
8. 映射文件
<?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="main.dao.StudentMapper"> <select id="getJustStudentInfo" resultType="EStudent"> select ID,Name from TStudentML where Name = #{0} </select><select id="getJustStudentInfos" resultType="EStudent">
select * from TStudentML
<if test="#{0} != null">
where name like '%'+#{0}+'%'
</if>
</select><select id="getStudentInfo" resultMap="getStudentInfoResultMap">
select
a.ID a_id, a.Name a_name,
b.ID b_id, b.Name b_name,
c.ID c_id, c.Name c_name, c.ClassID c_classId
from
TStudentML a left outer join TClassML b on(a.ClassID=b.ID)
left outer join TTeacherML c on(b.ID=c.ClassID)
<trim prefix="where" prefixOverrides="and">
<if test="#{0} != null">
a.Name like '%'+#{0}+'%'
</if>
<if test="#{1} != null">
and b.Name like '%'+#{1}+'%'
/if>
</trim>
</select>
<resultMap id="getStudentInfoResultMap" type="EStudent">
<id property="id" column="a_id"/>
<result property="name" column="a_name"/>
<!-- 一对一关系 -->
<association property="myClass" javaType="EClass">
<id property="id" column="b_id"/>
<result property="name" column="b_name"/>
</association>
<!-- 一对多关系 -->
<collection property="teachers" ofType="ETeacher">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<result property="classId" column="c_classId"/>
</collection>
</resultMap><insert id="addStudent" parameterType="EStudent">
insert into TStudentML(Name, ClassID) values(#{name}, #{myClass.id})
</insert><delete id="delStudent">
delete from TStudentML where ID = #{0}
</delete>
</mapper>
9. 数据库操作基类
public class DBase{ private static SqlSessionFactory factory = null; public DBase(){ if (factory != null) return;InputStream </span><span style="color: rgba(0, 0, 255, 1)">is</span> = <span style="color: rgba(0, 0, 255, 1)">this</span>.getClass().getClassLoader().getResourceAsStream(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">mybatis-config.xml</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); factory </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> SqlSessionFactoryBuilder().build(<span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)">);
}
/
- 获取 SqlSession 实例,使用后需调用实例的 close() 函数释放资源
- @return
*/
protected SqlSession openSession(){
return factory.openSession();
}
}
10. 数据库操作类
public class DStudent extends DBase{ /** * 通过姓名获取学生信息(不含教师、班级信息) * @param name 学生姓名 * @return */ public EStudent getJustStudentInfo(String name){ EStudent ret = null; SqlSession session = openSession(); try{ StudentMapper sm = session.getMapper(StudentMapper.class); ret = sm.getJustStudentInfo(name); } finally{session.close(); }</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ret; } </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* * 通过姓名模糊查询学生信息(不含教师、班级信息) * @param name 学生姓名 * @return </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> List<EStudent><span style="color: rgba(0, 0, 0, 1)"> getJustStudentInfos(String name){ List</span><EStudent> ret = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; SqlSession session </span>=<span style="color: rgba(0, 0, 0, 1)"> openSession(); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{ StudentMapper sm </span>= session.getMapper(StudentMapper.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); ret </span>=<span style="color: rgba(0, 0, 0, 1)"> sm.getJustStudentInfos(name); } </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">{ session.close(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ret; } </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* * 通过姓名和班级名称获取学生信息(含教师、班级信息) * @param name 学生姓名 * @param className 班级名称 * @return </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> EStudent getStudentInfo(String name, String className){ EStudent ret </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; SqlSession session </span>=<span style="color: rgba(0, 0, 0, 1)"> openSession(); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{ StudentMapper sm </span>= session.getMapper(StudentMapper.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); ret </span>=<span style="color: rgba(0, 0, 0, 1)"> sm.getStudentInfo(name, className); } </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">{ session.close(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ret; } </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* * 新增学生信息 * @param student </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> addStudent(EStudent student){ SqlSession session </span>=<span style="color: rgba(0, 0, 0, 1)"> openSession(); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{ StudentMapper sm </span>= session.getMapper(StudentMapper.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); sm.addStudent(student); session.commit(); } </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">{ session.close(); } } </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* * 删除学生信息 * @param id </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> delStudent(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> id){ SqlSession session </span>=<span style="color: rgba(0, 0, 0, 1)"> openSession(); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{ StudentMapper sm </span>= session.getMapper(StudentMapper.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); sm.delStudent(id); session.commit(); } </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">{ session.close(); } }
}
三、基础知识
作为一个 ORM 框架,可以知道其至少由对象模型转换为关系模型、关系模型转换为对象模型和缓存管理这三个模块组成。
MyBatis 在对象模型转换为关系模型模块的实现方式是对象模型实例属性 + 自定义 SQL 语句,好处是对 SQL 语句的可操作性高,同时简化 SQL 入参的处理;坏处是对于简单的单表操作,依旧要写 SQL 语句,无法由对象模型自动生成 SQL,最明显的问题是在开发初期数据表结构不稳定,一旦表结构改了,代码上不仅要改对象模型还要改 SQL 语句(不过 MyBatis 也考虑到这点,通过 <sql> 标签实现 SQL 语句复用,缓解这样问题)。
关系模型转换为对象模型则采用关系模型结果集字段映射到对象模型实体字段的方式处理。
缓存模块则分为 SQL 语句缓存和查询数据缓存两种,由于 MyBatis 需要开发者自定义 SQL 语句,因此 SQL 语句缓存不用考虑;而查询数据缓存则被分为一级和二级缓存,一级缓存以事务为作用域,二级缓存以同一个映射集为作用域,而且二级缓存采用直写的方式处理缓存数据被修改和删除的情况。
(本人不才,曾开发轻量级 ORM 框架 LessSQL.Net,由于设计为 SQL 语句必须由对象模块实例映射生成,而关系模型数据集合无法自动填充任意的对象模型实体中,无法支撑复杂的查询语句,而缓存方面仅实现了 SQL 语句缓存性能优化有限,因此框架仅适用于小型工具软件。因为踩过这些坑,所以对 ORM 框架有一点浅薄的认识和看法)
言归正转,我们一起了解 MyBatis 的基础知识吧。
1. MyBatis 框架配置文件
实际上就是 MyBatis 会话工厂的配置文件,用于配置如缓存、日志框架、数据库链接信息、两种模型间转换的处理器和注册映射集等。通过上文大家应该知道如何 Make it work 了。而 Make it better 也是从这里出发。
2. 映射集
映射集是由多个“标识”——“SQL 语句”组成,映射记录上还有如入参类型、返回类型等信息,为对象关系模型转换引擎提供元数据。
设置映射集的方式有两种,一种是通过接口,一种通过 xml 文档。但上文示例采用两者相结合的方式,综合两者优点。
[a]. 映射接口方式
public interface StudentMapper{ @Select("select * from TStudentML where Name=#{0}")EStudent getJustStudent(String name);@Insert("insert into TStudentML(Name, ClassID) values(#{name}, #{myClass.id})")
void addStudent(EStudent student);@Delete("delete from TStudentML where ID=#{0}")
void delStudent(int id);@Update("update TStudentML set Name=#{name},ClassID=#{myClass.id} where ID=#{id}")
void updateStudent(EStudent student);
}
很明显通过接口方式定义映射集是无法实现上文中复杂的查询操作,而好处就是代码量锐减,且由于使用了接口,所以 IDE 的智能提示和编译时语法、依赖关系检查会降低编码错误的风险。
使用接口方式需要将 mybatis-config.xml 中的 mapper 改为如下内容
<mappers> <mapper class="com.dao.StudentMapper"></mapper> </mappers>
<!--
或注册某包下的所有映射接口
<mappers>
<package name="com.dao"/>
</mappers>
-->
[b]. 映射 XML 文件
上文示例已经展示了映射 XML 文件的用法,下面我们逐个细节理解。
1. select、update、delete 和 insert 标签用于填写对应动作的 SQL 语句。
2. parameterType 属性就是入参类型具体法则如下 (parameterMap 已被 deprecated,所以不理也罢):
a. parameterType 为 POJO 类型时,可通过 #{属性名} 填入属性值(该属性值经过防 SQL 注入处理),也可通过 ${name} 填入属性 raw 值(未经过防 SQL 注入处理的属性值),更爽的是 #{} 支持短路径操作如上文中的 #{myClass.id} 。
b. parameterType 为 int、long 等值类型时,当仅有一个入参时,可以使用 #{任意字符} 填入属性值,但无法通过 ${任意字符串} 填入属性 raw 值(报找不到改实例属性),还可以通过 #{0} 和 #{param0} 来填入属性值;而入参为多个时,则只能使用 #{0} 到 #{n} 和 #{param0} 到 #{paramn} 来填入属性值了;但由于动态 SQL 下的标签仅识别 #{0} 等格式的占位符,因此建议通过使用 #{0} 格式的占位符,保持代码一致性。
3. resultType 属性就是返回值类型。
4. sql 标签则用于重用 SQL 片段,示例如下:
<sql id="studentCols"> Name, ClassID </sql> <select id="qryStudent" resultType="EStudent"> select <include id="studentCols"/> from TStudentML </select>
5. 模糊查询
网上有很多做法,但试过不是效果不好,就是报错,误打误撞发现最原始的做法 '%'+#{0}+'%'就 OK 了!
6. 一对一关系
一对一关系 MyBatis 为我们提供 嵌套结果、嵌套查询 两种查询方式。由于嵌套查询需要向数据库执行两次查询操作,因此推荐使用嵌套结果方式。
嵌套结果示例:
<!-- resultMap 属性值为配置返回值映射信息的 resultMap 标签的 id 值 -->
<select id="getClass" parameterType="int" resultMap="ClassResultMap"> select * from class c inner join teacher t on(c.tid = t.id) where t.id = #{0} </select>
<!-- type 属性值为返回值的 JAVA 数据类型 --> <resultMap id="ClassResultMap" type="Cls">
<!--
id 标签表示对象属性对应的是表主键
result 标签表示对象属性对应的是普通表字段
注意:必须用 id 或 result 标出需要返回的字段 / 属性映射,否则在查询多条记录时,仅会返回最后一条记录
--> <id property="id" column="c_id"/> <result property="name" column="c_name"/>
<!--
一对一关系采用 association 标签配置关联信息
javaType 为 JAVA 数据类型
--> <association property="teacher" javaType="Teacher"> <id property="id" column="t_id"/> <result property="name" column="t_name"/> </association> </resultMap>
嵌套查询示例:
<select id="getClass" parameterType="int" resultMap="ClassResultMap"> select * from class where tid = #{id} </select> <select id="getTeacher" parameterType="int" resultType="Teacher"> select t_id id, t_name name from teacher where t_id = #{0} </select> <resultMap id="ClassResultMap" type="Cls"> <id property="id" column="c_id"/> <result property="name" column="c_name"/>
<!--
select 属性值为第二执行 SQL 语句 id
而 column 属性值为传递给第二执行 SQL 语句的入参,而且入参为第一次 SQL 语句的查询结果集字段值
注意:若嵌套查询的条件不只一个,那么就需要将 column 属性设置为 column="{prop1: fie;d1, prop2: field2}",然后嵌套查询的 SQL 中通过 #{prop1},#{prop2} 获取查询条件值
--> <association prorperty="teacher" column="tid" select="getTeacher"> </association> </resultMap>
7. 一对多关系
一对多关系同样分为 嵌套结果 和嵌套查询两种,由于嵌套查询会由于 N+1 次查询导致性能下降,一般推荐使用嵌套结果的做法,但有些查询操作必须使用嵌套查询才能完成。
嵌套结果示例:
<select id="getClass" parameterType="int" resultMap="ClassResultMap"> select * from class c inner join student s on(c.id = s.cid) where c.id = #{id} </select> <resultMap id="ClassResultMap" type="Cls">
<!--
注意:必须用 id 或 result 标出需要返回的字段 / 属性映射,否则在查询多条记录时,仅会返回最后一条记录
--> <id property="id" column="c_id"/> <result property="name" column="c_name"/>
<!--
一对多关系采用 collection 标签来配置映射信息
ofType 属性值为返回值的 JAVA 数据类型
--> <collection prorperty="students" ofType="Student"> <id property="id" column="s_id"/> <result property="name" column="s_name"/> </collection> </resultMap>
嵌套查询示例:
<select id="getClass" parameterType="int" resultMap="ClassResultMap"> select * from class where tid = #{id} </select> <select id="getStudents" parameterType="int" resultType="Student"> select s_id id, s_name name from student where cid = #{id} </select> <resultMap id="ClassResultMap" type="Cls">
<!-- 注意:若不写这句,那么 c_id 字段将作为嵌套查询的条件,而不会赋值到 id 属性了 --> <id property="id" column="c_id"/> <result property="name" column="c_name"/> <collection prorperty="students" column="c_id" select="getStudents"> </collection> </resultMap>
8. 动态 SQL
MyBatis 的动态 SQL 与数据库中通过 exec、sp_executesql() 等执行的动态 SQL 目的是一致的,只是操作形式的不同而已。MyBatis 的动态 SQL 定义方式上与文本模板定义无异,定义后均为经过类似于模板引擎的模块进行解析得到最终的数据。下面我们来了解具体的标签吧。
[a]. <if test="逻辑条件判断"></if>
如果 test 内返回 true,则标签体的内容将被添加到最终结果中。示例:
<if test="name != null and job != null"> and Name like '%'+#{name}+'%' and Description like '%'+#{job}+'%' </if>
注意:test 语句中的逻辑条件判断必须使用入参的属性名或键名,而不能使用 #{0} 或 #{1} 等形式的入参,否则条件判断一律视为 true。
[b]. <choose></choose>
相当于 Java 的 switch 语句。示例:
<choose> <when test="#{title} !=null"> and Title = #{title} </when> <when test="#{name} !=null"> and Name = #{name} </when> <otherwise> and Age > 10 </otherwise> </choose>
[c]. <where></where>
用于处理动态条件时,where 留存与否的尴尬。具体就是
select * from tbl where <if test="#{name}!=null"> Name = #{name} </if> <if test="#{title}!=null"> and Title = #{title} </if>
当两个条件都不符合时,sql 语句就变成 select * from tbl where ,报错是必然的。而 where 标签 会根据其标签体是否有值来决定是否插入 where 关键字,并会自动去除无用的 or 和 and 关键字。示例:
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG <where> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </where> </select>
[d]. <set></set>
用于在 update 语句中,动态设置更新的列。示例:
<update id="updateAuthorIfNecessary"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>
[e].<trim></trim>
当标签体有内容时则为内容添加前缀、后缀,而且可以除去内容前后部分内容。与 where 标签 功能相同的示例:
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG
<!--
prefix 属性值为添加到内容的前缀信息
prefixOverrides 属性值为除去内容前的内容,当需要除去多个内容时,使用管道符 | 分割,注意:空格也将被除去
--> <trim prefix="where" prefixOverrides="AND |OR "> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </trim> </select>
与 set 标签 功能相同的示例:
<update id="updateAuthorIfNecessary"> update Author <trim prefix="set" suffixOverrides=","> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </trim> where id=#{id} </update>
[f]. <foreach></foreach>
主要用于 IN 关键字,示例:
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
入参为 List 或 Array 类型时,MyBatis 会自动将其添加到内部 Map 对象中,并且 List 类型对象的键名为 list,Array 类型对象的键名为 array,并通过 foreach 标签 的 collection 属性指定入参的键名。而 item 属性用于指定 foreach 标签 内集合元素的占位符名称,index 属性则指定 foreach 标签 内当前元素索引的占位符名称,而 open、close 和 separator 属性则分别指定动态 SQL 的开头、结束文本和集合元素项间的分隔符。
3. 操作数据
如上文示例那样,通过 SqlSessionFactoryBuilder 实例 生成 SqlSessionFactory 实例 ,然后在生成操作数据库的 SqlSession 实例 。
需要注意的是:
a. 每次使用完 SqlSession 实例 必须调用其 close() 方法释放链接;
b. 由于 mybatis-config.xml 中 <transactionManager type="JDBC"></transactionManager> 而且通过 SqlSessionFactory 实例.openSession() 获取链接对象,因此链接对象默认时不会自动提交增、删和改操作的,因此需要调用 commit() 方法手动提交操作。
4. 生命周期
[a]. SqlSessionFactoryBuilder
由于 `SqlSessionFactoryBuilder` 实例用于生成 `SqlSessionFactory` 实例而已,因此并没有必要以应用程序全局作为作用域,并且无必要多线程共享。因此作为函数的局 部变量使用即可。
[b]. SqlSessionFactory
作为数据库连接池和连接池管理器使用,为达到数据库连接复用的效果,`SqlSessionFactory` 实例应当以程序全局作为作用域,并且多线程共享。采用单例或静态单例模式较好
[c]. SqlSession
由于 `SqlSession` 实例非线程安全,因此作为函数的局部变量使用。而且由于数据库连接为共享资源,因此必须遵循晚调用早释放原则, 确保调用 `close()` 函数释放连接。
四、总结
初尝 MyBatis 时会觉得麻烦,尤其是使用过 Hibernate 或其他可将对象模型实例自动转成 SQL 语句的框架的朋友来说,这确实太不方便了,而且容易出错。其实我们可以将很多工作交给相关的工具去解决。以后慢慢说吧!
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4014819.html ^_^ 肥仔 John