<>一、简介
<>1、历史
mybatis的前身是apache ibatis,2010年由ASF转移到google
code,从ibatis3.x改名为mybatis,2013年转移到github。mybatis是由java实现的持久层框架。
<>2、mybatis的特性
mybatis是一个半自动化的持久层框架,
可以注解配置、映射xml文件中编写动态sql语句。通常使用xml编写sql。
mybatis是对jdbc封装的框架,几乎不需要jdbc操作
mybatis提供面向接口编程,持久层只需要一个接口,不需要实现类。
<>3、和其他持久层框架 的比较
jdbc : 硬编码,在java语句中写sql,耦合度高
hibernate、jpa:自动框架,由框架生成sql语句,不需要人为编写,灵活度较差,高度定制化的sql语句需要跳过框架。
mybatis:半自动化的框架,可以注解配置、xml配置。
<>二、映射文件
<>1、mybatis的映射文件结构
<!DOCTYPE mapper ...> <mapper namespace=
"com.sly.ecs.business.mapper.mission.MissionMapper"> <insert id="insertBatch"
parameterType="list" ...> <select id="selectMission" parameterType="int"
resultMap="BaseResultMap" ...> </mapper>
通过顶级标签中的namespace属性来指定对应的mapper接口,
通过子标签的id属性来指定对应的抽象方法。
<>2、参数
(1)传入参数的情形:
①、mapper接口方法的参数是单个:
xml文件用#{ }, 来取值,需要注意 { }来取值,需要注意 来取值,需要注意{ }要用单引号。
{}中的名称是任意的,因为只有一个参数,无论取值时叫什么,都能够取到值。
②、mapper接口方法的参数是多个:
会用Map集合保存参数,键是由mybatis框架自动生成
1)xml文件可以用 arg0 、arg1 。。。 获取对应位置的参数
2)xml文件可以用 param1 、param2 。。。 获取对应位置的参数
也可以交叉使用,例如 arg0、 param2
③、mapper接口方法的参数是多个,可以放到一个Map集合里
键是由开发者人为设置。
在xml映射文件里根据人为设置的键获取参数。
例如:在600ly公司代码中使用 GenericQueryParam装载参数。
public final class GenericQueryParam extends LinkedHashMap<String, Object>
implements ListQueryParam { ... } var param = new GenericQueryParam(); param.
fill("signCode", signCode).fill("typeId", typeId).fill("labelId", labelId) .fill
("areaId", areaId).fill("status", status).fill("userId", userId);
④、mapper接口方法的参数是实体类
xml文件里根据实体类的属性获取属性值。
补充知识:实体类中有set、get方法的才算做属性,不是按照全局变量来定义的。
⑤、mapper接口方法传入@Param注解参数
参数会保存到Map集合,以@Param的值作为键,xml文件以Map的键来获取参数。
归类:
①、参数加上@Param注解 传入方法
②、参数装进Map或者实体类 传入方法
(2)获取参数的两种方式:(jdbc原生写法)
${} 字符串拼接。不会带上单引号。注意要手动加上单引号。
#{} 占位符赋值。会带上单引号。不需要手动加上单引号。
示例:
①、常规
username = #{username} AND sign_code = #{signCode}
②、Mybatis处理模糊查询
role_name LIKE CONCAT('%',#{roleName},'%') role_name LIKE '%${roleName}%'
role_name LIKE "%"#{roleName}"%"
③、in范围查询:
String ids=“1,4,25”;
pk_id IN (${ids})
④、动态设置表名、字段名、排序关键字:
select * from ${tableName} ORDER BY ${sortColumn} ${sortOrder}
总结:
sql语法中需要引号的 尽量使用#{},或者${} 手动加上引号。 例如字符串、日期等类型的值。 sql语法中不需要引号的 使用${}
,不用手动加上引号。例如in范围查询、表名、字段、排序关键字。
<>3、返回值
(1)增删改操作的返回值可以是void、int
int类型的返回值是sql语句影响的数据条数。
(2)查询操作的返回值在xml映射文件标签的属性resultType或resultMap里设置。
resultType是单个类型,resultMap是人为设置的字段-属性的映射关系。
select查询结果的情况:
①、如果查询的结果是一条记录
查询结果可以用实体类或者list 或者 map集合接收。
用map集合接收,查询结果的字段作为键,字段对应的值作为值。
②、如果查询的结果是多条记录
1)可以用List<> 接收。
List<实体类>或者 List<Map<String,Object>接收
不能用单个实体类接收多条记录。会报TooManyResultsException。
2)可以用一个map集合接收。
在@MapKey(“”) 注解的value属性中指定某个字段的值作为map的键。
例如:@MapKey(“id”)
③、查询结果是单行单列,可以用基本数据或String类型接收。
<>4、类型别名
Mybatis框架对常用的类型设置了对应的别名,用来替代全类名,简化代码。
简记:基本数据类型前面加下划线,复杂数据类型为全小写。
<>5、添加操作,获取自增主键
User u=new User(null,"yang","123456","男"); userMapper.insertUser(u); u.getId();
int insertUser(User user); <insert id="insertUser" useGeneratedKeys="true"
keyProperty="id"> insert into t_user values(null,#{username},#{password},#{sex}
)</insert>
useGeneratedKeys:设置当前的sql使用了自增的主键。
keyProperty:将自增的主键的值赋值给传输到映射文件中参数的某个属性。
<>6、解决字段名和属性名不一致的情况
1)在Mapper.xml文件里给sql语句的字段起别名
emp_name as empName
2)在mybatis配置文件设置 下划线转换为驼峰式。
<settings> <setting name="mapUnderscoresToCamelCase" value="true"/> </settings>
3)在Mapper.xml文件里设置结果映射
<resultMap id="BaseResultMap" type="User" > <id property="pkId" colume="pk_id"
/> <result property="userName" colume="user_name"/> <result property="password"
colume="password"/> <result property="sex" colume="sex"/> <result property="
email" colume="email"/> </resultMap>
处理多对一 映射关系:
例:dept部门表和emp员工表是一对多的关系。在Emp实体类中加入Dept dept属性。
select * from t_emp t1 left join t_dept t2 on t1.did=t2.did
需要将查询结果字段装到Emp类中。
方式1:设置级联映射
<resultMap id="empAndDeptMap" type="Emp" > <id property="eid" colume="eid"/> <
result property="empName" colume="emp_name"/> <result property="address" colume=
"address"/> <result property="sex" colume="sex"/> <result property="email"
colume="email"/> <result property="dept.did" colume="did"/> <result property="
dept.deptName" colume="dept_name"/> </resultMap>
方式2:association
<resultMap id="empAndDeptMap" type="Emp" > <id property="eid" colume="eid"/> <
result property="empName" colume="emp_name"/> <result property="address" colume=
"address"/> <result property="sex" colume="sex"/> <result property="email"
colume="email"/> <association property="dept" javaType="Dept"> <id property="did
" colume="did"/> <result property="deptName" colume="dept_name"/> </association>
</resultMap>
方法3:分步查询
处理一对多 映射关系:
<>三、动态sql
<>1、where、 if 标签
if 标签:对标签中test属性的表达式 进行判断,为true则显示标签内的过滤条件,为false则不显示标签内的过滤条件。
where标签中 没有过滤条件,不加where关键字;
有过滤条件,加上where关键字,并将第一个条件前边的and 或者or 关键字删掉。
<select id="selectRoleByName" resultType="_int" parameterType="param"> SELECT
* FROM t_project_role<where> <if test="roleName != null and roleName != ''">
AND role_name = #{roleName}</if> <if test="roleId != null and roleId != ''">
AND pk_id != #{roleId}</if> <if test="roleType != null and roleType != ''"> OR
role_type = #{roleType}</if> </where> </select>
如果 第2、3个if标签存在,则生成的动态sql是
SELECT * FROM t_project_role where pk_id != #{roleId} OR role_type =
#{roleType}
<>2、trim 标签
有四个属性。
prefix,suffix:给trim标签里的内容加上前、后缀。
prefixoverrides,suffixoverrides:给trim标签里的内容 前或后 去掉内容。
<select id="selectRoleByName" resultType="_int" parameterType="param"> SELECT
* FROM t_project_role<trim prefix="where" suffixoverrides="AND|OR"> <if test="
roleName != null and roleName != ''"> role_name = #{roleName} AND </if> </trim>
</select>
生成的动态sql是
SELECT * FROM t_project_role where role_name != #{roleName }
<>3、choose、when、otherwise标签
相当于java语言中if…else if…else 条件判断代码块。
when 标签对test属性值进行判断,值为true则作为动态sql的条件,不再判断后面的条件。所以标签里的条件不需要加and或者or 。
值为false则判断后面的条件。如果when标签中的test 属性值都为false,则将otherwise标签作为条件。
when标签至少有一个,otherwise标签最多有一个。
<select id="findUserByChoose" parameterType="domain.User" resultType="
domain.User"> select * from USER <where> <choose> <when test="username!=null
and username!=''"> username=#{username} </when> <when test="sex!=null and
sex!=''"> sex=#{sex} </when> <otherwise> did=1 </otherwise> </choose> </where>
</select>
<>4、foreach 遍历集合、数组
(1)in 范围查找
DELETE FROM t_code WHERE pk_id IN <foreach collection="list" item="pkId" open="
(" close=")" separator="," > #{pkId} </foreach> DELETE FROM t_code WHERE pk_id
IN (12,23,55,86)
(2)批量添加
INSERT INTO t_user(pk_id, name, sex, age) values <foreach collection="list"
item="user" separator=","> ( null, #{user.username}, #{user.sex}, #{user.age} )
</foreach>
<>5、sql
写一些常用的sql块
例如:
<sql id="Base_Column_List" > pk_id, code_class, code_name, sort, remark,
create_user, create_date, update_user, update_date, is_valid</sql> <sql id="
Base_Count"> SELECT COUNT(0) FROM t_code </sql> <sql id="Base_Select"> SELECT <
include refid="Base_Column_List" /> FROM t_code </sql> <sql id="Base_Where"> <
where> <if test="pkId != null and pkId != ''"> AND pk_id = #{pkId} </if> <if
test="codeClass != null and codeClass != ''"> AND code_class = #{codeClass} </if
> <if test="codeName != null and codeName != ''"> AND code_name = #{codeName} </
if> <if test="(sort != null and sort != '') or sort == 0"> AND sort = #{sort} </
if> <if test="remark != null and remark != ''"> AND remark = #{remark} </if> <if
test="(createUser != null and createUser != '') or createUser == 0"> AND
create_user = #{createUser}</if> <if test="createDate != null"> AND create_date
= #{createDate}</if> <if test="(updateUser != null and updateUser != '') or
updateUser == 0"> AND update_user = #{updateUser} </if> <if test="updateDate !=
null"> AND update_date = #{updateDate} </if> <if test="(isValid != null and
isValid != '') or isValid == 0"> AND is_valid = #{isValid} </if> </where> </sql>
<sql id="Base_Sort"> <if test="_parameter.getSortParam().size() > 0"> ORDER BY <
foreach collection="_parameter.getSortParam()" item="sortParam" separator=",">
${sortParam.column} ${sortParam.order}</foreach> </if> </sql> <sql id="Base_Page
"> <if test="isPage == 1"> LIMIT #{_offset}, #{_pagesize} </if> </sql>
<>四、mybatis缓存
<>1、一级缓存
使用同一个SqlSession,根据相同的查询条件,只在第一次查询时执行sql语句,之后每次查询,都使用mybatis缓存的结果数据。
一级缓存失效的情形:
(1)不同的SqlSession对应不同的一级缓存
(2)同一个SqlSession ,查询条件不同
(3)同一个SqlSession ,两次查询之间有增删改操作
(4)同一个SqlSession ,两次查询之间清理过缓存clearCache()
<>2、二级缓存
使用同一个SqlSessionFactory,只在第一次查询时执行sql语句,之后每次查询,都是用mybatis二级缓存。
二级缓存开启的条件:
(1)在核心配置文件中加上cacheEnabled="true"全局属性
(2)在映射文件加上 标签
(3)在两次查询之间,对SqlSession 进行commit()或close()
(4)查询结果转换的实体类型要实现序列化接口。
失效的情形:在两次查询之间,执行任意的增删改操作,一级和二级缓存都会失效。
<>3、mybatis缓存的查询顺序
(1)先在二级缓存中查找
(2)二级缓存没有,则在一级缓存中查找
(3)一级缓存中没有,则到数据库中查找
(4)SqlSession关闭之后,一级缓存中的数据保存到二级缓存中。
mybatis是持久层框架,做缓存功能不是很擅长,有开放的接口可以使用第三方缓存工具。只能替代mybatis的二级缓存。例如EHCache。
<>五、mybatis逆向工程
正向工程:根据实体类 生成数据库表。例如Hibernate框架
逆向工程:根据数据库表生成实体类、映射文件、mapper接口。
mybatis的逆向工程分为简易版和全面版。
简易版包括五个增删改查方法。其中查询方法包括查询全部和根据id查找。