mybatis的前身是apache ibatis,2010年由ASF转移到google code,从ibatis3.x改名为mybatis,2013年转移到github。mybatis是由java实现的持久层框架。
mybatis是一个半自动化的持久层框架,
可以注解配置、映射xml文件中编写动态sql语句。通常使用xml编写sql。
mybatis是对jdbc封装的框架,几乎不需要jdbc操作
mybatis提供面向接口编程,持久层只需要一个接口,不需要实现类。
jdbc : 硬编码,在java语句中写sql,耦合度高
hibernate、jpa:自动框架,由框架生成sql语句,不需要人为编写,灵活度较差,高度定制化的sql语句需要跳过框架。
mybatis:半自动化的框架,可以注解配置、xml配置。
<!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属性来指定对应的抽象方法。
(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范围查询、表名、字段、排序关键字。
(1)增删改操作的返回值可以是void、int
int类型的返回值是sql语句影响的数据条数。
(2)查询操作的返回值在xml映射文件标签的属性resultType或resultMap里设置。
resultType是单个类型,resultMap是人为设置的字段-属性的映射关系。
select查询结果的情况:
①、如果查询的结果是一条记录
查询结果可以用实体类或者list 或者 map集合接收。
用map集合接收,查询结果的字段作为键,字段对应的值作为值。
②、如果查询的结果是多条记录
1)可以用List<> 接收。
List<实体类>或者 List
③、查询结果是单行单列,可以用基本数据或String类型接收。
Mybatis框架对常用的类型设置了对应的别名,用来替代全类名,简化代码。
简记:基本数据类型前面加下划线,复杂数据类型为全小写。
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:将自增的主键的值赋值给传输到映射文件中参数的某个属性。
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:分步查询
处理一对多 映射关系:
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}
有四个属性。
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 }
相当于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>
(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>
写一些常用的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>
使用同一个SqlSession,根据相同的查询条件,只在第一次查询时执行sql语句,之后每次查询,都使用mybatis缓存的结果数据。
一级缓存失效的情形:
(1)不同的SqlSession对应不同的一级缓存
(2)同一个SqlSession ,查询条件不同
(3)同一个SqlSession ,两次查询之间有增删改操作
(4)同一个SqlSession ,两次查询之间清理过缓存clearCache()
使用同一个SqlSessionFactory,只在第一次查询时执行sql语句,之后每次查询,都是用mybatis二级缓存。
二级缓存开启的条件:
(1)在核心配置文件中加上cacheEnabled="true"全局属性
(2)在映射文件加上 标签
(3)在两次查询之间,对SqlSession 进行commit()或close()
(4)查询结果转换的实体类型要实现序列化接口。
失效的情形:在两次查询之间,执行任意的增删改操作,一级和二级缓存都会失效。
(1)先在二级缓存中查找
(2)二级缓存没有,则在一级缓存中查找
(3)一级缓存中没有,则到数据库中查找
(4)SqlSession关闭之后,一级缓存中的数据保存到二级缓存中。
mybatis是持久层框架,做缓存功能不是很擅长,有开放的接口可以使用第三方缓存工具。只能替代mybatis的二级缓存。例如EHCache。
正向工程:根据实体类 生成数据库表。例如Hibernate框架
逆向工程:根据数据库表生成实体类、映射文件、mapper接口。
mybatis的逆向工程分为简易版和全面版。
简易版包括五个增删改查方法。其中查询方法包括查询全部和根据id查找。