mybatis入門(mén)詳解

什么是mybatis

MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集。MyBatis 可以對(duì)配置和原生Map使用簡(jiǎn)單的 XML 或注解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫(kù)中的記錄。--官方文檔

說(shuō)白了,mybatis就是用來(lái)簡(jiǎn)化和數(shù)據(jù)庫(kù)的操作,讓程序員不太需要關(guān)注和數(shù)據(jù)庫(kù)連接、事務(wù)等的操作。同時(shí)對(duì)動(dòng)態(tài) sql 有很好的支持,并且可以讓java對(duì)象方便的映射到數(shù)據(jù)庫(kù)sql上,進(jìn)而映射到數(shù)據(jù)庫(kù)中的記錄,這樣就從重點(diǎn)關(guān)注業(yè)務(wù)邏輯。

我們的項(xiàng)目是通過(guò)maven管理的話,使用mybatis的時(shí)候,需要引入mybatis依賴(lài)的包

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

關(guān)于SqlSessionFactory

SqlSessionFactory是mbatis中的核心概念,它相當(dāng)于是數(shù)據(jù)庫(kù)在代碼中的映射了?;趍ybatis的應(yīng)用都會(huì)有一個(gè)sqlSessionFactory,SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在,沒(méi)有任何理由對(duì)它進(jìn)行清除或重建。使用 SqlSessionFactory 的最佳實(shí)踐是在應(yīng)用運(yùn)行期間不要重復(fù)創(chuàng)建多次,多次重建 SqlSessionFactory 被視為一種代碼“壞味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是應(yīng)用作用域。有很多方法可以做到,最簡(jiǎn)單的就是使用單例模式或者靜態(tài)單例模式。因?yàn)楝F(xiàn)在的項(xiàng)目都是集成了spring,所以我在這里只說(shuō)使用spring的情況下,sqlSessionFactory是如何得到的。

mybatis要和spring集成,需要引入Mybatis-Spring模塊

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>x.x.x</version>
</dependency>

在基本的 MyBatis 中,session 工廠可以使用 SqlSessionFactoryBuilder 來(lái)創(chuàng)建。而在 MyBatis-Spring 中,則使用 SqlSessionFactoryBean 來(lái)替代,通過(guò)spring的xml配置文件中,加入下面的代碼,就可以得到sqlSessionFactory

 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource" />
        <property name="typeAliasesPackage" value="com.xdf.ucan.vps.avatar.pojo" />
        <property name="mapperLocations"
                  value="classpath*:sample/config/mappers/**/*.xml" />
    </bean>

大家看到了sqlSessionFactory需要注入一個(gè)DataSource,dataSource就是一個(gè)數(shù)據(jù)源,對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)的連接,它主要是用來(lái)設(shè)置數(shù)據(jù)的環(huán)境信息和數(shù)據(jù)庫(kù)的配置,看一下下面的配置

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx/avatar?useUnicode=true&characterEncoding=UTF8" />
    <property name="username" value="admin" />
    <property name="password" value="admin" />
    <!-- 連接初始值,連接池啟動(dòng)時(shí)創(chuàng)建的連接數(shù)量的初始值 -->
    <property name="initialSize" value="10" />
    <!-- 連接池的最大值,同一時(shí)間可以從池分配的最多連接數(shù)量,0時(shí)無(wú)限制 -->
    <property name="maxActive" value="100" />
    <!-- 最大空閑值.當(dāng)經(jīng)過(guò)一個(gè)高峰時(shí)間后,連接池可以慢慢將已經(jīng)用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 ,0時(shí)無(wú)限制-->
    <property name="maxIdle" value="0
