mybatis 動(dòng)態(tài)SQL以及和spring的集成
3.6 動(dòng)態(tài)SQL
有時(shí)候浑玛,靜態(tài)的 SQL 語(yǔ)句并不能滿足應(yīng)用程序的需求焙压。我們可以根據(jù)一些條件谆趾,來(lái)動(dòng)態(tài)地構(gòu)建 SQL 語(yǔ)句于购。
例如巩梢,在 Web 應(yīng)用程序中创泄,有可能有一些搜索界面,需要輸入一個(gè)或多個(gè)選項(xiàng)括蝠,然后根據(jù)這些已選擇的條件去執(zhí)行檢 索操作鞠抑。在實(shí)現(xiàn)這種類型的搜索功能,我們可能需要根據(jù)這些條件 來(lái)構(gòu)建動(dòng)態(tài)的 SQL 語(yǔ)句忌警。如果用戶提供了任何輸入條 件搁拙,我們需要將那個(gè)條件 添加到SQL語(yǔ)句的WHERE子句中。
MyBatis 通過(guò)使用if,choose,where,foreach,trim元素提供了對(duì)構(gòu)造動(dòng)態(tài) SQL 語(yǔ)句的高級(jí)別支持慨蓝。
3.6.1 if條件
if元素被用來(lái)有條件地嵌入 SQL 片段感混,如果測(cè)試條件被賦值為 true,則相應(yīng)地 SQL 片段將會(huì)被添加到 SQL 語(yǔ) 句中礼烈。
假定我們有一個(gè)課程搜索界面,設(shè)置了 講師(Tutor)下拉列表框婆跑,課程名稱(CourseName)文本輸入框此熬,開(kāi)始 時(shí)間(StartDate)輸入框,結(jié)束時(shí)間(EndDate)輸入框滑进,作為搜索條件犀忱。假定課講師下拉列表是必須選的,其他的 都是可選的扶关。
當(dāng)用戶點(diǎn)擊 搜索 按鈕時(shí)阴汇,我們需要顯示符合以下條件的成列表:
- 特定講師的課程
- 課程名 包含輸入的課程名稱關(guān)鍵字的課程;如果課程名稱輸入為空,則取所有課程
- 在開(kāi)始時(shí)間和結(jié)束時(shí)間段內(nèi)的課程
我們可以對(duì)應(yīng)的映射語(yǔ)句节槐,如下所示:
<resultMap type="Course" id="CourseResult">
<id property="courseId" column="course_id" />
<result property="name" column="name" />
<result property="description" column="" />
<result property="startDate" column="start_date" />
<result property="endDate" column="end_date" />
</resultMap>
<select id="searchCourses" parameterType="hashMap" resultMap="CourseResult">
select * from courses
where tutor_id=#{tutorId}
<if test="courseName!=null">
and name like #{courseName}
</if>
<if test="startDate!=null">
and start_date <![CDATA[>=]]> #{startDate}
</if>
<if test="endDate!=null">
and end_date <![CDATA[<=]]> endDate
</if>
</select>
public interface CourseMapper
{
List<Course> searchCourses(Map<String, Object> map);
}
public void searchCourses()
{
Map<String, Object> map = new HashMap<String, Object>();
map.put("tutorId", 1);
map.put("courseName", "%java%");
map.put("startDate", new Date());
CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
List<Course> courses = mapper.searchCourses(map);
for (Course course : courses)
{
System.out.println(course);
}
}
此處將生成查詢語(yǔ)句 SELECT * FROM COURSES WHERE TUTOR_ID= ? AND NAME like ? AND START_DATE >= ?搀庶。 準(zhǔn)備根據(jù)給定條件的動(dòng)態(tài) SQL 查詢將會(huì)派上用場(chǎng)。
MyBatis 是使用 ONGL(Object Graph Navigation Language)表達(dá)式來(lái)構(gòu)建動(dòng)態(tài) SQL 語(yǔ)句
3.6.2 choose,when 和 otherwise 條件
有時(shí)候铜异,查詢功能是以查詢類別為基礎(chǔ)的哥倔。首先,用戶需要選擇是否希望通過(guò)選擇 講師揍庄,課程名稱咆蒿,開(kāi)始時(shí)間, 或結(jié)束時(shí)間作為查詢條件類別來(lái)進(jìn)行查詢,然后根據(jù)選擇的查詢類別沃测,輸入相應(yīng)的參數(shù)蒂破。在這樣的情景中,我們需要 只使用其中一種查詢類別寞蚌。
MyBatis 提供了choose元素支持此類型的 SQL 預(yù)處理.
現(xiàn)在讓我們書寫一個(gè)適用此情景的 SQL 映射語(yǔ)句田巴。如果沒(méi)有選擇查詢類別挟秤,則查詢開(kāi)始時(shí)間在今天之后的課程
<select id="searchCourses2" parameterType="hashMap" resultMap="CourseResult">
select * from courses
<choose>
<when test="searchBy=='Tutor'">
where tutor_id=#{tutorId}
</when>
<when test="searchBy=='CourseName'">
where name like #{courseName}
</when>
<otherwise>
where start_date <![CDATA[>=]]> #{startDate}
</otherwise>
</choose>
</select>
MyBatis 計(jì)算choose測(cè)試條件的值,且使用第一個(gè)值為 TRUE 的子句艘刚。如果沒(méi)有條件為 true管宵,則使用<otherwise> 內(nèi)的子句攀甚。
3.6.2 where條件
有時(shí)候秋度,所有的查詢條件(criteria)應(yīng)該是可選的。在需要使用至少一種查詢條件的情況下埠居,我們應(yīng)該使用 WHERE 子句事期。并且, 如果有多個(gè)條件绎橘,我們需要在條件中添加AND或OR唠倦。MyBatis提供了where元素支持這種類型的動(dòng) 態(tài) SQL 語(yǔ)句牵敷。
在我們查詢課程界面,我們假設(shè)所有的查詢條件是可選的靶瘸。進(jìn)而,當(dāng)需要提供一個(gè)或多個(gè)查詢條件時(shí)屋剑,應(yīng)該改使用 WHERE 子句诗眨。
<select id="searchCourses3" parameterType="hashMap" resultMap="CourseResult">
select * from courses
<where>
<if test="tutorId!=null">
tutor_id=#{tutorId}
</if>
<if test="courseName!=null">
and name like #{courseName}
</if>
<if test="startDate!=null">
and start_date <![CDATA[>=]]> #{startDate}
</if>
<if test="endDate!=null">
and end_date <![CDATA[<=]]> #{endDate}
</if>
</where>
</select>
where元素只有在其內(nèi)部標(biāo)簽有返回內(nèi)容時(shí)才會(huì)在動(dòng)態(tài)語(yǔ)句上插入 WHERE 條件語(yǔ)句匠楚。并且,如果 WHERE 子句以 AND 或者 OR 打頭峡懈,則打頭的 AND 或 OR 將會(huì)被移除与斤。
如果 tutor_id 參數(shù)值為 null撩穿,并且 courseName 參數(shù)值不為 null,則where標(biāo)簽會(huì)將 AND name like #{courseName} 中的 AND 移除掉雾狈,生成的 SQL WHERE 子句為:where name like #{courseName}抵皱。
3.6.4 trim條件
trim元素和where元素類似叨叙,但是<trim>提供了在添加前綴/后綴 或者 移除前綴/后綴方面提供更大的靈活 性堪澎。
<select id="searchCourses" parameterType="hashMap" resultMap="CourseResult">
select * from courses
<trim prefix="where" prefixOverrides="AND | OR">
<if test="tutorId!=null">
tutor_id=#{tutorId}
</if>
<if test="courseName!=null">
and name like #{courseName}
</if>
</trim>
</select>
這里如果任意一個(gè)if條件為 true,trim元素會(huì)插入 WHERE,并且移除緊跟 WHERE 后面的 AND 或 OR
3.6.5 foreach 循環(huán)
另外一個(gè)強(qiáng)大的動(dòng)態(tài) SQL 語(yǔ)句構(gòu)造標(biāo)簽即是foreach钮呀。它可以迭代遍歷一個(gè)數(shù)組或者列表爽醋,構(gòu)造 AND/OR 條件或 一個(gè) IN 子句便脊。
假設(shè)我們想找到 tutor_id 為 1,3遂赠,6 的講師所教授的課程,我們可以傳遞一個(gè) tutor_id 組成的列表給映射語(yǔ) 句筷弦,然后通過(guò)<foreach>遍歷此列表構(gòu)造動(dòng)態(tài) SQL抑诸。
<select id="searchCoursesByTutors1" parameterType="map" resultMap="CourseResult">
select * from courses
<if test="tutorIds!=null">
<where>
<foreach collection="tutorIds" item="tutorId">
or tutor_id=#{tutorId}
</foreach>
</where>
</if>
</select>
public void searchCoursesByTutors() {
Map<String, Object> map = new HashMap<>();
List<Integer> tutorIds = new ArrayList<>();
tutorIds.add(1);
tutorIds.add(2);
tutorIds.add(6);
map.put("tutorIds", tutorIds);
TutorService tutorService = new TutorService();
List<Course> courses = tutorService.searchCoursesByTutors(map);
System.out.println("++++");
for (Course course : courses) {
System.out.println("-----");
System.out.println(course);
}
}
看一下怎樣使用<foreach>生成 IN 子句:
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
select * from courses
<if test="tutorIds!=null">
<where>
tutor_id in
<foreach collection="tutorIds" item="tutorId" open="(" separator="," close=")">
#{tutorId}
</foreach>
</where>
</if>
</select>
3.6.6 set 條件:
set元素和where元素類似,如果其內(nèi)部條件判斷有任何內(nèi)容返回時(shí)奸绷,他會(huì)插入 SET SQL 片段异希。
<update id="updateTutor" parameterType="Tutor">
update tutors
<set>
<if test="name!=null">name=#{name},</if>
<if test="email!=null">email=#{email},</if>
</set>
where tutor_id=#{tutorId}
</update>
這里称簿,如果if條件返回了任何文本內(nèi)容,set將會(huì)插入set關(guān)鍵字和其文本內(nèi)容父虑,并且會(huì)剔除將末尾的 “士嚎,”悔叽。 在上述的例子中娇澎,如果 email!=null,set將會(huì)讓會(huì)移除email=#{email}后的逗號(hào)“,”,生成 set email=#{email} 括细。
3.7 mybatis 其它功能
除了簡(jiǎn)化數(shù)據(jù)庫(kù)編程外奋单,MyBatis 還提供了各種功能猫十,這些對(duì)實(shí)現(xiàn)一些常用任務(wù)非常有用呆盖,比如按頁(yè)加載表數(shù)據(jù)絮短,存取 CLOB/BLOB 類型的數(shù)據(jù)丁频,處理枚舉類型值邑贴,等等拢驾。讓我們來(lái)看看其中一些特性吧。
3.7.1 處理枚舉類型
MyBatis支持開(kāi)箱方式持久化enum 類型屬性咖为。假設(shè)STUDENTS表中有一列g(shù)ender(性別)類型為varchar躁染,存 儲(chǔ)”MALE”或者“FEMALE”兩種值架忌。并且叹放,Student 對(duì)象有一個(gè) enum 類型的 gender 屬性,如下所示:
public enum Gender
{
FEMALE,
MALE
}
默認(rèn)情況下埋嵌,MyBatis 使用 EnumTypeHandler 來(lái)處理 enum 類型的 Java 屬性莉恼,并且將其存儲(chǔ)為 enum 值的名稱速那。你 不需要為此做任何額外的配置端仰。你可以可以向使用基本數(shù)據(jù)類型屬性一樣使用 enum 類型屬性田藐,代碼如下:
public class Student
{
private Integer id;
private String name;
private String email;
private PhoneNumber phone;
private Address address;
private Gender gender;
//setters and getters
}
<insert id="insertStudent" parameterType="Student"
useGeneratedKeys="true" keyProperty="id">
insert into students(name,email,addr_id, phone,gender)
values(#{name},#{email},#{address.addrId},#{phone},#{gender})
</insert>
當(dāng)你執(zhí)行 insertStudent 語(yǔ)句的時(shí)候,MyBatis 會(huì)取 Gender 枚舉(FEMALE/MALE)的名稱踊餐,然后將其存儲(chǔ)到 GENDER 列中吝岭。
如果你希望存儲(chǔ)原 enum 的順序位置吧寺,而不是 enum 名稚机,你需要明確地配置它。
如果你想存儲(chǔ) FEMALE 為 0失乾,MALE 為 1 到 gender 列中碱茁,你需要在 mybatis-config.xml 文件中配置 EnumOrdinalTypeHandler:
<typeHandler
handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.mrq.domain.Gender"/>
使用順序位置為值存儲(chǔ)到數(shù)據(jù)庫(kù)時(shí)要當(dāng)心蕾额。順序值是根據(jù) enum 中的聲明順序賦值 的诅蝶。如果你改變了 Gender enum 的聲明順序,則數(shù)據(jù)庫(kù)存儲(chǔ)的數(shù)據(jù)和此順序值就 不匹配了语盈。
3.7.2 傳入多個(gè)輸入?yún)?shù)
MyBatis 中的映射語(yǔ)句有一個(gè) parameterType 屬性來(lái)制定輸入?yún)?shù)的類型刀荒。如果我們想給映射語(yǔ)句傳入多個(gè)參數(shù)的 話缠借,我們可以將所有的輸入?yún)?shù)放到 HashMap 中宜猜,將 HashMap 傳遞給映射語(yǔ)句姨拥。
MyBatis 還提供了另外一種傳遞多個(gè)輸入?yún)?shù)給映射語(yǔ)句的方法渠鸽。假設(shè)我們想通過(guò)給定的name和email信息查找 學(xué)生信息徽缚,定義查詢接口如下:
Public interface StudentMapper
{
List<Student> findAllStudentsByNameEmail(String name, String email);
}
MyBatis 支持 將多個(gè)輸入?yún)?shù)傳遞給映射語(yǔ)句凿试,并以#{param}的語(yǔ)法形式引用它們:
<select id="findAllStudentsByNameEmail" resultMap="StudentResult">
select stud_id, name,email, phone from Students
where name=#{param1} and email=#{param2}
</select>
這里#{param1}引用第一個(gè)參數(shù) name屠阻,而#{param2}引用了第二個(gè)參數(shù) email红省。
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.
class);
studentMapper.findAllStudentsByNameEmail(name, email);
3.7.2 多行結(jié)果集映射成 Map
如果你有一個(gè)映射語(yǔ)句返回多行記錄国觉,并且你想以 HashMap 的形式存儲(chǔ)記錄的值麻诀,使用記錄列名作為 key 值蝇闭,而記 錄對(duì)應(yīng)值或?yàn)?value 值。我們可以使用 sqlSession.selectMap(),如下所示:
<select id=" findAllStudents" resultMap="StudentResult">
select * from Students
</select>
Map<Integer, Student> studentMap =
sqlSession.selectMap("com.mrq.mappers.StudentMapper.findAllStudents", "studId");
這里 studentMap 將會(huì)將 studId 作為 key 值礼仗,而 Student 對(duì)象作為 value 值元践。
3.7.4 使用 RowBounds 對(duì)結(jié)果集進(jìn)行分頁(yè)
有時(shí)候童谒,我們會(huì)需要跟海量的數(shù)據(jù)打交道饥伊,比如一個(gè)有數(shù)百萬(wàn)條數(shù)據(jù)級(jí)別的表琅豆。由于計(jì)算機(jī)內(nèi)存的現(xiàn)實(shí)我們不可能一 次性加載這么多數(shù)據(jù),我們可以獲取到數(shù)據(jù)的一部分粒氧。特別是在 Web 應(yīng)用程序中外盯,分頁(yè)機(jī)制被用來(lái)以一頁(yè)一頁(yè)的形式展 示海量的數(shù)據(jù)翼雀。
MyBatis 可以使用 RowBounds 逐頁(yè)加載表數(shù)據(jù)狼渊。RowBounds 對(duì)象可以使用 offset 和 limit 參數(shù)來(lái)構(gòu)建狈邑。參數(shù) offset 表示開(kāi)始位置,而 limit 表示要取的記錄的數(shù)目糕伐。
假設(shè)如果你想每頁(yè)加載并顯示 25 條學(xué)生的記錄良瞧,你可以使用如下的代碼:
<select id="findAllStudents" resultMap="StudentResult">
select * from Students
</select>
然后训唱,你可以加載如下加載第一頁(yè)數(shù)據(jù)(前 25 條):
int offset =0 , limit =25;
RowBounds rowBounds = new RowBounds(offset, limit);
List<Student> = studentMapper.getStudents(rowBounds);
若要展示第二頁(yè)况增,使用 offset=25,limit=25;第三頁(yè)澳骤,則為 offset=50,limit=25誊锭。
3.7.5 使用 ResultSetHandler 自定義結(jié)果集 ResultSet 處理
MyBatis 在將查詢結(jié)果集映射到 JavaBean 方面提供了很大的選擇性丧靡。但是温治,有時(shí)候我們會(huì)遇到由于特定的目的戒悠,需 要我們自己處理 SQL 查詢結(jié)果的情況绸狐。MyBatis 提供了 ResultHandler 插件形式允許我們以任何自己喜歡的方式處理 結(jié)果集 ResultSet。
假設(shè)我們想從學(xué)生的 stud_id 被用作 key突琳,而 name 被用作 value 的 HashMap 中獲取到 student 信息拆融。
對(duì)于 sqlSession.select()方法镜豹,我們可以傳遞給它一個(gè) ResultHandler 的實(shí)現(xiàn),它會(huì)被調(diào)用來(lái)處理 ResultSet 的每一條記錄泰讽。
看一下怎么使用 ResultHandler 來(lái)處理結(jié)果集 ResultSet菇绵,并返回自定義化的結(jié)果咬最。
public Map<Integer, String> getTutorIdNameMap() {
final Map<Integer, String> map = new HashMap<>();
SqlSession sqlSession = MyBatisSqlSessionFactory.openSession();
try {
sqlSession.select("com.mrq.mappers.TutorMapper.findAllTutors",
new ResultHandler<Tutor>() {
@Override
public void handleResult(ResultContext<? extends Tutor> context) {
// TODO Auto-generated method stub
Tutor tutor = context.getResultObject();
map.put(tutor.getTutorId(),tutor.getName());
}
});
} finally {
//sqlSession.close();
MyBatisSqlSessionFactory.closeSqlSession();
}
return map;
}
在上述的代碼中永乌,我們提供了匿名內(nèi)部 ResultHandler 實(shí)現(xiàn)類具伍,在 handleResult()方法中人芽,我們使用 context.getResultObject()獲取當(dāng)前的 result 對(duì)象萤厅,即 Tutor對(duì)象,因?yàn)槲覀兌x了 findAllTutors 映射語(yǔ) 句的 resultMap=”tutorResult“楼誓。對(duì)查詢返回的每一行都會(huì)調(diào)用 handleResult()方法疟羹,并且我們從 findAllTutors 對(duì)象 中取出 tutorId 和 name榄融,將其放到 map 中。
3.7.6 緩存
將從數(shù)據(jù)庫(kù)中加載的數(shù)據(jù)緩存到內(nèi)存中黄刚,是很多應(yīng)用程序?yàn)榱颂岣咝阅芏扇〉囊回炞龇āyBatis 對(duì)通過(guò)映射的 SELECT 語(yǔ)句加載的查詢結(jié)果提供了內(nèi)建的緩存支持畏邢。默認(rèn)情況下检吆,啟用一級(jí)緩存;即蹭沛,如果你使用同一個(gè) SqlSession 接口對(duì)象調(diào)用了相同的 SELECT 語(yǔ)句摊灭,則直接會(huì)從緩存中返回結(jié)果帚呼,而不是再查詢一次數(shù)據(jù)庫(kù)。
我們可以在 SQL 映射器 XML 配置文件中使用cache 元素添加全局二級(jí)緩存眷蜈。 當(dāng)你加入了cache 元素酌儒,將會(huì)出現(xiàn)以下情況:
- 所有的在映射語(yǔ)句文件定義的select語(yǔ)句的查詢結(jié)果都會(huì)被緩存
- 所有的在映射語(yǔ)句文件定義的insert,update 和delete語(yǔ)句將會(huì)刷新緩存
- 緩存根據(jù)最近最少被使用(Least Recently Used忌怎,LRU)算法管理
- 緩存不會(huì)被任何形式的基于時(shí)間表的刷新(沒(méi)有刷新時(shí)間間隔)柔袁,即不支持定時(shí)刷新機(jī)制
- 緩存將存儲(chǔ)1024個(gè) 查詢方法返回的列表或者對(duì)象的引用
- 緩存會(huì)被當(dāng)作一個(gè)讀/寫緩存捶索。這是指檢索出的對(duì)象不會(huì)被共享,并且可以被調(diào)用者安全地修改酝润,不會(huì)其他潛 在的調(diào)用者或者線程的潛在修改干擾璃弄。(即夏块,緩存是線程安全的)
可以通過(guò)復(fù)寫默認(rèn)屬性來(lái)自定義緩存的行為脐供,如下所示:
<cache eviction="FIFO" flushInterval="60000" size="512"
readOnly="true"/>
以下是對(duì)上述屬性的描述:
- eviction:此處定義緩存的移除機(jī)制政己。默認(rèn)值是 LRU,其可能的值有:LRU(least recently used,最近最少使用),FIFO(first in first out,先進(jìn)先出)卵牍,SOFT(soft reference,軟引用)糊昙,WEAK(weakreference,弱引用)溅蛉。
- flushInterval:定義緩存刷新間隔他宛,以毫秒計(jì)厅各。默認(rèn)情況下不設(shè)置队塘。所以不使用刷新間隔憔古,緩存 cache 只有調(diào)用語(yǔ)句的時(shí)候刷新鸿市。
- size:此表示緩存 cache 中能容納的最大元素?cái)?shù)即碗。默認(rèn)值是 1024剥懒,你可以設(shè)置成任意的正整數(shù)初橘。
- readOnly:一個(gè)只讀的緩存 cache 會(huì)對(duì)所有的調(diào)用者返回被緩存對(duì)象的同一個(gè)實(shí)例(實(shí)際返回的是被返回對(duì)象的一份引用)保檐。一個(gè)讀/寫緩存 cache 將會(huì)返回被返回對(duì)象的一分拷貝(通過(guò)序列化)崔梗。默認(rèn)情況下設(shè)置為 false炒俱∪ㄎ颍可能的值有 false 和 true峦阁。
一個(gè)緩存的配置和緩存實(shí)例被綁定到映射器配置文件所在的名空間(namespace)上榔昔,所以在相同名空間內(nèi)的所有語(yǔ)
句被綁定到一個(gè) cache 中瘪菌。
認(rèn)的映射語(yǔ)句的 cache 配置如下:
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>
可以為任意特定的映射語(yǔ)句復(fù)寫默認(rèn)的 cache 行為;例如师妙,對(duì)一個(gè) select 語(yǔ)句不使用緩存默穴,可以設(shè)置 useCache=“false”蓄诽。
除了內(nèi)建的緩存支持仑氛,MyBatis 也提供了與第三方緩存類庫(kù)如 Ehcache,OSCache自阱,Hazelcast 的集成支持米酬。你可 以在MyBatis官方網(wǎng)站https://code.google.com/p/mybatis/wiki/Caches 上找到關(guān)于繼承第三方緩存類庫(kù)的更 多信息赃额。
四. mybatis與spring集成
MyBatis-Spring 是 MyBatis 框架的子模塊跳芳,用來(lái)提供與當(dāng)前流行的依賴注入框架 Spring 的無(wú)縫集成飞盆。
Spring 框架是一個(gè)基于依賴注入(Dependency Injection)和面向切面編程(Aspect Oriented Programming,AOP)的 Java 框架吓歇,鼓勵(lì)使用基于 POJO 的編程模型城看。另外测柠,Spring 提供了聲明式和編程式的事務(wù)管理 能力,可以很大程度上簡(jiǎn)化應(yīng)用程序的數(shù)據(jù)訪問(wèn)層(data access layer)的實(shí)現(xiàn)谒主。在章節(jié)中,我們將看到在基于 Spring 的應(yīng)用程序中使用 MyBatis 并且使用 Spring 的基于注解的事務(wù)管理機(jī)制赃阀。
4.1 在spring應(yīng)用程序中配置mybatis
4.1.1 安裝
使用 Maven 構(gòu)建工具瘩将,你可以配置 MyBatis 的 spring 依賴如下:
pom.xml:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!--mybatis-spring適配器 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!--Spring框架核心庫(kù) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- aspectJ AOP 織入器 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!--Spring java數(shù)據(jù)庫(kù)訪問(wèn)包,在本例中主要用于提供數(shù)據(jù)源 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
4.1.2 配置 MyBatis Beans
spring-mvc.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config></context:annotation-config>
<context:property-placeholder location="classpath:application.properties" />
<context:component-scan base-package="com.mrq"></context:component-scan>
<!-- ========================================配置數(shù)據(jù)源========================================= -->
<!--定義一個(gè)jdbc數(shù)據(jù)源凹耙,創(chuàng)建一個(gè)驅(qū)動(dòng)管理數(shù)據(jù)源的bean -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="acquireIncrement" value="5"></property>
<property name="initialPoolSize" value="10"></property>
<property name="minPoolSize" value="5"></property>
<property name="maxPoolSize" value="20"></property>
</bean>
<!-- ========================================分隔線========================================= -->
<!-- ========================================針對(duì)myBatis的配置項(xiàng)============================== -->
<!-- 配置sqlSessionFactory -->
<!--創(chuàng)建一個(gè)sql會(huì)話工廠bean,指定數(shù)據(jù)源 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 實(shí)例化sqlSessionFactory時(shí)需要使用上述配置好的數(shù)據(jù)源以及SQL映射文件 -->
<property name="dataSource" ref="dataSource" />
<!-- 自動(dòng)掃描com.mrq.mappers/目錄下的所有SQL映射的xml文件, 省掉Configuration.xml里的手工配置
value="classpath:com/mrq/mappers/*.xml"指的是classpath(類路徑)下com.mrq.mappers包中的所有xml文件
TutorMapper.xml位于com.mrq.mappers包下肖抱,這樣TutorMapper.xml就可以被自動(dòng)掃描
-->
<property name="mapperLocations" value="classpath:com/mrq/mappers/*.xml" />
</bean>
<!-- 配置掃描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 掃描com.mrq.mappers這個(gè)包以及它的子包下的所有映射接口類 -->
<property name="basePackage" value="com.mrq.mappers" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- ========================================分隔線========================================= -->
<!--5 聲明式事務(wù)管理 -->
<!--定義事物管理器备典,由spring管理事務(wù) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--支持注解驅(qū)動(dòng)的事務(wù)管理,指定事務(wù)管理器 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--6 容器自動(dòng)掃描IOC組件 -->
<context:component-scan base-package="com.mrq"></context:component-scan>
<!--7 aspectj支持自動(dòng)代理實(shí)現(xiàn)AOP功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
使用上述的 bean 定義意述,Spring 會(huì)使用如下配置屬性創(chuàng)建一個(gè) SqlSessionFactory 對(duì)象:
- dataSource:它引用了 dataSource bean
- typeAliases:它指定了一系列的完全限定名的類名列表提佣,用逗號(hào)隔開(kāi)吮蛹,這些別名將通過(guò)默認(rèn)的別名規(guī)則創(chuàng)建(將首字母小寫的非無(wú)完全限定類名作為別名)。
- typeAliasesPackage:它指定了一系列包名列表拌屏,用逗號(hào)隔開(kāi)潮针,包內(nèi)含有需要?jiǎng)?chuàng)建別名的JavaBeans。
- typeHandlers:它指定了一系列的類型處理器類的完全限定名的類名列表倚喂,用逗號(hào)隔開(kāi)每篷。
- typeHandlersPackage: 它指定了一系列包名列表,用逗號(hào)隔開(kāi)端圈,包內(nèi)含有需要被注冊(cè)的類型處理器類焦读。
- mapperLocations:它指定了 SQL 映射器 Mapper XML 配置文件的位置
- configLocation:它指定了 MyBatisSqlSessionFactory 配置文件所在的位置。
- MapperScannerConfigurer 來(lái)掃描包 (package)中的映射器 Mapper 接口舱权,并自動(dòng)地注冊(cè)矗晃。
- 使用 Spring 的基于注解的事務(wù)處理機(jī)制來(lái)避免書寫上述的每個(gè)方法中控制事務(wù)的冗余代碼。為了能使用 Spring 的事務(wù)管理功能宴倍,我們需要在 Spring 應(yīng)用上下文中配置 TransactionManager bean 實(shí)體對(duì) 象:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
在 Spring 中使用基于注解的事務(wù)管理特性张症,如下:
<tx:annotation-driven transaction-manager="transactionManager"/>
現(xiàn)在你可以在 Spring service bean 上使用@Transactional 注解,表示在此 service 中的每一個(gè)方法都應(yīng)該在 一個(gè)事務(wù)中運(yùn)行鸵贬。如果方法成功運(yùn)行完畢吠冤,Spring 會(huì)提交操作。如果有運(yùn)行期異常發(fā)生恭理,則會(huì)執(zhí)行回滾操作。另外郭变, Spring 會(huì)將 MyBatis 的異常轉(zhuǎn)換成合適的 DataAccessExceptions颜价,這樣會(huì)為特定錯(cuò)誤上提供額外的信息。
package com.mrq.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mrq.dao.TutorDao;
import com.mrq.domain.Tutor;
@Service
public class TutorService {
private TutorDao tutorDao;
@Autowired
public void setTutorDao(TutorDao tutorDao) {
this.tutorDao = tutorDao;
}
public Tutor findTutorById(int tutorId) {
return tutorDao.findTutorById(1);
}
@Transactional
public int insertTutor(Tutor tutor) {
return tutorDao.insertTutor(tutor);
}
}
寫一個(gè)獨(dú)立的測(cè)試客戶端來(lái)測(cè)試 TutorService诉濒,如下:
package com.mrq.main;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mrq.domain.Tutor;
import com.mrq.service.TutorService;
public class TestTutorService {
static TutorService tutorService;
@BeforeClass
public static void before() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring-mvc.xml");
tutorService = (TutorService) ctx.getBean("tutorService");
}
@Test
public void testFindTutor() {
Tutor tutor = tutorService.findTutorById(1);
System.out.println(tutor+"---====");
}
@Test
public void insertTutor() {
Tutor tutor = new Tutor();
tutor.setEmail("Piter@163.com");
tutor.setName("Piter");
int count = tutorService.insertTutor(tutor);
System.out.println("insert-> "+count);
}
}
domain:
public class Tutor {
private Integer tutorId;
private String name;
private String email;
private String phone;
//setter and getter
}
dao:
package com.mrq.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.mrq.domain.Tutor;
import com.mrq.mappers.TutorMapper;
@Repository
public class TutorDao {
private TutorMapper tutorMapper;
@Autowired
public void setTutorMapper(TutorMapper tutorMapper) {
this.tutorMapper = tutorMapper;
}
public Tutor findTutorById(int tutorId) {
return tutorMapper.findTutorById(1);
}
public int insertTutor(Tutor tutor) {
return tutorMapper.insertTutor(tutor);
}
}
service:
package com.mrq.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mrq.dao.TutorDao;
import com.mrq.domain.Tutor;
@Service
public class TutorService {
private TutorDao tutorDao;
@Autowired
public void setTutorDao(TutorDao tutorDao) {
this.tutorDao = tutorDao;
}
public Tutor findTutorById(int tutorId) {
return tutorDao.findTutorById(1);
}
@Transactional
public int insertTutor(Tutor tutor) {
return tutorDao.insertTutor(tutor);
}
}
mapper接口
package com.mrq.mappers;
import com.mrq.domain.Tutor;
public interface TutorMapper {
Tutor findTutorById(int tutorId);
int insertTutor(Tutor tutor);
}
映射文件TutorMapper.xml:
<?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="com.mrq.mappers.TutorMapper">
<resultMap type="com.mrq.domain.Tutor" id="TutorResult">
<id property="tutorId" column="tutor_id" />
<result property="name" column="name" />
<result property="email" column="email" />
</resultMap>
<select id="findTutorById" parameterType="int" resultMap="TutorResult">
select T.tutor_id,T.name,email
from tutors T where T.tutor_id=#{tutorId}
</select>
<insert id="insertTutor" parameterType="com.mrq.domain.Tutor">
insert into tutors(name,email) values(#{name},#{email})
</insert>
</mapper>
至此,一個(gè)簡(jiǎn)單的spring和mybatis集成的小demo完成了.