MyBatis快速进阶--编码规范

1,226 阅读4分钟

在开发中最重要的就是数据库的开发,其中涉及到了各种各样的规范。本文会结合阿里的编码规范一文,帮助你在掌握了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完成了关联查询的结果集返回。