" />
    <!-- 最小空閑值.當(dāng)空閑的連接數(shù)少于閥值時(shí),連接池就會(huì)預(yù)申請(qǐng)去一些連接,以免洪峰來(lái)時(shí)來(lái)不及申請(qǐng) -->
    <property name="minIdle" value="${minIdle}" />
    <!-- 是否對(duì)已備語(yǔ)句進(jìn)行池管理(布爾值),是否對(duì)PreparedStatement進(jìn)行緩存 -->
    <property name="poolPreparedStatements" value="true" />
    <!-- 是否對(duì)sql進(jìn)行自動(dòng)提交 -->
    <property name="defaultAutoCommit" value="true" />
</bean>

可以是任意的JDBC datasource,上面那個(gè)只是mysql的一個(gè)例子
從配置信息看,mapperLocations屬性含義就很明顯了。mapperLocations 屬性使用一個(gè)資源位置的 list。 這個(gè)屬性可以用來(lái)指定 MyBatis 的 XML 映射器文件的位置。 它的值可以包含 Ant 樣式來(lái)加載一個(gè)目錄中所有文件, 或者從基路徑下 遞歸搜索所有路徑

<property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" />

這會(huì)從類(lèi)路徑下加載在 sample.config.mappers 包和它的子包中所有的 MyBatis 映射器 XML 文件。這個(gè)文件具體是什么內(nèi)容我們一會(huì)細(xì)講
typeAliasesPackage :它一般對(duì)應(yīng)我們的實(shí)體類(lèi)所在的包,這個(gè)時(shí)候會(huì)自動(dòng)取對(duì)應(yīng)包中不包括包名的簡(jiǎn)單類(lèi)名作為包括包名的別名。多個(gè)package之間可以用逗號(hào)或者分號(hào)等來(lái)進(jìn)行分隔。這樣的話,其他mapper的配置文件中就可以通過(guò)別名使用它了
sqlSessionFactory還有很多其他屬性的配置,想要了解可以查看官方文檔去。我們這兒只說(shuō)一些常用的配置信息。

關(guān)于SqlSession

正如從名字上看到的,sqlsession是有生命周期,可以理解對(duì)應(yīng)一次數(shù)據(jù)庫(kù)事務(wù),基本可以理解為一次事務(wù)對(duì)應(yīng)一個(gè)sqlsession。如果你現(xiàn)在正在使用一種 Web 框架(SpringMVC),開(kāi)發(fā)一個(gè)web項(xiàng)目,要考慮 SqlSession 放在一個(gè)和 HTTP 請(qǐng)求對(duì)象相似的作用域中。換句話說(shuō),每次收到的 HTTP 請(qǐng)求,就可以打開(kāi)一個(gè) SqlSession,返回一個(gè)響應(yīng),就關(guān)閉它。這個(gè)關(guān)閉操作是很重要的,你應(yīng)該把這個(gè)關(guān)閉操作放到 finally 塊中以確保每次都能執(zhí)行關(guān)閉。下面的示例就是一個(gè)確保 SqlSession 關(guān)閉的標(biāo)準(zhǔn)模式:

SqlSession session = sqlSessionFactory.openSession();
try {
  // do work
} finally {
  session.close();
}

sqlsession是從SqlSessionFactory獲取的。sqlsession包含了數(shù)據(jù)庫(kù)執(zhí)行 SQL 命令所需的所有方法。你可以通過(guò) SqlSession 實(shí)例來(lái)直接執(zhí)行已映射的 SQL 語(yǔ)句。在使用spring的時(shí)候不需要像上面那樣關(guān)注sqlsession的創(chuàng)建和關(guān)閉,sqlsession的整個(gè)生命周期都由spring來(lái)管理,但是你還是要理解sqlsession的生命周期的。在spring配置中獲取sqlSession方法有好幾種
1.SqlSessionTemplate
2.SqlSessionDaoSupport
3.MapperFactoryBean
我們?cè)谶@兒重點(diǎn)說(shuō)一下第三種方式。因?yàn)榈谌N使用起來(lái)是最方便的。現(xiàn)在大部分項(xiàng)目也是使用第三種方式。它在背后使用了java動(dòng)態(tài)代理技術(shù)。使用注入的映射器代碼,在 MyBatis,Spring 或 MyBatis-Spring 上面不會(huì)有直接的依賴(lài)。 MapperFactoryBean 創(chuàng)建的代理控制開(kāi)放和關(guān)閉 session,翻譯任意的異常到 Spring 的 DataAccessException 異常中。此外,如果需要或參與到一個(gè)已經(jīng)存在活動(dòng)事務(wù)中,代理將 會(huì)開(kāi)啟一個(gè)新的 Spring 事務(wù)。
數(shù)據(jù)映射器接口可以按照如下做法加入到 Spring 中:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserDAO" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

