java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis映射器

深入浅出MyBatis映射器

作者:Davieyang.D.Y

映射器是MyBatis最复杂也最重要的组件,也是基于MyBatis应用程序开发中,本文主要介绍了深入浅出MyBatis映射器,具有一定的参考价值,感兴趣的可以了解一下

映射器是MyBatis最复杂也最重要的组件,也是基于MyBatis应用程序开发中,占工作量比较大的工作,甚至能达到8成;映射器由Mapper接口和XML或者注解组成,在映射器中可以配置参数,各类SQL语句,存储过程、缓存、级联等复杂处理,并通过简易的映射规则将数据映射到指定的POJO或其对象上,这也是MyBatis核心运转逻辑

在XML和注解之间的选择其实很明显,其一面对复杂SQL,尤其比较长的SQL,注解会显得很无力;其二注解的可读性较差,尤其是对于那些需要复杂配置的SQL;其三注解没有XML上下文相互引用的优势

MyBatis映射器可配置元素

MyBatis映射器配置文件中可以包含以下主要元素,这些元素用于定义与数据库交互的各种操作和相关配置:

Select元素

<select>元素在 MyBatis 映射器配置文件中用于定义 SQL SELECT 查询语句及其相关属性。以下是<select> 元素常见的属性及其详细解释:

总结起来,<select>元素的主要属性用于定义查询语句的身份标识、输入参数类型、输出结果映射方式、缓存策略、查询超时、预取大小等执行特性。这些属性帮助开发者精细控制查询行为,确保与数据库交互的高效性和灵活性。在实际应用中,最常用的属性通常是idparameterTyperesultType或 resultMapflushCache和 useCache其他属性在特定场景下可能会发挥作用,但并不常用。

