文章目录

  • 一、简介
  • 1、历史
  • 2、mybatis的特性
  • 3、和其他持久层框架 的比较
  • 二、映射文件
  • 1、mybatis的映射文件结构
  • 2、参数
  • 3、返回值
  • 4、类型别名
  • 5、添加操作,获取自增主键
  • 6、解决字段名和属性名不一致的情况
  • 三、动态sql
  • 1、where、 if 标签
  • 2、trim 标签
  • 3、choose、when、otherwise标签
  • 4、foreach 遍历集合、数组
  • 5、sql
  • 四、mybatis缓存
  • 1、一级缓存
  • 2、二级缓存
  • 3、mybatis缓存的查询顺序
  • 五、mybatis逆向工程


一、简介

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文件用#{ },mybatis框架详解_java{ }要用单引号。
{}中的名称是任意的,因为只有一个参数,无论取值时叫什么,都能够取到值。
②、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框架对常用的类型设置了对应的别名,用来替代全类名,简化代码。

简记:基本数据类型前面加下划线,复杂数据类型为全小写。

mybatis框架详解_sql_02

mybatis框架详解_mybatis_03

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查找。