MapperFactoryBean 創(chuàng)建的代理類(lèi)實(shí)現(xiàn)了 UserDAO 接口,并且在運(yùn)行時(shí)注入到應(yīng)用程序中。 因?yàn)榇韯?chuàng)建在運(yùn)行時(shí)環(huán)境中(Runtime,譯者注) ,那么指定的映射器必須是一個(gè)接口,而 不是一個(gè)具體的實(shí)現(xiàn)類(lèi),也就是說(shuō)你不需要寫(xiě)一個(gè)UserDAO的實(shí)現(xiàn)類(lèi)。spring在運(yùn)行時(shí)會(huì)通過(guò)代理創(chuàng)建一個(gè)UserDAO實(shí)現(xiàn)類(lèi),這個(gè)實(shí)現(xiàn)類(lèi)會(huì)創(chuàng)建sqlSession,并且會(huì)和一個(gè)Mybatis的映射器文件對(duì)應(yīng)。下面是UserDAO接口的示例代碼

package cn.xdf.ucan.vps.avatar.dao;
import cn.xdf.ucan.vps.avatar.pojo.A_Update;
import java.util.List;
public interface A_UpdateDao {
    int deleteByPrimaryKey(Long id);
    int insert(A_Update record);
    int insertSelective(A_Update record);
    A_Update selectByPrimaryKey(Long id);
    A_Update selectByVersion(String version);
    List<A_Update> selectNextVersionsById(Long id);
    List<A_Update> selectAll();
    int updateByPrimaryKeySelective(A_Update record);

    int updateByPrimaryKey(A_Update record);
}

這個(gè)類(lèi)的方法怎么和一個(gè)sql對(duì)應(yīng),我們后面會(huì)說(shuō)明。
如果 UserDAO 有一個(gè)對(duì)應(yīng)的 MyBatis 的 XML 映射器文件, 如果 XML 文件在類(lèi)路徑的 位置和映射器類(lèi)相同時(shí), 它會(huì)被 MapperFactoryBean 自動(dòng)解析。 沒(méi)有必要在 MyBatis 配置文 件 中 去 指 定 映 射 器 , 除 非 映 射 器 的 XML 文 件 在 不 同 的 類(lèi) 路 徑 下 ,關(guān)于怎么映射,我們?cè)谙旅嬲f(shuō)明
注意,當(dāng) MapperFactoryBean 需要 SqlSessionFactory 或 SqlSessionTemplate 時(shí)。 這些可以通過(guò)各自的 SqlSessionFactory 或 SqlSessionTemplate 屬性來(lái)設(shè)置, 或者可以由 Spring 來(lái)自動(dòng)裝配。如果兩個(gè)屬性都設(shè)置了,那么 SqlSessionFactory 就會(huì)被忽略,因?yàn)镾qlSessionTemplate 是需要有一個(gè) session 工廠的設(shè)置; 那個(gè)工廠會(huì)由 MapperFactoryBean. 來(lái)使用。

Mapper XML 文件

