在开发中最重要的就是数据库的开发,其中涉及到了各种各样的规范。本文会结合阿里的编码规范一文,帮助你在掌握了MyBatis的基本使用之后,掌握一些提升编码规范的小细节,让我们开始吧!
MyBatis的元素与参数
- 元素:定义的SQL语句段,有唯一的id,可以在其他地方通过include使用。
<sql id="Base_Column_List">
id, userName, realName, sex, mobile, email, note,
position_id
</sql>
<select id="selectUserHealthReport" resultMap="userAndHealthReport">
select
<include refid="Base_Column_List" />
from t_user a
</select>
- 参数:预编译SQL语句时填充进去的字段,看JDBC的例子:
sql = "SELECT * FROM t_user where userName= ? ";
stmt = conn.prepareStatement(sql);
stmt.setString(1,"test");
MyBatis传参数采用两种方式:
- #{}:类似于JDBC的预编译过程,看一下执行流程:
这个(String)会自动的将参数加上单引号,有效的防止了sql注入:
- ${}:直接将值传递过去,会拼接在sql中:
resultType与resultMap
二者都是在MyBatis的Mapper配置文件中用于返回结果,却又有点不同:
-
resultType的值就是当前项目的具体POJO类(与配置文件中的别名搭配使用),数据库中的字段需要与POJO定义的字段一一对应(可以设置驼峰转换),未对应上的会被赋值为null。
-
resultMap需要在Mapper文件中显示定义,在进行联合查询,返回的不单纯是一个POJO时,能够带来很大的遍历。
<!-- id为当前resultMap的标识,用于在其他处进行引用; type同resultType,是POJO的全限定名 autoMapping为是否自动映射,true则直接将结果映射到POJO中--> <resultMap id="UserResultMap" type="TUser" autoMapping="true"> <!-- 对查询结果的标注,用于结果集合并. --> <id column="id" property="id" /> <!-- autoMapping为flase时,则需要自己手动的写<result>来显示的为POJO字段赋值. property为具体的POJO字段名.--> <result column="userName" property="userName"/> <result column="realName" property="realName" /> <result column="sex" property="sex" /> <result column="mobile" property="mobile" /> <result column="email" property="email" /> <result column="note" property="note" /> </resultMap>看完了resultMap的相关配置,是不是觉得有一点难受?那么在日常开发中应该怎么取舍呢?
根据开发规范,应当**强制使用resultMap!**主要还是为了解耦合,在出现改动的时候不需要修改两个文件。
传递多个查询参数
参数需要传递给Mapper接口,然后在xml中配置parameterType,就可以在sql语句中通过#{}来调用啦。MyBatis提供了三种传参的方式:
- Map传递参数
- 注解传递参数
- Java Bean传递参数
可以从接收的方式看出来,Map可读性也太差了,不推荐使用;参数较少时使用注解,较多时使用Java Bean。可以参见开发规范:
使用Bean时要注意配置参数类型:
<select id="selectByEmailAndSex3" resultMap="BaseResultMap"
parameterType="com.lele.mybatis.entity.EmailSexBean">
select
<include refid="Base_Column_List" />
from t_user a
where a.email like CONCAT('%', #{email}, '%') and
a.sex = #{sex}
</select>
动态SQL
在实际开发中,我们往往要根据各种不同的场景拼接出不同的SQL语句。有两种实现模式:考虑到所有的情况并分别写出SQL语句,在Service中判断调用哪个;使用MyBatis提供的动态SQL。在此,讨论较为常用的三个场景:
1.where + if 实现动态拼接查询条件
select
<include refid="Base_Column_List" />
from t_user a
<where>
<if test="email != null and email != ''">
and a.email like CONCAT('%', #{email}, '%')
</if>
<if test="sex != null ">
and a.sex = #{sex}
</if>
</where>
为什么要加一个where呢?
where标签是为了防止两个if都不满足时,出现where后面没有语句报错的情况。
2.set + if 实现更新语句操作
update t_user
<set>
<if test="userName != null">
userName = #{userName,jdbcType=VARCHAR},
</if>
<if test="note != null">
note = #{note,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
set标签的作用同where一致,都是作为if的辅助出现。
3.使用foreach实现批处理操作
insert into t_user (userName, realName,
sex, mobile,email,note,
position_id)
values
<foreach collection="list" separator="," item="user">
(
#{user.userName,jdbcType=VARCHAR},
#{user.realName,jdbcType=VARCHAR},
#{user.sex,jdbcType=TINYINT},
#{user.mobile,jdbcType=VARCHAR},
#{user.email,jdbcType=VARCHAR},
#{user.note,jdbcType=VARCHAR},
#{user.position.id,jdbcType=INTEGER}
)
</foreach>
传入一个user的list,循环一次加一个”,“
实现批处理操作还有另一种方式,就是在创建SqlSession时指定ExecutorType.BATCH。
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, true);
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
TUser user = new TUser();
user.setUserName("第一个操作");
System.out.println(mapper.insertSelective(user));
TUser user1 = new TUser();
user1.setUserName("第二个操作");
System.out.println(mapper.updateIfAndSetOper(user1));
sqlSession.commit();
实质上就是在关闭了JDBC的自动提交事务机制,必须得先关闭自动提交后再创建Statement。
// STEP 1: 关闭自动提交 conn.setAutoCommit(false); stmt=conn.createStatement(); // STEP 2: 创建两个查询 String sql1 = "insert into t_user (userName) values ('第一次插入')"; String sql2 = "insert into t_user (userName) values ('第二次插入')"; stmt.addBatch(sql1); stmt.addBatch(sql2); // STEP 3: 执行语句 int[] executeBatch = stmt.executeBatch(); // STEP 4: 手动提交数据 conn.commit();
两种方式的区别在于,前者是一个sql语句,只不过MySql支持这种语法;而后者是真正的执行了两个sql语句。
关联查询
在关系型数据库中,我们会经常的处理多表关联的问题。
开发手册中对于关联查询的sql语句编写做了如下的要求:
1.超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引;
2.不得使用外键与级联,一切外键概念必须在应用层解决;
3.字段允许适当冗余(减少查询时不必要的join),以提高查询性能,但必须考虑数据一致。
那么,在MyBatis中是怎么优雅的将这些结果返回的呢?
通过上文我们得知,应当用resultMap来接收具体的返回值。
如果处理一对一的关系,就使用association标签;
<resultMap id="BaseResultMap" type="TUser"> <id column="id" property="id" /> <result column="userName" property="userName" /> <result column="realName" property="realName" /> <result column="sex" property="sex" /> <result column="mobile" property="mobile" /> <result column="email" property="email" /> <result column="note" property="note" /> </resultMap> <!-- 可以通过extends来继承一个基础的resultMap.--> <resultMap id="userAndPosition" extends="BaseResultMap" type="TUser"> <!-- 关联POJO中的TPosition字段,为该字段赋值; column为数据表的字段名,property为position对象内部的值.--> <association property="position" javaType="TPosition" columnPrefix="post_" > <id column="id" property="id"/> <result column="name" property="postName"/> <result column="note" property="note"/> </association> </resultMap>
如果处理一对多的关系,就使用collection标签。
<resultMap id="userAndJobs1" extends="BaseResultMap" type="TUser"> <collection property="jobs" ofType="TJobHistory" > <result column="comp_name" property="compName"/> <result column="years" property="years"/> <result column="title" property="title"/> </collection> </resultMap>
二者的使用差不多是一致的,至此我们就通过resultMap完成了关联查询的结果集返回。