Mybatis框架基础(续)

Mybatis的基础使用(续)

1.更新数据

  • 接口中的抽象方法
int updatePasswordById(@Param("id") Long id, @Param("password") String password);
* 提示:在默认情况下,当Java程序源代码(.java文件) 经过编译后,所有局部的变量的名称都会丢失,为使得配置SQL语句是可以根据指定的名称使用方法中的参数,需要在方法的各个参数前添加@Param以指定名称
    * 如果方法的参数只有一个则可以匍@Param指定名称,因为Mybatis可以直接找到此参数
  • 在xx.XML文件中配置以上抽象方法映射的SQL语句
<update id="updatePasswordById">
update ams_admin set password=#{password} where id=#{id}
</update
* 提示:以上占位符中的名称是通过@Param注解指定的名称,而不是抽象方法的参数名

2.查找数据

  • 接口中的抽象方法-查询次数
int count();
  • 在xx.XML文件配置抽象方法映射的SQL语句:
<!-- int count(); -->
<select id="count" resultType="int">
select count(*) from ams_admin
</select>
* 注意:所有的select节点必须配置resultType或resultMap这两个属性中的其中一个
  • 接口中的抽象方法-查询某条数据
Admin getById(Long id); 
  • xml文件中配置抽象方法的映射
<!-- Admin getById(Long id); -->
<select id="getById" resultType="cn.tedu.mybatis.Admin">
select * from ams_admin where id=#{id}
</select>
* 需要注意:如果查询结果集中的列名与类的属性名不匹配时,默认将放弃处理这些结果数据,则返回的对象中对应的属性值为null
* 为了解决此问题,可以再查询时使用自定义的别名,使得名称保持一致,但是更推荐配置<resultMap>以指导Mybatis封装 查询结果
  • 示例:
<select id="getById" resultMap="BaseResultMap">
select * from ams_admin where id=#{id}
</select>
<!-- resultMap节点的作用是:指导Mybatis如何将结果集中的数据封装到返回的对象中 -->
<!-- id属性:自定义名称 -->
<!-- type属性:将结果集封装到哪种类型的对象中 -->
<resultMap id="BaseResultMap" type="cn.tedu.mybatis.Admin">
<!-- 使用若干个result节点配置名称不统一的对应关系 -->
<!-- 在单表查询中,名称本来就一致的是不需要配置的 -->
<!-- column属性:列名 -->
<!-- property属性:属性名 -->
<result column="is_enable" property="isEnable" />
<result column="last_login_ip" property="lastLoginIp" />
<result column="login_count" property="loginCount" />
<result column="gmt_last_login" property="gmtLastLogin" />
<result column="gmt_create" property="gmtCreate" />
<result column="gmt_modified" property="gmtModified" />
</resultMap

3.动态SQL

  • Mybatis中的动态SQL表现为:根据参数不同,生成不同的SQL语句
  • 例如:你可以对某些参数值进行判断,根据判断结果走向不同分支,来决定SQL语句的某个片段,如果参数值是可遍历的,你还可以遍历此参数来生成部分 SQL片段
3.1 动态SQL–foreach
  • 接口中的抽象方法
int deleteByIds(Long... ids);
int deleteByIds(Long[] ids); 
int deleteByIds(List<Long> ids);
* 以上三种情况表示相同内容
  • xml文件中配置抽象方法的映射SQL
<!-- int deleteByIds(List<Long> ids); -->
<delete id="deleteByIds">
  delete from ams_admin where id in (
    <foreach collection="list" item="id" separator=",">
    #{id}
    </foreach>
  )
</delete>
* 以上代码中:
    *  <foreach>标签:用于遍历集合或数组类型的参数对象
    * collection属性:被遍历的参数对象,当抽象方法的参数只有1个且没有添加 @Param注解时,如果参数是List类型则此属性值为list,如果参数是数组类型(包 括可变参数)则此属性值为array;当抽象方法的参数有多个或添加了@Param注解 时,则此属性值为@Param注解中配置的值
    * item属性:自定义的名称,表示遍历过程中每个元素的变量名,可在<foreach>子级 使用#{变量名}表示数据
    * separator属性:分隔符号,会自动添加在遍历到的各元素之间
3.2其他动态SQL节点
*  <if>
* <choose> / <when> / <otherwise>
* 其它

4.关联查询