MyBatis 的真正強(qiáng)大在于它的映射語(yǔ)句,也是它的魔力所在。由于它的異常強(qiáng)大,映射器的 XML 文件就顯得相對(duì)簡(jiǎn)單。如果拿它跟具有相同功能的 JDBC 代碼進(jìn)行對(duì)比,你會(huì)立即發(fā)現(xiàn)省掉了將近 95% 的代碼。MyBatis 就是針對(duì) SQL 構(gòu)建的,并且比普通的方法做的更好。
SQL 映射文件有很少的幾個(gè)頂級(jí)元素(按照它們應(yīng)該被定義的順序):

  • cache – 給定命名空間的緩存配置。
  • cache-ref – 其他命名空間緩存配置的引用。
  • resultMap – 是最復(fù)雜也是最強(qiáng)大的元素,用來(lái)描述如何從數(shù)據(jù)庫(kù)結(jié)果集中來(lái)加載對(duì)象。
  • parameterMap – 已廢棄!老式風(fēng)格的參數(shù)映射。內(nèi)聯(lián)參數(shù)是首選,這個(gè)元素可能在將來(lái)被移除,這里不會(huì)記錄。
  • sql – 可被其他語(yǔ)句引用的可重用語(yǔ)句塊。
  • insert – 映射插入語(yǔ)句
  • update – 映射更新語(yǔ)句
  • delete – 映射刪除語(yǔ)句
  • select – 映射查詢語(yǔ)句
    我們通過(guò)一個(gè)例子,一步步說(shuō)清楚這些元素,隨便找的一個(gè)配置文件,請(qǐng)忽略字段名,實(shí)在不想改了。
