之前在 SSM 框架整合的思想(一) 一文中提到,mapper层的任务应该是:只做最基本的、通用性最强的事情的,它不应该有太多的逻辑上的成分存在。mapper层是直接操作数据库的,它应该在很大程度上只有那四个方法(增改查删),只有特殊情况才应该需要写新的sql。
那么到底应该怎么样去写这些方法呢?这里分享一下这些方法的书写方法,就是直接当作模板使用即可。(另外听说有人做了通用mapper的项目,有兴趣的也可以了解一下。文中的模板旨在减轻本身使用MyBatis开发的朋友的负担,同时让代码更加简洁、工整)
直接看代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aa.dao.mapper.AccountMapper">
<resultMap id="resultMap" type="com.aa.domain.entity.Account">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="loginName" column="login_name"/>
<result property="password" column="password"/>
<result property="isDelete" column="is_delete"/>
</resultMap>
<sql id="table_name">
account
</sql>
<sql id="column">
id, name, login_name, password, is_delete
</sql>
<sql id="values">
#{id}, #{name}, #{loginName}, #{password}, #{isDelete}
</sql>
<sql id="set_values">
id=#{id}, name=#{name}, login_name=#{loginName}, password=#{password},is_delete=#{isDelete}
</sql>
<sql id="condition">
<if test="id != null">
AND id IN
<foreach collection="id" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="name != null">
AND name like concat("%/", #{name}, "%") escape "/"
</if>
<if test="nameMatch != null">
AND name = #{nameMatch}
</if>
<if test="loginName != null">
AND login_name = #{loginName}
</if>
<if test="isDelete != null">
AND is_delete = #{isDelete}
</if>
ORDER BY name
<if test="start != null and size != null">
limit ${start},${size}
</if>
</sql>
<insert id="add">
INSERT INTO <include refid="table_name"/>
(
<include refid="column"/>
)VALUES(
<include refid="values"/>
)
</insert>
<update id="update">
UPDATE <include refid="table_name"/>
SET <include refid="set_values"/>
WHERE id = #{id}
</update>
<select id="query" resultMap="resultMap">
SELECT <include refid="column"/>
FROM <include refid="table_name"/>
WHERE 1=1 <include refid="condition"/>
</select>
<update id="delete">
UPDATE <include refid="table_name"/>
SET is_delete = 0;
WHERE id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</update>
</mapper>
这几个方法几乎涵盖所有常用功能:
id="cloumn" 的 sql 中直接列出数据库的全部字段
id="values" 的 sql 中则列出实体的全部字段,与上面的数据库字段位置一一对应。
id="set_values" 的 sql 则把所有可能需要更新的字段都写上即可(一般都是把全部字段都写上)
id="condition" 的 sql 是需要的查询条件,具体请看代码(其中的模糊查询已经做了字符转义)
查询方法接口定义使用Map传递参数:
List<Account> query(Map<String, Object> map);
单单看上面的说明,估计还看不出来怎么个通过用法,这里说明一下。
首先,新增的肯定没问题的了(我这里使用了UUID,所以需要传递id过来),其次更新,有个问题:如果有些参数没有传值过来,但是那个数据本身已经有值。这时候直接调用更新方法是会清空原来的值的,这种情况必须处理。
有的人直接在 id="set_values" 的 sql 中写上判断,只有传值的时候才设值。这样就大大增加了代码量了,而且很多都是重复的无用代码,不仅浪费时间,而且后期维护看起来很不方便。
这个问题只需要在dao层的更新方法封装一层即可:1、先根据传递过来的数据的id查询数据,没有数据,退出;2、有数据,则根据判断字段是否有传值,有则设值。这样的更新方法就不用担心清空数据的问题了,如果是假删除的话,发现我那里写的那个删除方法都是多余的了,这个更新方法就可以解决问题。
这个封装有个问题,就是:有时候可能我就是要清掉没传值的数据的,那么在dao层再写一个方法,直接调用更新方法即可。
接着看查询方法:例如:根据id获取数据的,dao层封装一下query方法,把一个id放进一个list里面传递过去。所有形式的查询都只是对这个方法的封装,而不是每种不一样的查询方法都在xml中写一个,这样xml中的代码就能尽可能的减少,更容易维护。
这时候发现dao层有事可做了,不再是简单调用一下mapper层的方法了,而且做的也只是对mapper层的简单封装,增加通用性,也建议不要涉及任何业务逻辑,这样它的通用性才能得到保障。
最后,service层就可以尽情的写逻辑实现了,xml代码的减少,就像丢掉了一个大包袱,既获得MyBais的灵活性,也使得代码简洁、工整,拓展性也得到提高。这时候产品跑过老跟你说要增加3个字段的时候,你只需要一下几步操作:增加数据库字段、增加实体字段、在xml中的三个sql中加上字段的罗列。一切解决。resultMap也要配置一下,如果直接返回实体类的话则不需要了。但是建议使用resultMap会更好。