一、概述
MyBatis是Apache的一個開源項目,是一個基于Java的持久層框架。它支持普通SQL查詢、存儲過程以及高級映射。消除了幾乎所有的JDBC代碼,并且基本不需要手動去設置參數(shù)和獲取檢索結果。使用XML或者注解進行配置,能夠映射基本數(shù)據(jù)元素、Map接口和POJO到數(shù)據(jù)庫。
二、框架
MyBatis的功能架構分三層:基礎支撐層、數(shù)據(jù)處理層、API接口層。
-
基礎支撐層
負責最基本的功能支撐,包括連接管理、事務管理、配置加載和緩存處理?;A支撐層為上層的數(shù)據(jù)處理層提供了最基礎的支撐。
-
數(shù)據(jù)處理層
負責具體的SQL查詢、SQL解析、SQL執(zhí)行和執(zhí)行結果的映射處理等。數(shù)據(jù)處理層的主要目的是根據(jù)調(diào)用的請求完成一次數(shù)據(jù)庫操作。
-
API接口層
提供給外部使用的接口API,通過這些API來操作數(shù)據(jù)庫。接口層接收到調(diào)用請求會立刻調(diào)用數(shù)據(jù)處理層來完成具體的操作。
三、工作流程
1、加載配置并初始化
通過配置文件或注解,將SQL的配置信息加載成MappedStatement對象,存儲在內(nèi)存中。
2、接收調(diào)用請求
調(diào)用MyBatis提供的API,并將請求傳遞給下層的數(shù)據(jù)處理層進行處理。
3、處理操作請求
- 根據(jù)傳入的SQL的ID查找對應的MappedStatement對象
- 根據(jù)傳入的參數(shù)解析MappedStatement對象,得到要執(zhí)行的SQL
- 獲取數(shù)據(jù)庫連接,執(zhí)行SQL,并得到執(zhí)行結果
- 根據(jù)MappedStatement對象中的結果映射配置,對執(zhí)行結果進行轉換處理,得到最終結果
- 釋放連接資源
4、返回處理結果
四、實現(xiàn)
1、創(chuàng)建與表對應的實體類
2、創(chuàng)建映射文件xxxMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>?
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper .//EN" "http://mybatis.org/dtd/mybatis--mapper.dtd">
<mapper namespace="com.demo.ssm.mapping.userMapper">
<select id="getUser" parameterType="int" resultType="User">
select * from user where id = #{id}
</select>
</mapper>
其中,namespace屬性用來定義mapper的命名空間。<select>標簽中的id屬性用來標識sql語句,值必須唯一。parameterType屬性用來定義查詢時使用的參數(shù)類型。resultType屬性用來定義查詢返回的結果集類型。如果返回的結果集是個列表,應該定義resultMap類型。
<resultMap id="resultListUser" type="User">
<id column="id" property="id" />
<result column="userName" property="userName" />
<id column="userAge" property="userAge" />
</resultMap>
<mapper namespace="com.demo.ssm.mapping.userMapper">
<select id="getUsers" parameterType="string" resultMap="resultListUser">
select * from user where username like #{userName}
</select>
</mapper>
3、配置MyBatis的配置文件
<?xml version="." encoding="UTF-"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config .//EN" "http://mybatis.org/dtd/mybatis--config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
<environment>
<environments>
<mappers>
<mapper resource="com.demo.ssm.mapping.userMapper.xml">
</mappers>
</configuration>
五、關聯(lián)查詢
在實際應用中,經(jīng)常需要進行關聯(lián)查詢,如一對多、多對一等。這時,就需要在配置文件中使用<association>標簽進行關聯(lián)。關聯(lián)查詢的配置有兩種方式:內(nèi)部關聯(lián)和外部關聯(lián)。
1、在內(nèi)部關聯(lián)
<resultMap id="resultUserArticleList" type="Article">
<id property="id" column="aid" />
<result property="title" column="title" />
<result property="content" column="content" />
<association property="user" javaType="User">
<id property="id" column="id" />
<result property="userName" column="userName" />
<result property="userAge" column="userAge" />
</association>
</resultMap>
<select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList">
select user.id,user.userName,user.userAge,article.id aid,article.title,article.content from user,article
where user.id=article.userid and user.id=#{id}
</select>
2、從外部關聯(lián)
<resultMap type="User" id="resultListUser">
<id column="id" property="id" />
<result column="userName" property="userName" />
<result column="userAge" property="userAge" />
</resultMap>
<resultMap id="resultUserArticleList" type="Article">
<id property="id" column="aid" />
<result property="title" column="title" />
<result property="content" column="content" />
<association property="user" javaType="User" resultMap="resultListUser" />
</resultMap>
<select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList">
select user.id,user.userName,user.userAge,article.id aid,article.title,article.content from user,article
where user.id=article.userid and user.id=#{id}
</select>
很顯然,第二種方式更容易達到復用的效果。
六、分頁
在實際應用中,如果查詢返回的結果記錄很多,就需要做物理分頁。不同的數(shù)據(jù)庫,對應不同的實現(xiàn)方法。mysql利用limit offset和pagesize來實現(xiàn),而oracle利用rownum來實現(xiàn)。實現(xiàn)MyBatis的物理分頁。
1、在mapper的sql語句中直接使用分頁條件
<select id="getUserArticles" parameterType="params" resultMap="resultUserArticleList">
select user.id,user.userName,user.userAge,article.id aid,article.title,article.content from user,article
where user.id=article.userid and user.id=#{id} limit #{offset},#{pagesize}
</select>
這里的parameterType是傳入的參數(shù)類或者Map,包含offset和pagesize,以及其他需要的參數(shù)。相對來說,這是比較簡單的一種方式。
2、使用MyBatis插件
更通用的一種方式是使用插件進行分頁。使用插件的話,需要在MyBatis的配置文件中進行設置。
七、動態(tài)SQL
MyBatis的動態(tài)sql語句是基于OGNL表達式的,可以很方便地在sql語句中實現(xiàn)某些邏輯。
1、if
<select id="dynamicIf" parameterType="User" resultType="User">
select * from user where 1 = 1
<if test="userName != null">
and userName = #{userName}
</if>
</select>
如果提供的userName參數(shù)不為空,就為sql語句動態(tài)添加userName=#{userName}的語句。
2、choose
<select id="dynamicChoose" parameterType="User" resultType="User">
select * from user where 1 = 1
<choose>
<when test="userName != null">
and userName = #{userName}
</when>
<otherwise>
and userName = "sean"
</otherwise>
</choose>
</select>
當userName不為空時,為sql語句動態(tài)添加userName=#{userName}。否則,為sql語句動態(tài)添加userName="sean"語句。當條件滿足時,不再繼續(xù)判斷,直接跳出choose。當所有條件都不滿足時,輸出otherwise中的內(nèi)容。
3、where
<select id="dynamicWhere" parameterType="User" resultType="User">
select * from user
<where>
<if test="userName != null">
userName = #{userName}
</if>
</where>
</select>
在where元素的地方輸出一個where,并且能夠智能地處理and和or條件。主要用來簡化sql語句中where條件判斷。
4、trim
<select id="dynamicTrim" parameterType="User" resultType="User">
select * from user
<trim prefix="where" prefixOverrides="and | or">
<if test="userName != null">
userName = #{userName}
</if>
</trim>
</select>
用來給包含的內(nèi)容加上前綴或后綴,也可以把包含內(nèi)容的首部或尾部的某些內(nèi)容覆蓋。通??梢岳胻rim來代替where元素的功能。
5、set
<update id="dynamicSet" parameterType="User">
update user
<set>
<if test="userName != null">
userName = #{userName}
</if>
</set>
where userId = #{userId}
</update>
主要用于更新操作,在包含的語句前輸出一個set,功能和where元素基本相同。
6、foreach
主要用于構建in條件,可以在sql語句中迭代一個集合。foreach元素主要有幾個屬性:
- item:表示每個元素在進行迭代時的別名
- index:表示在迭代時,每次迭代到的位置
- open:表示語句以什么開始
- separator:表示迭代之間以什么作為分隔符
- close:表示語句以什么結束
- collection:表示集合
不同情況下,collection屬性的值是不同的。如果傳入的是單參數(shù)且參數(shù)類型是一個List的時候,屬性值為list。
<select id="dynamicForeachList" resultType="User">
select * from user where userId in
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
如果傳入的是單參數(shù)且參數(shù)類型是一個Array的時候,屬性值為array。
<select id="dynamicForeachArray" resultType="User">
select * from user where userId in
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
如果傳入的是多個參數(shù)時,將參數(shù)封裝成一個Map,屬性值為map里的key。
<select id="dynamicForeachMap" resultType="User">
select * from user where userName like "%"#{userName}"%" and userId in
<foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
八、代碼生成工具
由于MyBatis應用程序需要大量的配置文件,如果完全手工配置,工作量巨大。MyBatis官方推出一個代碼生成工具mybatis-generator-core的jar包,以便提高效率。
代碼生成工具主要有幾個功能:
- 生成pojo與數(shù)據(jù)庫結構相對應
- 如果有主鍵,能夠匹配主鍵;如果沒有主鍵,可以用其他字段匹配
- 動態(tài)select、update、delete方法
- 自動生成接口
- 自動生成mapper,以及對單表的增刪改查語句配置
- 提供例子以供參考
首先需要創(chuàng)建一個配置文件,進行生成工具的各種設置。
<generatorConfiguration>
<!-- 配置mysql驅(qū)動jar包路徑 -->
<classPathEntry location=""
<context id="mysql_tables" targetRuntime="MyBatis3">
<!-- 控制生成代碼中的注釋 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
<property name="suppressDate" value="true" />
</commentGenerator>
<!-- 數(shù)據(jù)庫連接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8"
userId="root"
password="root">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- model層 -->
<javaModelGenerator targetPackage="com.demo.ssm.model" targetProject="src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- mapper映射文件 -->
<sqlMapGenerator targetPackage="com.demo.ssm.mapper" targetProject="src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- mapper接口 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.demo.ssm.inter" targetProject="src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 指定要生成代碼的數(shù)據(jù)庫表 -->
<table schema="mybatis" tableName="user" domainObjectName="User"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
然后創(chuàng)建一個代碼生成類,用于生成代碼。
public class GenMain {
public static void main(String[] args) {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
String genCfg = "/mbgConfiguration.xml";
File configFile = new File(GenMain.class.getResource(genCfg).getFile());
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = null;
try {
config = cp.parseConfiguration(configFile);
} catch (IOException e) {
e.printStackTrace();
} catch (XMLParserException e) {
e.printStackTrace();
}
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = null;
try {
myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
}
try {
myBatisGenerator.generate(null);
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}