<?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="cn.xdf.ucan.vps.avatar.dao.UserDao" >
<resultMap id="BaseResultMap" type="cn.xdf.ucan.vps.avatar.pojo.User" >
    <id column="ID" property="id" jdbcType="DECIMAL" />
    <result column="VERSION" property="version" jdbcType="VARCHAR" />
    <result column="URL" property="url" jdbcType="VARCHAR" />
    <result column="FILENAME" property="filename" jdbcType="VARCHAR" />
    <result column="MD5SUM" property="md5sum" jdbcType="VARCHAR" />
    <result column="ANGEL_USER" property="angelUser" jdbcType="VARCHAR" />
    <result column="UPDATE_TYPE" property="updateType" jdbcType="VARCHAR" />
    <result column="updateinfo" property="updateinfo" jdbcType="VARCHAR" />
    <result column="CREATETIME" property="createtime" jdbcType="TIMESTAMP" />
    <result column="UPDATETIME" property="updatetime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Base_Column_List" >
    ID, VERSION, URL, FILENAME, MD5SUM, ANGEL_USER, UPDATE_TYPE, updateinfo, CREATETIME, UPDATETIME
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select
    <include refid="Base_Column_List" />
    from a_update
    where ID = #{id,jdbcType=DECIMAL}
  </select>
  <select id="selectByVersion" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from a_update
    where version = #{version,jdbcType=VARCHAR}
  </select>
  <select id="selectNextVersionsById" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from a_update
    where id > #{id,jdbcType=DECIMAL}
    ORDER BY id ASC
  </select>
  <select id="selectAll" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from a_update
    ORDER BY id DESC
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from a_update
    where ID = #{id,jdbcType=DECIMAL}
  </delete>
  <insert id="insert" parameterType="cn.xdf.ucan.vps.avatar.pojo.A_Update" >
    insert into a_update (VERSION, URL,
      FILENAME, MD5SUM, ANGEL_USER, 
      UPDATE_TYPE,updateinfo, CREATETIME, UPDATETIME
      )
    values (#{version,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR},
      #{filename,jdbcType=VARCHAR}, #{md5sum,jdbcType=VARCHAR}, #{angelUser,jdbcType=VARCHAR}, 
      #{updateType,jdbcType=VARCHAR},#{updateinfo,jdbcType=VARCHAR}, now(),now()
      )
  </insert>
  <insert id="insertSelective" parameterType="cn.xdf.ucan.vps.avatar.pojo.A_Update" >
    insert into a_update
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="version != null" >
        VERSION,
      </if>
      <if test="url != null" >
        URL,
      </if>
      <if test="filename != null" >
        FILENAME,
      </if>
      <if test="md5sum != null" >
        MD5SUM,
      </if>
      <if test="angelUser != null" >
        ANGEL_USER,
      </if>
      <if test="updateType != null" >
        UPDATE_TYPE,
      </if>
      <if test="updateinfo != null" >
        updateinfo,
      </if>
        CREATETIME,
        UPDATETIME,
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="version != null" >
        #{version,jdbcType=VARCHAR},
      </if>
      <if test="url != null" >
        #{url,jdbcType=VARCHAR},
      </if>
      <if test="filename != null" >
        #{filename,jdbcType=VARCHAR},
      </if>
      <if test="md5sum != null" >
        #{md5sum,jdbcType=VARCHAR},
      </if>
      <if test="angelUser != null" >
        #{angelUser,jdbcType=VARCHAR},
      </if>
      <if test="updateType != null" >
        #{updateType,jdbcType=VARCHAR},
      </if>
      <if test="updateinfo != null" >
        #{updateinfo,jdbcType=VARCHAR},
      </if>
      now(),
      now(),
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="cn.xdf.ucan.vps.avatar.pojo.A_Update" >
    update a_update
    <set >
      <if test="version != null" >
        VERSION = #{version,jdbcType=VARCHAR},
      </if>
      <if test="url != null" >
        URL = #{url,jdbcType=VARCHAR},
      </if>
      <if test="filename != null" >
        FILENAME = #{filename,jdbcType=VARCHAR},
      </if>
      <if test="md5sum != null" >
        MD5SUM = #{md5sum,jdbcType=VARCHAR},
      </if>
      <if test="angelUser != null" >
        ANGEL_USER = #{angelUser,jdbcType=VARCHAR},
      </if>
      <if test="updateType != null" >
        UPDATE_TYPE = #{updateType,jdbcType=VARCHAR},
      </if>
      <if test="updateinfo != null" >
        updateinfo = #{updateinfo,jdbcType=VARCHAR},
      </if>
      <if test="updatetime != null" >
        UPDATETIME = now(),
      </if>
    </set>
    where ID = #{id,jdbcType=DECIMAL}
  </update>
  <update id="updateByPrimaryKey" parameterType="cn.xdf.ucan.vps.avatar.pojo.A_Update" >
    update a_update
    set VERSION = #{version,jdbcType=VARCHAR},
      URL = #{url,jdbcType=VARCHAR},
      FILENAME = #{filename,jdbcType=VARCHAR},
      MD5SUM = #{md5sum,jdbcType=VARCHAR},
      ANGEL_USER = #{angelUser,jdbcType=VARCHAR},
      UPDATE_TYPE = #{updateType,jdbcType=VARCHAR},
      updateinfo = #{updateinfo,jdbcType=VARCHAR},
      UPDATETIME = now()
    where ID = #{id,jdbcType=DECIMAL}
  </update>
</mapper>

我們來(lái)一步步解釋xml文件

<mapper namespace="cn.xdf.ucan.vps.avatar.dao.A_UpdateDao" >

namespace:Mybatis中namespace可以簡(jiǎn)單的理解為用于綁定dao接口,這也就解釋了上面的UserDAO是怎么找到對(duì)應(yīng)的mapper方法的。dao接口的方法對(duì)應(yīng)mapper中的sql語(yǔ)名,具體怎么對(duì)應(yīng),我們下面會(huì)說(shuō)明

