MyBatis單表操作
前言
在前面一小節(jié)中,介紹了MyBatis以及MyBatis的簡單操作,并且簡單地分析了MyBatis的工作機制,接下來這一小節(jié),我們來學習MyBatis的單表操作,包括簡單的增刪改查
MyBatis單表操作
在本小節(jié)中,依然沿用上一小節(jié)所使用的環(huán)境以及資源
增加數(shù)據(jù)
在CountryMapper.xml中加入下面插入代碼
<insert id="insertCountry">
insert into
country(country_name, country_code)
values (#{countryName}, #{countryCode})
</insert>
在CountryMapper.java中加入代碼
int insertCountry(Country country);
測試代碼如下
@Test
public void testInsertCountry() {
SqlSession sqlSession = sqlSessionFactory.openSession();
CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);
Country country = new Country();
country.setCountryCode("SGP");
country.setCountryName("新加坡");
int result = countryMapper.insertCountry(country);
// 默認情況是不自動提交事務的,需要手動提交或者在配置文件中設(shè)置自動提交
sqlSession.commit();
Assert.assertEquals(1, result);
}
上面的操作基本上沒有問題,但是如果我們想插入數(shù)據(jù)的時候順便回到自增的主鍵,那還需要多做一點修改
CountryMapper.xml
<insert id="insertCountry" useGeneratedKeys="true" keyProperty="id">...</insert>
userGeneratedKyes="true"表示使用數(shù)據(jù)庫的自增主鍵,需要注意的是,只能使用一部分支持自增主鍵的數(shù)據(jù)庫如MySQL,keyProperty=""指定對應的屬性
如果是在不支持自增主鍵的數(shù)據(jù)庫,如Oracle,則需要采用下面的方式
<selectKey keyColumn = "主鍵列" resultType ="主鍵類型" keyProperty="id" order="BEFORE/AFTER">
SELECT SQL LIKE:
MySQL: select LAST_INSERT_ID()
Oracle: select SEQ_ID.nextval from dual
</selectKey>
在selectKey中,需要根據(jù)具體的數(shù)據(jù)庫來選擇對應的獲取主鍵的操作
order="before/after"需要根據(jù)具體的數(shù)據(jù)庫來選擇,有的是先生成主鍵再插入,有的是插入后再生成主鍵
上面的兩種情況中,如果主鍵列是由多個字段組成,則多個字段之間使用,分隔即可
在上面的插入情況中,我們是使用對象插入的,或者說,是使用一個參數(shù)插入的,在MyBatis中,如果是單個參數(shù),可以不同做其他操作,如果是多個參數(shù),為了在SQL中獲取參數(shù),還需要額外的一些操作,如果簡單的按照上面的操作,是無法成功的,如下
<insert id="insertCountryByNameAndCode">
insert into country(country_name, country_code)
values (#{countryName}, #{countryCode})
</insert>
int insertCountryByNameAndCode(String countryName, String countryCode);
執(zhí)行結(jié)果
org.apache.ibatis.exceptions.PersistenceException:
# ....
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'countryName' not found. Available parameters are [arg1, arg0, param1, param2]
可以看到,MyBatis提示我們使用arg0, arg1或者param1, param2這種方式,但實際上這種方式不太好,因為在SQL中無法看出插入的數(shù)據(jù)是哪個。有兩種解決方案,一種是將參數(shù)封裝到一個map中,key就是參數(shù)的名稱,value是參數(shù)的值,但這種方式比較麻煩,而且還需要手動構(gòu)造一個map對象,另一種方案是采用下面的方式
int insertCountryByNameAndCode(@Param("countryName") String countryName, @Param("countryCode") String countryCode);
也就是使用@Param("NAME")來指定參數(shù)的名稱,這種方式是比較推薦的,當然,這種方式本質(zhì)也是構(gòu)造一個map,只是由MyBatis來構(gòu)造,不用我們自己動手。
刪除數(shù)據(jù)
刪除數(shù)據(jù)的情況跟插入數(shù)據(jù)是類似的,只需要在CountryMapper.xml中加入對應的SQL,然后在CountryMapper.java中加入對應的操作接口即可
<delete id="deleteCountryByCountryCode">
delete from country
where country_code = #{countryCode}
</delete>
int deleteCountryByCountryCode(String countryCode);
測試的操作同上,這里就不展開了
修改數(shù)據(jù)
修改數(shù)據(jù)的操作也基本同上,可以通過構(gòu)造對象作為參數(shù),也可以傳入多個參數(shù),傳入多個參數(shù)則使用@Param("")指定參數(shù)名稱即可
<update id="updateCountryNameByCountryCode">
update country
set country_name = #{countryName}
where country_code = #{countryCode}
</update>
int updateCountryNameByCountryCode(Country country);
測試的操作基本同上,這里就不展開了
查詢數(shù)據(jù)
查詢數(shù)據(jù)是增刪改查這幾個操作中使用頻率最高的,也是操作方式中比較豐富的一個操作,同時也是MyBatis方便性最能體現(xiàn)的一個部分,在查詢操作中,通常數(shù)據(jù)庫返回給我們的是一行行的數(shù)據(jù),在JDBC操作中,我們需要手動將一行行的數(shù)據(jù)封裝到對象中,而在MyBatis中,這些操作通常只需要指定一個resultType或者一個resultMap,MyBatis就會自動將其封裝到對象中,這在復雜操作中比較好用。
<select id="selectCountryByCountryCode">
select id,
country_name,
country_code
from country
where country_code = #{country_code}
</select>
Country selectCountryByCountryCode(String countryCode);
當測試上面的操作時,會出現(xiàn)拋出下面的異常
Caused by: org.apache.ibatis.executor.ExecutorException:
A query was run and no Result Maps were found for the Mapped Statement 'mapper.CountryMapper.selectCountryByCountryCode'.
It's likely that neither a Result Type nor a Result Map was specified.
由于MyBatis從數(shù)據(jù)庫中獲取的是一行行的數(shù)據(jù),它并不知道要怎么封裝這些數(shù)據(jù),所以我們需要告訴它怎么封裝這些數(shù)據(jù),方法有兩個,一個是指定resultType,這通常適用于比較簡單的情況,另一個是resultMap,適用于比較復雜的情況
所以只需要將上面的xml修改成下面的xml即可
<select id="selectCountryByCountryCode" resultType="domain.Country">
select id,
country_name,
country_code
from country
where country_code = #{country_code}
</select>
如果有在前面指定typeAlias,則可以直接使用類名,否則就需要使用類的全限定名
當然,上面修改后不會拋出異常,但是查詢出來的對象字段除了id之外,其他的全是null,Country{id=2, countryName='null', countryCode='null'},原因很簡單啦,就是字段沒對應上,country_name和countryName之間對不上,解決方法有兩種,一種是開啟MyBatis的下劃線轉(zhuǎn)駝峰功能
config.xml
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
這種方式適用于數(shù)據(jù)庫表字段和對象字段之間均能對應上的情況,如果對應不上,則可以使用下面的方式,通過SQL的別名方式
<select id="selectCountryByCountryCode" resultType="domain.Country">
select id,
country_name as CountryName,
country_code as CountryCode
from country
where country_code = #{country_code}
</select>
通過上面的兩種方式之一修改之后就沒問題啦
上面我們提到,可以通過resultType來封裝結(jié)果集,也可以通過resultMap來封裝,下面介紹下resultMap的使用
<select id="selectCountryByCountryCode" resultMap="countryMap">
select id, country_name, country_code
from country
where country_code = #{country_code}
</select>
<!--定義resultMap-->
<resultMap id="countryMap" type="domain.Country">
<!--封裝屬性-->
<id property="id" column="id" />
<result property="countryName" column="country_name"/>
<result property="countryCode" column="country_code"/>
</resultMap>
在resultMap中有幾個需要注意的地方
- 盡量指定
<id>,由于是將一行行的數(shù)據(jù)對應到對象中,那么就可能出現(xiàn)多行數(shù)據(jù)相同的情況,而這個時候MyBatis會將其對應到同一個對象中,如果指定了id,那么直接根據(jù)id字段的值來比較,如果沒有,則根據(jù)所有的字段來比較,比較一個字段總是比比較多個字段高效的嘛 - 在
resultMap的字標簽中,均可以使用javaType=""以及jdbcType=""這兩個類型,當對象的字段跟數(shù)據(jù)庫中字段出現(xiàn)差異的時候,就需要顯式指定了,比如Date對象對應的應該是TIMESTAMP而不是數(shù)據(jù)庫中的Date,這個時候就需要指定jdbcType="TIMESTAMP"了 - resultMap中還可以嵌套
collection、association等字標簽,下一節(jié)將詳細介紹這一部分內(nèi)容
總結(jié)
在本小節(jié)中,我們主要學習了MyBatis的單表操作:增刪改查,其中需要注意的是如果傳入的時候有多個參數(shù),則推薦使用@Param("")指定參數(shù)名稱,如果需要獲取插入的數(shù)據(jù)的主鍵,可以根據(jù)數(shù)據(jù)庫來使用userGeneratedKey=""或者<selectKey>來獲取,在封裝數(shù)據(jù)集到對象的時候,可以使用resultType或者resultMap,而且必須指定一種的一種,也只能使用一種,resultType適用于比較簡單的情況,resultMap使用與比較復雜的情況,關(guān)于resultMap的更多用法我們將在下節(jié)進行詳細學習。