Select元素实例 单个参数

    <select id="countUserByFirstName" parameterType="string" resultType="int">
        select count(*) total from t_user where user_name like concat ('%',#{firstName, jdbcType=VARCHAR, javaType=String},'%')
    </select>

SQL很容易懂,里边的元素可以参考对应上边每一项的解释理解,有了SQL,就需要对应的Mapper接口和方法(也就是Dao Data Access Object)public Integer countUserByFirstName(String firstName)

在MyBatis的配置文件的settings元素中,有autoMappingBehavior和mapUnderscoreToCamelCase两个可配置的选项,他们是控制自动映射和驼峰映射的开关,一般来说自动映射会用的多一些,这样可以使用SQL的别名机制,更加灵活,而驼峰映射要求比较严格;autoMappingBehavior配置项有3个选择,NONE表示不自动映射,PARTIAL是默认值,表示只对没有嵌套的结果集自动映射,FULL表示对所有的结果集自动映射包括嵌套结果集,通常默认即可

假设我们有如下这么一个POJO

package com.ssm.pojo;
import org.apache.ibatis.type.Alias;
@Alias("role")
public class Role{
	private int id;
	private String roleName;
	private String note;
	/**  setters and getters */
}

在这个POJO里有三个属性id、roleName、note,如果在Mapper里编写SQL列名和属性名保持一致,它就会形成自动映射,例如

    <select id="getRole" parameterType="int" resultType="role">
        select id, role_name as roleName, note from t_role where id = #{id}
    </select>

列名role_name被别名roleName代替了,和POJO上的属性名保持一致,如此MyBatis就能将查询结果集和POJO的属性一一对应,自动完成映射无需再进行任何配置

如果严格按照驼峰命名法,比如数据库字段为role_name,POJO属性名为roleName,数据库字段名为user_name,POJO属性名为userName,那么只要在配置项把mapUnderscoreToCamelCase设置为true即可,如果这样,那么SQL就可以写成

select id, role_name, note from t_role where id = #{id}

MyBatis会严格按照驼峰命名的方式自动映射,只是这样要求数据字段和POJO的属性名严格对应

自动映射和驼峰映射都建立在SQL列名和POJO属性名的映射关系上,在实际使用中会更复杂,比如可能有些字段有主表和从表关联的级联关系,又比如typeHandler的转换规则复杂,此时resultType元素无法满足这些需求需要更强大的映射规则需要考虑使用resultMap等等

Select元素实例 使用Map传递多个参数

假设有这样一个Mapper接口定义,它通过键值对传递多个参数

public List<Role> findRolesByMap(Map<String, Object>parameterMap);

此时传递给映射器的一个Map对象,使用它在SQL中设置对应的参数,如下所示

    <select id="findRoleByMap" parameterType="map" resultType="role">
        select id, role_name as roleName, note from t_role 
        where role_name like concat('%', #{roleName}, '%') and note like concat('%', #{note},'%')
    </select>

这里使用两个数据库字段进行了一个模糊查询,传参方式是使用map,执行代码如下所示

RoleDao roleDao = sqlSession.getMapper(RoleDao.class);
Map<String, Object>parameterMap = new HashMap<>();
parameterMap.put("roleName", "1");
parameterMap.put("note","1");
List<Role> roleList = roleDao.findRolesByMap(parameterMap);

严格来说Map几乎适用于所有场景,但其可读性比较差,且不能限定其传递的数据类型

Select元素实例 使用注解传递多个参数

MyBatis提供了@Param(org.apache.ibatis.annotations.Param),可以通过它定义映射器的参数名称,如下代码所示

public List<Role> findRolesByAnnotation(@Param("roleName") String roleName, @Param("note") String note);

Mapper文件如下所示

    <select id="findRolesByAnnotation" "resultType="role">
        select id, role_name as roleName, note from t_role 
        where role_name like concat('%', #{roleName}, '%') and note like concat('%', #{note},'%')
    </select>

Select元素实例 使用JavaBean传递多个参数

先定义一个POJO

package com.ssm.pojo.parameter;

public class RoleParams{
	private String roleName;
	private String note;
	/** setter and getter */
}

把Mapper接口定义为

public List<Role> findRolesByBean(RoleParams roleParam);

Mapper文件如下所示

    <select id="findRolesByBean" "resultType="role" parameterType="com.ssm.pojo.parameter.RoleParams">
        select id, role_name as roleName, note from t_role 
        where role_name like concat('%', #{roleName}, '%') and note like concat('%', #{note},'%')
    </select>

执行代码如下

sqlSession = SqlSessionFactoryUtils.openSqlSession();
RoleDao roleDao = sqlSession.getMapper(RoleDao.class);
RoleParams roleParams = new RoleParams();
roleParams.setNote("1");
roleParams.setRoleName("1");
List<Role>roleList = roleDao.findRolesByBean(roleParams);

在某些场景下也会混合使用,例如通过角色名称和备注查询角色,同时需要支持分页

分页的POJO如下定义

package com.ssm.pojo.parameter;
public class PageParams{
	private int start;
	private int limit;
	/** setters and getters */
}

Mapper接口如下

public List<Role> findByMix(@Param("params") RoleParams roleParams, @Param("page") PageParams PageParam);

Mapper文件如下写法

    <select id="findByMix" "resultType="role">
        select id, role_name as roleName, note from t_role 
        where role_name like concat('%', #{params.roleName}, '%') and note like concat('%', #{params.note},'%')
        limit #{page.start}, #{page.limit}
    </select>

MyBatis对params和page这类JavaBean参数提供EL(中间语言)支持,为编程带来了很多便利

结果集映射resultMap

自动映射和驼峰映射规则比较简单,无法定义更多的属性和满足复杂的场景,例如枚举需要typeHandler,还有数据级联等等,因此select元素提供了resultMap属性,用于指定具体的resultMap作为映射规则,如下定义所示

<result Map id="roleMap" type="role">
	<id property="id" column="id"/>
	<result property="roleName" column="role_name"/>
	<result property="note" column="ntoe"/>
</resultMap>
<select id="getRoleUserResultMap" parameterType="int" resultMap="roleMap">
	select id, role_name, note from t_role where id = #{id}
</select>
    <!-- 定义结果映射,用于将数据库中的数据映射成Role对象 -->
    <resultMap id="userMap" type="com.ssm.pojo.User">
        <id property="userid" column="userid"/> <!-- 主键映射 -->
        <result property="username" column="username"/> <!-- 角色名映射 -->
        <result property="password" column="password"/> <!-- 备注映射 -->
        <result property="sex" column="sex"/>
        <result property="phone" column="phone"/>
        <result property="email" column="email"/>
        <result property="phonenumber" column="phonenumber"/>
        <result property="comment" column="comment"/>
    </resultMap>
    <!-- 定义结果映射,用于将数据库中的数据映射成Role对象 -->
    <resultMap id="roleMap" type="com.ssm.pojo.Role">
        <id property="id" column="id"/> <!-- 主键映射 -->
        <result property="roleName" column="roleName"/> <!-- 角色名映射 -->
        <result property="note" column="note" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/> <!-- 备注映射 -->
    </resultMap>

分页参数RowBounds

MyBatis不仅支持分页,还内置了一个专门处理分页的类RowBounds,如下代码所示

/**
 * RowBounds类用于设置SQL查询的偏移量和限制结果集的大小。
 */
package org.apache.ibatis.session;

public class RowBounds {
    // 无偏移量,无限制的行数
    public static final int NO_ROW_OFFSET = 0;
    // 最大整数2147483647
    public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
    // 默认的RowBounds实例,无偏移,无限制
    public static final RowBounds DEFAULT = new RowBounds();
    private final int offset; // 偏移量,从第几行开始
    private final int limit; // 限制返回的行数

    /**
     * 构造函数,默认偏移量为0,限制的行数为最大整数值。
     */
    public RowBounds() {
        this.offset = 0;
        this.limit = Integer.MAX_VALUE;
    }

    /**
     * 构造函数,设置自定义的偏移量和限制的行数。
     * 
     * @param offset 查询结果的偏移量,即从第几行开始。
     * @param limit 返回结果集的最大行数。
     */
    public RowBounds(int offset, int limit) {
        this.offset = offset;
        this.limit = limit;
    }

    /**
     * 获取偏移量。
     * 
     * @return 偏移量,从第几行开始。
     */
    public int getOffset() {
        return this.offset;
    }

    /**
     * 获取限制的行数。
     * 
     * @return 返回结果集的最大行数。
     */
    public int getLimit() {
        return this.limit;
    }
}

Mapper接口定义如下

public List<Role> findByRowBounds(@Param("roleName") String rolename, @Param("note") String note, RowBounds rowBounds);

Mapper映射文件如下

<select id="findByRowBounds" resultType="role">
	select id, role_name as roleName, note from t_role 
	where role_name like concat('%', #{roleName}, '%') and note like concat('%', #{note}, '%')
</select>

映射代码中没有任何关于RowBounds参数的信息,MyBatis自身会自动识别和启用它

执行代码如下

Logger log = Logger.getLogger(Main.class);
SqlSession sqlsession = null;
try{
	sqlsession = SqlSessionFacotryUtils.openSqlSession();
	RoleDao roleDao = sqlSession.getMapper(RoleDao.class);
	List<Role> roleList = roleDao.findByRowBounds("role", "note", new RownBounds(0,20));
	log.info(roleList.size());
}finally{
	if(sqlSession!=null){
		sqlSession.close();
	}
}	

RowBounds分页一般来说只适合做少量数据的分页,其原理是在执行SQL的查询后,根据偏移量和限制条数返回结果,因此大量的数据查询性能会比较差

insert元素

在MyBatis框架中,<insert>标签用于定义SQL插入语句。以下是其常用的属性及其说明:

insert元素实例自增主键场景

    <!-- 插入一个新的Role记录 -->
    <insert id="insertRole" parameterType="com.ssm.pojo.Role" databaseId="mysql" >
        INSERT INTO t_role(roleName, note) VALUES (#{roleName}, #{note})
    </insert>

这个语句非常简单,因为表的id只自增主键,因此在插入数据的时候并没有为该字段做任何事,数据库会自动完成,但实际开发过程中往往插入一条记录,需要根据当前表的主键值处理关联表,MyBatis中可以通过getGeneratedKeys方法获得数据库生成的主键,默认它是false状态,也就是不会返回主键值,当打开了这个开关后,还需要配置其属性keyPropertykeyColumn,从而告诉系统把生成的主键放入POJO的对应属性中,如果存在多个主键,就要用逗号将他们分隔

    <!-- 
    插入一个新的Role记录
    
    参数:
    - parameterType: 指定插入操作时使用的参数类型,这里是 com.ssm.pojo.Role,表示插入的角色对象。
    - databaseId: 指定该SQL语句适用于哪个数据库,这里是 mysql。
    - useGeneratedKeys: 指定是否使用数据库自动生成的键值。如果为 true,则表示使用自动生成的键值。
    - keyProperty: 指定生成的键值应该被设置到对象的哪个属性上,这里是 id。
    
    返回值: 无返回值,仅执行插入操作。
     -->
    <insert id="insertRole" parameterType="com.ssm.pojo.Role" databaseId="mysql" useGeneratedKeys="true" keyProperty="id" >
        INSERT INTO t_role(roleName, note) VALUES (#{roleName}, #{note})
    </insert>

如此,将useGeneratedKeys配置为true表示采用JDBC的Statement对象的getGeneratedKeys方法返回主键,而keyProperty代表用哪个POJO的属性匹配这个主键,这个例子里是id,说明它会用数据生成的主键赋值给这个POJO的属性id

insert元素实例自定义主键场景

有时候主键未必是自增的,而是依赖于某些规则,例如取消角色表的id自增规则,将其规则改为当角色表记录为空时id设置为1,当角色表记录不为空时,id设置为当前id加3,对于这样的特殊规则主键的场景,MyBatis也提供了支持,它主要依赖selectKey元素,允许自定义键值生成规则

    <!-- 
     插入一个新的Role记录
     参数:
     - parameterType: 指定插入操作时使用的参数类型,这里是com.ssm.pojo.Role,代表插入的角色对象。
     - databaseId: 指定此SQL适用于哪个数据库,这里是mysql。
     - useGeneratedKeys: 指定是否使用数据库生成的主键值。如果为true,MyBatis会自动获取生成的主键值并设置到对象中。
     - keyProperty: 指定哪个属性会接收生成的主键值,这里是id。
     返回值: 插入操作成功后,返回值为插入的记录的主键ID。
    -->
    <insert id="insertRole" parameterType="com.ssm.pojo.Role" databaseId="mysql" useGeneratedKeys="true" keyProperty="id" >
        <!-- 
         使用selectKey标签来动态生成主键ID。在插入新记录之前执行,通过查询当前角色表中最大ID值,若不存在则默认为1,若存在则在最大ID上加3,以保证ID的唯一性。
        -->
        <selectKey keyProperty="id" resultType="int" order="BEFORE">
            select if (max(id) = null, 1, max(id) + 3) from t_role
        </selectKey>
        <!-- 插入新的Role记录,#{id}, #{roleName}, #{note}分别代表角色的ID,角色名和备注信息。这些值会从传入的Role对象中获取。 -->
        INSERT INTO t_role(id, roleName, note) VALUES (#{id}, #{roleName}, #{note})
    </insert>

其他的很容易看明白,唯独这个order,设置为BEFORE,说明它将于当前定义的SQL前执行,也就是它会在插入数据前生成主键的SQL,如果还有一些需要在SQL执行后的动作,例如插入语句内部可能有嵌入索引调用,这样可以把它设置为AFTER,就会在执行插入后再完成任务

update元素和delete元素

   <!-- 更新Role的信息 -->
    <!-- 
    函数级别注释:该标签用于更新Role表中的信息。
    参数:
    - roleName: 角色名,类型为String,用于更新角色的名称。
    - note: 备注信息,类型为String,用于更新角色的备注。
    - id: 角色ID,类型为Integer,用于指定要更新的角色。
    返回值:无返回值。
     -->
    <update id="updateRole" parameterType="com.ssm.pojo.Role">
        UPDATE t_role SET roleName = #{roleName}, note = #{note} WHERE id = #{id}
    </update>

    <!-- 
    函数级别块注释:
    根据提供的ID删除数据库中特定的Role记录。
    
    参数:
    id - 需要删除的Role记录的ID,类型为int。
    
    返回值:
    无返回值。
     -->
    <delete id="deleteRole" parameterType="int">
        DELETE FROM t_role WHERE id = #{id}
    </delete>

sql元素

在实际开发过程中,往往很多表的列都非常多,在编写代码的时候,重复写列名是个很枯燥的事情,即便编译器提供了很多方法,但还是很麻烦,MyBatis提供了sql元素,简化了该场景下的编码工作

    <!-- 定义查询角色信息时需要返回的列名 -->
    <sql id = "roleCols">
        id, roleName, note
    </sql>


    <!-- 根据ID查询Role信息,返回Role对象。该查询针对Oracle数据库。 -->
    <select id="getRoleById" parameterType="int" resultMap="roleMap" databaseId="oracle">
        SELECT <include refid="roleCols"/>FROM t_role WHERE id = #{id}
    </select>

    <!-- 插入新的Role信息。在插入前,通过selectKey为新Role生成一个唯一的ID。 -->
    <insert id="insertRole" parameterType="com.ssm.pojo.Role">
        <selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
            select if (max(id) = null, 1, max(id)+3) from t_role
        </selectKey>
        insert into t_role(<include refid="roleCols"/>) values(#{id}, #{roleName}, #{note})
    </insert>

sql元素还支持变量传递

    <!-- 定义一个SQL片段,用于查询角色表中的id、roleName和note列 -->
    <!-- 通过传入的别名(alias)动态拼接SQL语句中的表名前缀 -->
    <sql id ="roleCols2">
        ${alias}.id, ${alias}.roleName, ${alias}.note}
    </sql>


    <!-- 根据ID查询Role信息,返回Role对象。该查询针对Oracle数据库。 -->
    <select id="getRoleById" parameterType="int" resultMap="roleMap" databaseId="oracle">
        SELECT <include refid="roleCols">
        <property name="alias" value="r"/>
    </include>
        FROM t_role r WHERE id = #{id}
    </select>

include元素中定义了一个“alias”的变量,其值是SQL中表t_role的别名“r”,sql元素可以通过EL引用这个变量

到此这篇关于深入浅出MyBatis映射器的文章就介绍到这了,更多相关MyBatis映射器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文