<resultMap id="BaseResultMap" type="cn.xdf.ucan.vps.avatar.pojo.A_Update" >
    <id column="ID" property="id" jdbcType="DECIMAL" />
    <result column="VERSION" property="version" jdbcType="VARCHAR" />
    <result column="URL" property="url" jdbcType="VARCHAR" />
    <result column="FILENAME" property="filename" jdbcType="VARCHAR" />
    <result column="MD5SUM" property="md5sum" jdbcType="VARCHAR" />
    <result column="ANGEL_USER" property="angelUser" jdbcType="VARCHAR" />
    <result column="UPDATE_TYPE" property="updateType" jdbcType="VARCHAR" />
    <result column="updateinfo" property="updateinfo" jdbcType="VARCHAR" />
    <result column="CREATETIME" property="createtime" jdbcType="TIMESTAMP" />
    <result column="UPDATETIME" property="updatetime" jdbcType="TIMESTAMP" />
  </resultMap>

這段xml的作用是實(shí)現(xiàn)javaBean到數(shù)據(jù)庫(kù)字段的映射,如果數(shù)據(jù)庫(kù)中的字段名和javaBean的字段名一樣,也是可以自動(dòng)映射的,但是為了保險(xiǎn)(可能會(huì)修改字段名)和直觀起見(jiàn),還通過(guò)上面的映射文件來(lái)做吧

<sql id="Base_Column_List" >
    ID, VERSION, URL, FILENAME, MD5SUM, ANGEL_USER, UPDATE_TYPE, updateinfo, CREATETIME, UPDATETIME
  </sql>

這段xml的含義就是,相當(dāng)于給一段sql語(yǔ)句定義了一個(gè)別名,你在其他地方可以使用,這也編程里面的一個(gè)重要思想,避免重復(fù)代碼。

<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select
    <include refid="Base_Column_List" />
    from a_update
    where ID = #{id,jdbcType=DECIMAL}
  </select>

這段xml我們要重點(diǎn)說(shuō)一下,這里面包含了很多信息。
1.id 這個(gè)sql語(yǔ)句在這個(gè)命名空間(namespace)的唯一標(biāo)識(shí),這個(gè)id值對(duì)應(yīng)于UserDao的一個(gè)方法名
2.** parameterType** 將會(huì)傳入這條語(yǔ)句的參數(shù)類(lèi)的完全限定名或別名,可以是java類(lèi),也可以是自定義的類(lèi)
3.resultMap sql查詢結(jié)果對(duì)應(yīng)java映射,resultMap的值就是上面定義的resulMap元素的id值,resultMap元素可以有多個(gè),當(dāng)然id必須是唯一的
4.resultType 也是sql查詢結(jié)果對(duì)應(yīng)的java映射,是一個(gè)java的類(lèi),比如說(shuō)可以返回一個(gè)java.util.Map,還比如你的select語(yǔ)句只返回一個(gè)Id,那么你就可以用resultType="java.lang.Long"來(lái)直接映射,而不用去定義一個(gè)resultMap,注意:resultType不能和resultMap同時(shí)使用
還有其他屬性,就不一一說(shuō)明了,感興趣的,可以去看官方文檔~

我們來(lái)看一下sql 語(yǔ)句中的一些信息

    <include refid="Base_Column_List" />

這個(gè)很好理解,就是把上面用<sql>元素定義的sql片段引入進(jìn)來(lái)。

** where ID = #{id,jdbcType=DECIMAL}**這個(gè)是mybatis的核心所在!id的值是在使用的時(shí)候,動(dòng)態(tài)傳入的,這樣sql語(yǔ)句才能復(fù)用。這就告訴 MyBatis 創(chuàng)建一個(gè)預(yù)處理語(yǔ)句參數(shù),通過(guò) JDBC,這樣的一個(gè)參數(shù)在 SQL 中會(huì)由一個(gè)“?”來(lái)標(biāo)識(shí),并被傳遞到一個(gè)新的預(yù)處理語(yǔ)句中,如果用java代碼的話,就像這樣:

// Similar JDBC code, NOT MyBatis…
String selectPerson = "select *from a_update where ID = ?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);

這部分我們一會(huì)會(huì)再單獨(dú)講到,就是mybatis中強(qiáng)大的動(dòng)態(tài)SQL。上面配置文件中其他insert、update、delete和select的配置差不多,我們就不一一展開(kāi)了。他們不一樣的地方就是sql語(yǔ)句中的動(dòng)態(tài)sql使用,我著重說(shuō)一下

