1

前言

Mybatis在我所见过的持久化框架里真心是一个异类,因为它是sql-centric的,而不是基于对象和表映射的。我会在本文中讲一下Mybatis几个重要的技巧,与本文的上一篇文章Hibernate做个对比。

Mybatis配置

在ApplicationContext上加上如下配置:

XML<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath*:mapper/*.xml" />
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="sbeat.dao" />
</bean>

然后在mybatis-config.xml中做进一步的配置。

XML<configuration>

    <typeAliases>
        <package name="sbeat.model" />
    </typeAliases>

</configuration>    

Mybatis 分页

Mybatis不支持分页,所以我采用了PageHelper这个插件,首先在你的Mybatis配置文件里加上一下配置

XML
<plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql" /> <!-- 该参数默认为false --> <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 --> <!-- 和startPage中的pageNum效果一样 --> <!-- <property name="offsetAsPageNum" value="false" /> --> <!-- 该参数默认为false --> <!-- 设置为true时,使用RowBounds分页会进行count查询 --> <!-- <property name="rowBoundsWithCount" value="false" /> --> <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 --> <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型) --> <property name="pageSizeZero" value="true" /> <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 --> <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 --> <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 --> <property name="reasonable" value="false" /> <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 --> <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 --> <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 --> <!-- 不理解该含义的前提下,不要随便复制该配置 --> <!-- <property name="params" value="pageNum=start;pageSize=limit;" /> --> </plugin> </plugins>

然后在需要分页的查询之前,加上下面一句话:

javaPageHelper.startPage(pageNum,PAGE_SIZE);

Mybatis 中的sql标签

由于Mybatis是通过文本替换组装生成SQL语句的,所以不难发现它的插入和更新同样是静态的,对象里是null的插入也是null。你数据库的默认值不起作用而是得到null,那怎么解决这个问题呢?

XML<sql id="userColumn">
        <trim suffixOverrides=",">
            <if test="id!=null">id,</if>
            <if test="phone!=null">phone,</if>
            <if test="email!=null">email,</if>
            <if test="photo!=null">photo,</if>
        </trim>
</sql>

<sql id="userValue">
        <trim suffixOverrides=",">
            <if test="id!=null">#{id},</if>
            <if test="phone!=null">#{phone},</if>
            <if test="email!=null">#{email},</if>
            <if test="photo!=null">#{photo},</if>
        </trim>
</sql>

<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into user (<include refid="userColumn" />)
        values (<include refid="userValue"></include>)
</insert>

通过使用include和sql标签我们解决了这个问题。

Mybatis typeHandler

有时候我们常常想将Collection或者其他对象直接以Json字符串的形式存在数据库里而不是再开一张表,虽然普遍的观点是不赞同这种做法,但这种需求却是实际存在的。怎么才能在DAO中就将字符串和对象的转换做掉而不用交给上层显式地转换呢?

采用自定义的Typehandler就可以,下面给出一个例子

java@MappedJdbcTypes(JdbcType.VARCHAR)
public class JSONHandler implements TypeHandler<Object> {

    /**
      * json数据和类名的分隔符号
      * */
     private static final char SPLIT = '/'; 


     /**
      * json 转换成对象
      * */
     private Object jsonToObject(String json) throws RuntimeException{
        if (json == null) {
            return null;
        }
        int index = json.lastIndexOf(SPLIT);
        if (index < 0) {
            return null;
        }
        String key = json.substring(index + 1, json.length());
        json = json.substring(0, index);
        Class<?> cls = null;
        try {
            cls = Class.forName(key);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("序列化成json时找不到指定的类", e);
        }
        Object ob = JSON.parseObject(json, cls);
        return ob;
    }

    public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        if(parameter == null){
             ps.setString(i, null);
             return;
        }
            String json = JSON.toJSONString(parameter);
            json  = json + SPLIT + parameter.getClass().getName();
            ps.setString(i, json);

    }

    public Object getResult(ResultSet rs, String columnName) throws SQLException {
        String  json = rs.getString(columnName);
        return  jsonToObject(json);
    }

    public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
        String json=rs.getString(columnIndex);
        return jsonToObject(json);
    }

    public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String  json = cs.getString(columnIndex);
        return  jsonToObject(json);
    }


}

首先设定处理的JDBCType,显然是变长字符,然后实现给定的接口,最后在Mybatis的配置文件中加上这么一句。

XML
<typeHandlers> <typeHandler handler="sbeat.util.helper.JSONHandler" javaType="java.util.List"/> <typeHandler handler="sbeat.util.helper.JSONHandler" javaType="java.util.Map"/> </typeHandlers>

将Map和List均交由该handler处理,但本人实测,这个好像并没有什么卵用,有用的是在Mapper.xml文件中显式指定,如下所示。

XML<insert>
insert (tags) values (#{tags,typeHandler=sbeat.util.helper.JSONHandler})
</insert>
<resultMap type="Employee" id="employeeResult">
    <result property="tags" column="tags" javaType="java.util.List"  typeHandler="sbeat.util.helper.JSONHandler"/>
</resultMap>

Mybatis 外键查询

外键查询需要使用resultMap的association标签,如下所示

XML    <resultMap type="Message" id="msgResultMap">
        <id property="id" column="id" />
        <result property="created" column="created" />
        <result property="title" column="title" />
        <result property="content" column="content" />
        <result property="has_read" column="has_read" />
        <result property="msgType" column="msgType" />
        <result property="receiverId" column="receiverId" />        
        <association property="sender" javaType="User">
            <id property="id" column="userId"/>
            <result property="name" column="name"/>
            <result property="phone" column="phone"/>
            <result property="passwd" column="passwd"/>
            <result property="photo" column="photo"/>
            <result property="email" column="email"/>
            <result property="userType" column="userType"/>
        </association>
    </resultMap>

Mybatis 多参数

在接口定义中使用@Params注解,并在XML中不定义paramType,如下所示

javapublic List<Feedback> findByTradeId(@Param("tradeId") Long tradeId,@Param("ownerType") UserType ownerType);
XML    <select id="findByTradeId" resultType="Feedback">
        select * from feedback where tradeId=#{tradeId} 
        <if test="ownerType!=null">AND ownerType=#{ownerType}</if>
        ORDER BY created DESC
    </select>

Mybatis 逗号问题

逗号的不注意往往是使用Mybatis中出现最多的失误,可以通过使用where和set一级trim标签来尽量避免,如下所示:

XML<trim suffixOverrides=",">
    <if test="id!=null">id,</if>
    <if test="phone!=null">phone,</if>
</trim>
<set>
    <if test="phone!=null">phone=#{phone},</if>
    <if test="name!=null">name=#{name},</if>
</set>
<where>
    <if test="id!=null">id=#{id}</if>
    <choose>
        <when test="logic_delete!=null">AND logic_delete=#{logic_delete}</when>
        <otherwise>
            AND logic_delete=false
        </otherwise>
    </choose>
    <if test="name!=null"> AND name=#{name}</if>
</where>

suemi
1.5k 声望102 粉丝

程序猿一只,研究僧一枚


下一篇 »
拥抱JPA规范