4.1 RBAC
  • RBAC = Role Based Access Control(基于角色的访问控制)
  • RBAC是经典的用户权限管理的设计思路。在这样的设计中,会存在3种类 型:用户、角色、权限,权限将分配到各种角色上,用户可以关联某种角 色,进而实现用户与权限相关。使用这样的设计,更加利于统一管理若干 个用户的权限。
  • 在RBAC的设计思路中,用户与角色一般是多对多的关系,而在数据库中, 仅仅只是使用“用户”和“角色”这2张表是不利于维护多对多关系的, 通常会增加一张中间表,专门记录对应关系,同理,角色和权限也是多对 多的关系,也需要使用中间表来记录对应关系!
4.2 关联查询
  • 在阿里巴巴的《Java开发手册》中指出:
    • 【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明 确写明。
  • 使用<'sql>与<'include>的简单示例
<sql id="SimpleQueryFields">
<if test="true"> 
 id,  username,  password
</if>
</sql>

<select id="getById" resultType="xx.xx.xx.AdminVO">  
select
<include refid="SimpleQueryFields" />  
from ams_admin
where id=#{id}
</select>
  • 如果使用封装了查询的字段列表,与的相性更好,所 以,在开发实践中,通常结合一起使用
  • 另外,在开发实践中,许多不同条件的查询的字段列表是相同的,使用可以很好的实现代码片段的复用
  • 创建"角色"对应的数据类型:
public class Role {  
private Long id;  
private String name;
private String description;  
private Integer sort;
private LocalDateTime gmtCreate;  
private LocalDateTime gmtModified;
// Setters & Getterss
// toString()
}
  • 穿件用于封装此次查询结果的类型:
public class AdminDetailsVO {  
private Long id;
private String username; 
private String password;  
private String nickname;  
private String avatar;  
private String phone;  
private String email;  
private String description;  
private Integer isEnable;  
private String lastLoginIp;  
private Integer loginCount;
private LocalDateTime gmtLastLogin;  
private LocalDateTime gmtCreate;  
private LocalDateTime gmtModified;  
private List<Role> roles;
// Setters & Getterss
// toString()
}
  • 接口中添加抽象方法:
AdminDetailsVO getDetailsById(Long id);
  • xml文件中添加对应映射
 <!-- AdminDetailsVO getDetailsById(Long id); -->
    <select id="getDetailsById" resultMap="DetailsResultMap">
        select
            <include refid="DetailsQueryFields"/>
        from ams_admin
        left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
        left join ams_role on ams_role.id = ams_admin_role.role_id
        where ams_admin.id=#{id}
    </select>

    <sql id="DetailsQueryFields">
        <if test="true">
            ams_admin.id,
            ams_admin.username,
            ams_admin.password,
            ams_admin.nickname,
            ams_admin.avatar,
            ams_admin.phone,
            ams_admin.email,
            ams_admin.description,
            ams_admin.is_enable,
            ams_admin.last_login_ip,
            ams_admin.login_count,
            ams_admin.gmt_last_login,
            ams_admin.gmt_create,
            ams_admin.gmt_modified,

            ams_role.id AS role_id,
            ams_role.name AS role_name,
            ams_role.description AS role_description,
            ams_role.sort AS role_sort,
            ams_role.gmt_create AS role_gmt_create,
            ams_role.gmt_modified AS role_gmt_create
        </if>
    </sql>

    <!-- resultMap节点的作用:指导mybatis将查询到的结果集封装到对象中 -->
    <!-- resultMap节点的id属性:自定义名称 -->
    <!-- resultMap节点的type属性:封装查询结果的类型的全限定名 -->
    <!-- 主键应该使用id节点进行配置,非主键、非集合的使用result节点进行配置 -->
    <!-- column=结果集中的列名,property=属性名 -->
    <!-- 在关联查询中,即便结果集中的列名与类的属性名完全相同,也必须配置 -->
    <!-- collection子节点:用于配置1对多关系的数据部分,通常在类中是List类型的属性 -->
    <!-- collection子节点的ofType:List集合中的元素的类型 -->
    <resultMap id="DetailsResultMap" type="cn.tedu.mybatis.vo.AdminDetailsVO">
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <result column="nickname" property="nickname" />
        <result column="avatar" property="avatar" />
        <result column="phone" property="phone" />
        <result column="email" property="email" />
        <result column="description" property="description" />
        <result column="is_enable" property="isEnable" />
        <result column="last_login_ip" property="lastLoginIp" />
        <result column="login_count" property="loginCount" />
        <result column="gmt_last_login" property="gmtLastLogin" />
        <result column="gmt_create" property="gmtCreate" />
        <result column="gmt_modified" property="gmtModified" />
        <collection property="roles" ofType="cn.tedu.mybatis.entity.Role">
            <id column="role_id" property="id" />
            <result column="role_name" property="name" />
            <result column="role_description" property="description" />
            <result column="role_sort" property="sort" />
            <result column="role_gmt_create" property="gmtCreate" />
            <result column="role_gmt_modified" property="gmtModified" />
        </collection>
    </resultMap>
  • Mybatis处理中此查询时,并不会那么只能的完成结果集的封装,所以必须自行配置<'resultMap>用于指定Mybatis完成封装
