Mybatis 的強大之處之一體現(xiàn)在映射語句上,讓我們可以使用簡單的配置,就可以實現(xiàn)對參數(shù)和返回結果的映射。
實體
1
2
3
4
5
6
7
8
9
10
|
package com.test.User public class User{ private String userId; private String userName; private String userPassword; private Date createTime; ... setter getter.... } |
DAO
1
2
3
4
5
6
|
public interface UserMapper{ User getUserById(String userId); //返回單個實體 List<User> getUserByName(String userName); //返回List Map<String,Object> getUserInfoById(String userId); List<Map<String,Object>> getUserInfoByName(String userName); } |
數(shù)據庫
1
2
3
4
5
6
7
|
create table user { USER_ID varchar (40), USER_NAME varchar (200), USER_PASSWORD varchar (100), CREATE_TIME datetime, .... } |
1.返回某個實體
mybatis映射文件
1
2
3
|
< select id = "getUserById" parameterType = "string" resultType = "com.test.User" > select * from user where id = #{userId} </ select > |
- id :identification:語句的標識,在同一個mapper映射文件下id需要唯一
- parameterType: 參數(shù)類型,可以不寫。因為 MyBatis 可以推斷出傳入語句的具體參數(shù)
- resultType: 全限定類名或者是類型別名.
當使用resultType來映射結果時,需要 數(shù)據庫表的列名或列別名 和 類的屬性名相同,這樣才能進行字段的匹配(USER_ID 和userId 就不能匹配)。但是如果在Mybatis配置文件中設置了
1
2
3
|
< settings > < setting name = "mapUnderscoreToCamelCase" value = "true" /> <!--開啟自動駝峰命名規(guī)則(camel case)映射,即從經典數(shù)據庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似映射。--> </ settings > |
此時,表列名的下劃線標記方式可以映射到駝峰標記的形式。(USER_ID -> userId)。mybatis進行映射時會將實體類屬性和數(shù)據庫列名(別名)都轉化為大寫來比較,所以USER_ID 和 UserId,userID等都可以匹配。
TooManyResultsException
返回單個實體時,調用方法 getUserById
,但是如果是因為數(shù)據錯誤導致實際查詢結果存在多個時,則會拋出異常。
1
|
User getUserById(String userId); //返回單個實體 |
當實際返回值有多個時則拋出異常。
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
除非可以確定最多只能查詢到一條結果,否則的話不建議這么寫.可以嘗試返回集合的方式。
2.返回List<entityName>
1
2
3
|
< select id = "getUserByName" resultType = "com.test.User" > select * from user where user_name = #{userName} </ select > |
返回List<T> 集合時,resultType設置為集合元素的類型即T。然后使用返回幾何數(shù)據的Mapper方法即可。
1
|
List<User> getUserByName(String userName); //返回List |
從上面可以看到,返回單個實體與返回集合的resultType指定類型是一樣的,不一樣的地方在Mapper接口或者sqlSession中定義的返回結果類型。實際上mybatis執(zhí)行查詢的時候也都是使用sqlSession.selectList()來進行查詢的。
- 使用Mapper 接口的方式的查詢結果時,Mybatis會生成該接口的代理類(MapperProxy),然后根據Method的getReturnType()方法,拿到返回類型,來確定返回的是列表還是單個實體。最后也是調用sqlSession的一些方法。
- 使用SqlSession時,提供了selectOne() 或者selectList()來返回單個實體或者集合。selectOne 實際會調用selectList獲取結果。
推薦使用返回List的方式來查詢結果
1
2
3
4
5
6
7
|
//查詢單條結果 List<User> userList= mapper.getUserByName(userName); if (userList.isEmpty() || userList.size() > 1 ) //期望獲得一條結果 //業(yè)務處理,一般是拋出異常或者直接返回錯誤結果 //return xx; //throw xxx User user = userList.get( 0 ); |
擴展
為什么查詢單條和查詢多條使用的是相同的resultType
,而返回的結果不同呢。
這是因為Mybatis 在內部進行數(shù)據查詢的時,無論查詢單條還是多條都是通過selectList
實現(xiàn)的,不同的是查詢單條Mybatis會獲取第一條,并且如果結果中存在多條時拋出異常 TooManyResultsException
。
查詢單數(shù)據
1
2
3
4
5
6
7
8
9
10
11
12
|
@Override public <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this .<T>selectList(statement, parameter); if (list.size() == 1 ) { return list.get( 0 ); } else if (list.size() > 1 ) { throw new TooManyResultsException( "Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null ; } } |
查詢列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else { result = sqlSession.<E>selectList(command.getName(), param); } // issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; } |
那么Mybatis怎么知道是查詢的單條數(shù)據還是列表呢?
-
如果直接使用 SqlSession,這個需要自己控制 是調用
selectOne
還是selectList
- 如果使用 Mapper 接口,Mybatis會解析Mapper接口中的方法,會根據方法的返回值,判斷該方法屬于那種類型
解析方法中的參數(shù)、返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) { Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); if (resolvedReturnType instanceof Class<?>) { this .returnType = (Class<?>) resolvedReturnType; } else if (resolvedReturnType instanceof ParameterizedType) { this .returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType(); } else { this .returnType = method.getReturnType(); } this .returnsVoid = void . class .equals( this .returnType); this .returnsMany = (configuration.getObjectFactory().isCollection( this .returnType) || this .returnType.isArray()); this .returnsCursor = Cursor. class .equals( this .returnType); this .mapKey = getMapKey(method); this .returnsMap = ( this .mapKey != null ); this .rowBoundsIndex = getUniqueParamIndex(method, RowBounds. class ); this .resultHandlerIndex = getUniqueParamIndex(method, ResultHandler. class ); this .paramNameResolver = new ParamNameResolver(configuration, method); } |
method
對象就是 Mapper中的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// select 查詢操作 case SELECT: // 方法中沒有定義返回結果,并且方法存在結果處理器 if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null ; } else if (method.returnsMany()) { // 返回列表 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { // 返回Map result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { // 返回游標 result = executeForCursor(sqlSession, args); } else { // 返回單條數(shù)據 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } break ; |
3.返回Map
返回map 本質上也是返回一個實體。
1
2
3
|
< select id = "getUserInfoById" resultType = "map" > select * from user where id=#{userId} </ select > |
如果想要返回單個Map<key,value>集合,只需要設置resultType="map"
就可以了,此時返回的實例類型是HashMap。
Map的key就是數(shù)據表的列名
或者列別名
, value 就是查詢的數(shù)據庫中的結果。如果需要返回LinkedHashMap,需要使用全限定類名resultType="java.util.LinkedHashMap"
。
注意: 返回列對應的結果為 null
,則不顯示該 key - value 鍵值對(只針對該行數(shù)據對應的Map)
1
2
3
|
< select id = "getUserInfoById" resultType = "map" > select * from user where id=#{userId} </ select > |
在Oracle
環(huán)境下:
最終返回Map:
1
2
|
{ "name " : "全部" } // code 不是顯示 |
如果使用map接受則不會該map不存在數(shù)據. 因為Mybatis默認情況下,不會進行賦值,此時該key-value
缺失
如果需要改變該行為可以在mybatis配置文件中設置
1
|
< setting name = "callSettersOnNulls" value = "true" /> |
callSettersOnNulls指定當返回結果為null 的時候是否調用映射對象的 setter(map 對象時為 put)方法,注意基本類型(int、boolean等)是不能設置成 null 的。
配置之后的返回結果:
1
|
{ "code" : null , "name" : "全部" } |
4.返回List<Map>
1
2
3
|
< select id = "" parameterType = "" resultType = "map" > sql_caluse </ select > |
resultType設置為map,跟上面一樣resultType設置為List集合中元素的類型。
關于mybatis傳遞多個參數(shù),可以參考mybatis3-傳遞多參數(shù)
注意:
偶然發(fā)現(xiàn)Mybatis 會自動對重名的列做去重。
比如我有一組數(shù)據,使用Map接受
1
2
|
SELECT l1.*,l2.*,l3.* FROM ITEM_CAT l1 LEFT JOIN ITEM_CAT l2 ON l1.Id=L2.PARENT_ID LEFT JOIN ITEM_CAT l3 ON l2.Id=L3.PARENT_ID WHERE L1.PARENT_ID= '0' ; |
實際返回結果,會發(fā)現(xiàn) name1,name2 都沒有映射到Map中
1
2
3
4
5
6
7
|
[ { "parent_id" :0, "name" : "圖書" , "id" :1}, { "parent_id" :0, "name" : "圖書" , "id" :1}, { "parent_id" :0, "name" : "圖書" , "id" :1}, { "parent_id" :0, "name" : "圖書" , "id" :1} ...... ] |
稍微修改一下字段名稱。
1
2
|
SELECT l1.*,l2.*,l3.*, 'test' as name1 FROM ITEM_CAT l1 LEFT JOIN ITEM_CAT l2 ON l1.Id=L2.PARENT_ID LEFT JOIN ITEM_CAT l3 ON l2.Id=L3.PARENT_ID WHERE L1.PARENT_ID= '0' ; |
1
2
3
4
5
6
7
|
[ { "parent_id" :0, "name" : "圖書" , "id" :1, "name1" : "test" }, { "parent_id" :0, "name" : "圖書" , "id" :1, "name1" : "test" }, { "parent_id" :0, "name" : "圖書" , "id" :1, "name1" : "test" }, { "parent_id" :0, "name" : "圖書" , "id" :1, "name1" : "test" } ...... ] |
可以看到 新增的自定義列名 “name1”,可以正常顯示。這是因為使用sql 查詢出的同名的列名自動追加數(shù)字做區(qū)分,而實際保存在 元數(shù)據信息中的列名還是原來的。就如同Excel 的單元格一樣,不管單元格內容以什么樣式顯示都不會修改實際值。
小結:
- 返回集合與返回單個實體對象在映射文件的寫法是一致的,不同的地方在于Mapper的返回類型不同。
- 如果不確定返回值是否是唯一的,盡量使用 集合的返回方式。然乎使用get(0)的方式獲取實體。
- 如果返回實體,一般情況會使用 resultMap來映射返回結果。這樣更清晰,直觀,而且還可以使用typeHandler對數(shù)據類型做進一步處理
返回結果 | Mapper | xml |
---|---|---|
實體 | T getT() | returnType=“T” |
集合 | List<T> getTList() | returnType=“T” |
到此這篇關于Mybatis返回單個實體或者返回List的實現(xiàn)的文章就介紹到這了,更多相關Mybatis返回實體或者返回List內容請搜索服務器之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/yamadeee/article/details/79774638