動(dòng)態(tài)sql

MyBatis 的強(qiáng)大特性之一便是它的動(dòng)態(tài) SQL。如果你有使用 JDBC 或其他類(lèi)似框架的經(jīng)驗(yàn),你就能體會(huì)到根據(jù)不同條件拼接 SQL 語(yǔ)句有多么痛苦。拼接的時(shí)候要確保不能忘了必要的空格,還要注意省掉列名列表最后的逗號(hào)。利用動(dòng)態(tài) SQL 這一特性可以徹底擺脫這種痛苦。

通常使用動(dòng)態(tài) SQL 不可能是獨(dú)立的一部分,MyBatis 當(dāng)然使用一種強(qiáng)大的動(dòng)態(tài) SQL 語(yǔ)言來(lái)改進(jìn)這種情形,這種語(yǔ)言可以被用在任意的 SQL 映射語(yǔ)句中。

動(dòng)態(tài) SQL 元素和使用 JSTL 或其他類(lèi)似基于 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多的元素需要來(lái)了解。MyBatis 3 大大提升了它們,現(xiàn)在用不到原先一半的元素就可以了。MyBatis 采用功能強(qiáng)大的基于 OGNL 的表達(dá)式來(lái)消除其他元素。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
    這部分官方文檔說(shuō)的很詳細(xì)了,請(qǐng)移步官方文檔去看吧動(dòng)態(tài)sql

事務(wù)

事務(wù)是數(shù)據(jù)庫(kù)操作的重要部分,mybatis-spring 會(huì)讓 mybatis 參入到 spring 的事務(wù)管理中。MyBatis-Spring 利用了存在于 Spring 中的 DataSourceTransactionManager來(lái)給mybatis 加上事務(wù)特性。
一旦 Spring 的 PlatformTransactionManager 配置好了,你可以在 Spring 中以你通常的做 法來(lái)配置事務(wù)。@Transactional 注解和 AOP(Aspect-Oriented Program,面向切面編程,譯 者注)樣式的配置都是支持的。在事務(wù)處理期間,一個(gè)單獨(dú)的 SqlSession 對(duì)象將會(huì)被創(chuàng)建 和使用。當(dāng)事務(wù)完成時(shí),這個(gè) session 會(huì)以合適的方式提交或回滾。
一旦事務(wù)創(chuàng)建之后,MyBatis-Spring 將會(huì)透明的管理事務(wù)。在你的 DAO 類(lèi)中就不需要額 外的代碼了
要 開(kāi) 啟 Spring 的 事 務(wù) 處 理 , 在 Spring 的 XML 配 置 文 件 中 簡(jiǎn) 單 創(chuàng) 建 一 個(gè) DataSourceTransactionManager 對(duì)象:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>

關(guān)于dataSource我們前面已經(jīng)講過(guò)了。但是要注意的是,為事務(wù)管理器指定的 DataSource 必須和用來(lái)創(chuàng)建 SqlSessionFactoryBean 的 是同一個(gè)數(shù)據(jù)源,否則事務(wù)管理器就無(wú)法工作了。
配置了DataSourceTransactionManager,就可以配置事務(wù)作用在那些類(lèi)和方法上了。

<!-- 攔截器方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*"  propagation="REQUIRED" />
            <tx:method name="insert*"  propagation="REQUIRED" />
            <tx:method name="get*"  propagation="SUPPORTS" />
            <tx:method name="find*"  propagation="SUPPORTS" />
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="transactionPointcut" expression="execution(* cn.test.manage.*.*(..))" />
        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
    </aop:config>

上面的配置就表示事務(wù)作用于 cn.test.manage 包下的所有的類(lèi)會(huì)在事務(wù)管理下,同時(shí)transactionAdvice配置了那些方法受事務(wù)管理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容