<resultMap id="DetailsResultMap" type="xx.xx.xx.xx.AdminDetailsVO">
<!--1对多、多对多的查询中,即使名称匹配的结果,也必须显式的配置 -->
<!-- 主键字段的结果必须使用id节点进行配置,配置方式与result节点完全相同 -->
<id column="id" property="id" />
   <result column="gmt_create" property="gmtCreate" />
<!-- 需要使用collection节点配置1对多中“多”的数据  -->
  <collection property="roles" ofType="xx.xx.xx.Role">
    <id column="role_id" property="id" />
    <result column="gmt_create" property="gmtCreate" />
  </collection>
</resultMap>

5.附加

5.1 关于#{}和${}格式的占位符
  • 在Mybatis中,配置SQL语句时,参数可以使用#{}或${}格式的占位符
  • 例如存在需求:分页查询表中的所有数据。
  • 需要执行的SQL语句大致是:
select * from ams_admin order by id limit ?, ?
  • 则此功能的抽象方法应该是:
List<Admin> listPage(@Param("offset") Integer offset,@Param("size") Integer size);

  • 配置SQL语句
<select id="listPage" resultMap="BaseResultMap">  
  select
    <include refid="BaseQueryFields" />  
  from ams_admin
  order by id
  limit #{offset}, #{size}
</select>
  • 其实,使用#{}格式的占位符时,Mybatis在处理时会使用预编译的做法, 所以,在编写SQL语句时不必关心数据类型的问题(例如字符串值不需要 添加单引号),也不存在SQL注入的风险!这种占位符只能用于表示某个 值,而不能表示SQL语句片段!
  • 当使用${}格式的占位符时,Mybatis在处理时会先将参数值代入到SQL语 句中,然后再执行编译相关过程,所以需要关心某些值的数据类型问题(例如涉及字符串值时,需要在编写SQL语句时添加一对单引号框住字符 串),并且,存在SQL注入的风险!其优点是可以表示SQL语句中的任何 片段!
  • 在一般情况下,应该尽可能的使用#{}格式的占位符,并不推荐使用${}格 式的占位符,即使它可以实现“泛用”的效果!
  • 在一些特殊的情况下,如果一定要使用${}格式的占位符,必须考虑SQL注 入的风险,应该使用正则表达式或其它做法避免出现SQL注入问题!
5.2 Mybatis的缓存机制
  • 缓存:通常是一个临时存储的数据,在未来的某个时间点可能会被删除
  • 通常,存储缓存数据的位置是读写效率较高的,相比其它“非缓存”的数 据有更高的处理效率
  • 由于缓存的数据通常并不是必须的,则需要额外消耗一定的存储空间,同时由于从缓存获取数据的效率更高,所以是一种牺牲空间、换取时间的做法
  • 另外,你必须知道,从数据库读取数据的效率是非常低下的
  • Mybatis有2种缓存机制,分别称之一级缓存和二级缓存
  • 一级缓存是基于SqlSession的缓存,也称之为“会话缓存”,仅当是同 一个会话、同一个Mapper、同一个抽象方法(同一个SQL语句)、同样 的参数值时有效,一级缓存在集成框架的应用中默认是开启的,且整个过 程不由人为控制(如果是自行得到SqlSession后的操作,可自行清理一 级缓存)
  • 二级缓存默认是关闭的,它是基于namespace的,所以也称之为 “namespace缓存”,需要在配置SQL语句的XML中添加’<'cache/>'节点, 以表示当前XML中的所有查询都允许开通二级缓存,并且,在<'select>节 点上配置useCache=“true”,则对应的<'select>节点的查询结果将被二级 缓存处理,并且,此查询返回的结果的类型必须是实现了Serializable接口的,如果使用了<'resultMap>配置如何封装查询结果,则必须使用 节点来封装主键的映射,满足以上条件后,二级缓存将可用,只要是当前 namespace中查询出来的结果,都会根据所执行的SQL语句及参数进行 结果的缓存
  • 无论是一级缓存还是二级缓存,只要数据发生了写操作(增、删、改), 缓存数据都将被自动清理
  • 由于Mybatis的缓存清理机制过于死板,所以,一般在开发实践中并不怎 么使用!更多的是使用其它的缓存工具并自行制定缓存策略
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值