Mybatis在我所見過的持久化框架裏真心是一個異類,由於它是sql-centric的,而不是基於對象和表映射的。我會在本文中講一下Mybatis幾個重要的技巧,與本文的上一篇文章Hibernate作個對比。java
在ApplicationContext上加上以下配置:mysql
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中作進一步的配置。git
XML<configuration> <typeAliases> <package name="sbeat.model" /> </typeAliases> </configuration>
Mybatis不支持分頁,因此我採用了PageHelper這個插件,首先在你的Mybatis配置文件里加上一下配置github
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>
而後在須要分頁的查詢以前,加上下面一句話:spring
javaPageHelper.startPage(pageNum,PAGE_SIZE);
因爲Mybatis是經過文本替換組裝生成SQL語句的,因此不難發現它的插入和更新一樣是靜態的,對象裏是null的插入也是null。你數據庫的默認值不起做用而是獲得null,那怎麼解決這個問題呢?sql
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標籤咱們解決了這個問題。數據庫
有時候咱們經常想將Collection或者其餘對象直接以Json字符串的形式存在數據庫裏而不是再開一張表,雖然廣泛的觀點是不贊同這種作法,但這種需求倒是實際存在的。怎麼才能在DAO中就將字符串和對象的轉換作掉而不用交給上層顯式地轉換呢?json
採用自定義的Typehandler就能夠,下面給出一個例子mybatis
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的配置文件中加上這麼一句。app
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>
外鍵查詢須要使用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>
在接口定義中使用@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中出現最多的失誤,能夠經過使用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>