ResultMap和ResultType在使用中的區別、MyBatis中Mapper的返回值類型


在使用mybatis進行數據庫連接操作時對於SQL語句返回結果的處理通常有兩種方式,一種就是resultType另一種就是resultMap,下面說下我對這兩者的認識和理解

resultType:當使用resultType做SQL語句返回結果類型處理時,對於SQL語句查詢出的字段在相應的pojo中必須有和它相同的字段對應,而resultType中的內容就是pojo在本項目中的位置。

因此對於單表查詢的話用resultType是最合適的。但是,如果在寫pojo時,不想用數據庫表中定義的字段名稱,也是可以使用resultMap進行處理對應的。多表連接查詢時,若是一對一的連接查詢,那么需要新建一個pojo,pojo中包括兩個表中需要查詢出的所有的字段,這個地方的處理方式通常為創建一個繼承一個表字段的pojo,再在里面添加另外一個表內需要查詢出的字段即可。若是一對多查詢時,若是使用內連接查詢,則很可能出現查詢出的字段有重復。使用雙重for循環嵌套處理即可。

 

resultMap:當使用resultMap做SQL語句返回結果類型處理時,通常需要在mapper.xml中定義resultMap進行pojo和相應表字段的對應。

 

[java]  view plain  copy
 
  1. <!-- 訂單查詢關聯用戶的resultMap  
  2.     將整個查詢的結果映射到cn.itcast.mybatis.po.Orders中  
  3.      -->  
  4.     <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserResultMap">  
  5.         <!-- 配置映射的訂單信息 -->  
  6.         <!-- id:指定查詢列中的唯 一標識,訂單信息的中的唯 一標識,如果有多個列組成唯一標識,配置多個id  
  7.             column:訂單信息的唯 一標識 列  
  8.             property:訂單信息的唯 一標識 列所映射到Orders中哪個屬性  
  9.           -->  
  10.         <id column="id" property="id"/>  
  11.         <result column="user_id" property="userId"/>  
  12.         <result column="number" property="number"/>  
  13.         <result column="createtime" property="createtime"/>  
  14.         <result column="note" property="note"/>         
  15.     </resultMap>  

 

 

resultMap對於一對一表連接的處理方式通常為在主表的pojo中添加嵌套另一個表的pojo,然后在mapper.xml中采用association節點元素進行對另一個表的連接處理。例如:

 

[html]  view plain  copy
 
  1. <!-- 訂單查詢關聯用戶的resultMap  
  2.     將整個查詢的結果映射到cn.itcast.mybatis.po.Orders中  
  3.      -->  
  4.     <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserResultMap">  
  5.         <!-- 配置映射的訂單信息 -->  
  6.         <!-- id:指定查詢列中的唯 一標識,訂單信息的中的唯 一標識,如果有多個列組成唯一標識,配置多個id  
  7.             column:訂單信息的唯 一標識 列  
  8.             property:訂單信息的唯 一標識 列所映射到Orders中哪個屬性  
  9.           -->  
  10.         <id column="id" property="id"/>  
  11.         <result column="user_id" property="userId"/>  
  12.         <result column="number" property="number"/>  
  13.         <result column="createtime" property="createtime"/>  
  14.         <result column="note" property=note/>  
  15.           
  16.         <!-- 配置映射的關聯的用戶信息 -->  
  17.         <!-- association:用於映射關聯查詢單個對象的信息  
  18.         property:要將關聯查詢的用戶信息映射到Orders中哪個屬性  
  19.          -->  
  20.         <association property="user"  javaType="cn.itcast.mybatis.po.User">  
  21.             <!-- id:關聯查詢用戶的唯 一標識  
  22.             column:指定唯 一標識用戶信息的列  
  23.             javaType:映射到user的哪個屬性  
  24.              -->  
  25.             <id column="user_id" property="id"/>  
  26.             <result column="username" property="username"/>  
  27.             <result column="sex" property="sex"/>  
  28.             <result column="address" property="address"/>  
  29.           
  30.         </association>  
  31.     </resultMap>  


若是一對多的表連接方式,比如訂單表和訂單明細表即為一對多連接,若是不對sql語句進行處理,由於一個訂單對應多條訂單明細,因此查詢出的結果對於訂單表數據來說將會出現重復,例如:

 

resultMap的處理方式為在訂單表數據的pojo中添加一個list,list中為訂單明細表的屬性,在mapper.xml中采用如下的處理方式:

 

[html]  view plain  copy
 
  1. !-- 訂單及訂單明細的resultMap  
  2.     使用extends繼承,不用在中配置訂單信息和用戶信息的映射  
  3.      -->  
  4.     <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">  
  5.         <!-- 訂單信息 -->  
  6.         <!-- 用戶信息 -->  
  7.         <!-- 使用extends繼承,不用在中配置訂單信息和用戶信息的映射 -->  
  8.           
  9.           
  10.         <!-- 訂單明細信息  
  11.         一個訂單關聯查詢出了多條明細,要使用collection進行映射  
  12.         collection:對關聯查詢到多條記錄映射到集合對象中  
  13.         property:將關聯查詢到多條記錄映射到cn.itcast.mybatis.po.Orders哪個屬性  
  14.         ofType:指定映射到list集合屬性中pojo的類型  
  15.          -->  
  16.          <collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">  
  17.             <!-- id:訂單明細唯 一標識  
  18.             property:要將訂單明細的唯 一標識 映射到cn.itcast.mybatis.po.Orderdetail的哪個屬性  
  19.               -->  
  20.             <id column="orderdetail_id" property="id"/>  
  21.             <result column="items_id" property="itemsId"/>  
  22.             <result column="items_num" property="itemsNum"/>  
  23.             <result column="orders_id" property="ordersId"/>  
  24.          </collection>  
  25.           
  26.       
  27.     </resultMap>  


在查詢時,雖然一條訂單信息對應多條訂單明細,由於將多條信息明細存儲到了list中,因此查詢后將不再出現重復數據,達到了去重的效果

 

 

----------------------------------------------------------------------------------------------------------------

對數據庫執行修改操作時,數據庫會返回受影響的行數。

在MyBatis(使用版本3.4.6,早期版本不支持)中insert、update、delete語句的返回值可以是Integer、Long和Boolean。在定義Mapper接口時直接指定需要的類型即可,無需在對應的<insert><update><delete>標簽中顯示聲明。

對應的代碼在 org.apache.ibatis.binding.MapperMethod 類中,如下:

  • 對於insert、update、delete語句,MyBatis都會使用 rowCountResult 方法對返回值進行轉換;
  • rowCountResult 方法會根據Mapper聲明中方法的返回值類型來對參數進行轉換;
  • 對於返回類型為Boolean的情況,如果返回的值大於0,則返回True,否則返回False
 1 public Object execute(SqlSession sqlSession, Object[] args) {
 2     Object result;
 3     switch (command.getType()) {
 4       case INSERT: {
 5       Object param = method.convertArgsToSqlCommandParam(args);
 6         result = rowCountResult(sqlSession.insert(command.getName(), param));
 7         break;
 8       }
 9       case UPDATE: {
10         Object param = method.convertArgsToSqlCommandParam(args);
11         result = rowCountResult(sqlSession.update(command.getName(), param));
12         break;
13       }
14       case DELETE: {
15         Object param = method.convertArgsToSqlCommandParam(args);
16         result = rowCountResult(sqlSession.delete(command.getName(), param));
17         break;
18       }
19       case SELECT:
20         if (method.returnsVoid() && method.hasResultHandler()) {
21           executeWithResultHandler(sqlSession, args);
22           result = null;
23         } else if (method.returnsMany()) {
24           result = executeForMany(sqlSession, args);
25         } else if (method.returnsMap()) {
26           result = executeForMap(sqlSession, args);
27         } else if (method.returnsCursor()) {
28           result = executeForCursor(sqlSession, args);
29         } else {
30           Object param = method.convertArgsToSqlCommandParam(args);
31           result = sqlSession.selectOne(command.getName(), param);
32         }
33         break;
34       case FLUSH:
35         result = sqlSession.flushStatements();
36         break;
37       default:
38         throw new BindingException("Unknown execution method for: " + command.getName());
39     }
40     if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
41       throw new BindingException("Mapper method '" + command.getName() 
42           + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
43     }
44     return result;
45   }
46 
47   private Object rowCountResult(int rowCount) {
48     final Object result;
49     if (method.returnsVoid()) {
50       result = null;
51     } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
52       result = rowCount;
53     } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
54       result = (long)rowCount;
55     } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
56       result = rowCount > 0;
57     } else {
58       throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
59     }
60     return result;
61   }

select語句的返回值類型

對於select語句,必須在Mapper映射文件中顯示聲明返回值類型,否則會拋出異常,指出“A query was run and no Result Maps were found for the Mapped Statement”。

  • 返回Integer或Long類型
    • 與修改語句不同,select語句必須指定返回值類型,且mapper映射文件中的返回值類型需要與mapper接口中聲明的返回值類型一致,都是int或long;
    • mapper接口中的返回值類型可以是Integer、Long,也可以是int、long
  • 不能返回Boolean類型

   select語句不能返回Boolean類型

  • 返回自定義類型的Bean

  select語句返回的column值與Mapper方法返回值的屬性的映射有兩種方式(具體設置見下文):

    • 通過名稱實現自動映射
    • 通過resultMap標簽指定映射方式
  • 返回List、Set、Array類型

   直接指定select方法的返回值為List<T>、Set<T>、即可,注意此時mapper映射文件中select標簽的 resultType類型一定要是自定義的Bean類型,而不是集合類型,可以理解為resultType指定的類型對應的是數據庫中的一行數據

  • 返回Map類型

   返回Map類型有兩種含義:

    • resultType的類型為map,mapper接口的返回值也為Map,此時返回結果將Bean的屬性作為key,值作為Value返回
    • resultType的類型為非map類型(自定義類型、數值類型、String等),mapper接口的返回值為Map,且指定了mapKey,此時的返回結果以指定的mapKey作為Key,以resultType類型對象作為value

通過column別名實現自定義類型的自動映射

只要保證數據庫查詢返回的column名稱和Bean的屬性名一致,Mybatis便能夠實現自動映射。如:

    <select id="selectActorById"  resultType="canger.study.chapter04.bean.Actor">
        select actor_id as id, first_name as firstName ,last_name as lastName
        from actor
        where actor_id=#{id}
    </select>
public class Actor {
    Long id;
    String firstName;
    String lastName;
}

 需要特別說明的有3個地方:

  1. 返回值Bean無需為屬性設置getter/setter方法,Mybatis依然能夠為其賦值;
  2. 如果column名稱和Bean的屬性名只有部分相同,那么只有名稱相同的屬性會被賦值,Bean依然會被創建;
  3. 如果column名稱與所有Bean的屬性名都不相同,則select語句會返回null值,即使數據庫中存在符合查詢條件的記錄;

設置mapUnderscoreToCamelCase屬性實現自動映射

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

 

  • 如果數據庫采用了嚴格的under score命名規則,則通過設置mapUnderscoreToCamelCase屬性並可實現column到實體類property的自動映射;
  • 實際項目中,可將命名不規范的column通過resultMap進行顯示聲明,符合命名規范的進行自動映射

 通過resultMap標簽指定自定義類型的映射方式

 需要在mapper映射文件中定義 resultMap標簽

 
    <resultMap id="actorResultMap" type="canger.study.chapter04.bean.Actor">
        <id column="id" property="id" jdbcType="BIGINT" javaType="long" typeHandler="org.apache.ibatis.type.LongTypeHandler"/>
        <result column="firstName" property="firstName" jdbcType="VARCHAR" javaType="string" 
                typeHandler="org.apache.ibatis.type.StringTypeHandler"/>
        <result column="lastName" property="lastName" jdbcType="VARCHAR" javaType="string" 
                typeHandler="org.apache.ibatis.type.StringTypeHandler"/>
    </resultMap>
 

 

    <select id="selectActorById"  resultMap="actorResultMap">
        select actor_id as id, first_name as firstName ,last_name as lastName
        from actor
        where actor_id=#{id}
    </select>

 

需要注意的幾點

  • id或result標簽中的column屬性一定要使用SQL select 語句中最終返回到數據庫客戶端的列名,如對於select actor_id as id,在resultMap中需要使用 id而不是 actor_id;
  • 無需將查詢返回的所有column都進行顯示聲明,沒有在resultMap中聲明映射關系的column將進行自動映射
  • typeHandler屬性:如果使用MyBatis提供的typeHandler,則一定要是用全類名,MyBatis默認沒有為typeHandler其注冊別名;
  • jdbcType 、 javaType和typeHandler的對照關系,以及jdbcType和數據庫類型的對照關系參見 Mybatis中jdbcType和javaType、typeHandler的對照關系
  • MyBatis注冊的默認TypeHandler參見類 org.apache.ibatis.type.TypeHandlerRegistry,具體如下
 
public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    // mybatis-typehandlers-jsr310
    if (Jdk.dateAndTimeApiExists) {
      this.register(Instant.class, InstantTypeHandler.class);
      this.register(LocalDateTime.class, LocalDateTimeTypeHandler.class);
      this.register(LocalDate.class, LocalDateTypeHandler.class);
      this.register(LocalTime.class, LocalTimeTypeHandler.class);
      this.register(OffsetDateTime.class, OffsetDateTimeTypeHandler.class);
      this.register(OffsetTime.class, OffsetTimeTypeHandler.class);
      this.register(ZonedDateTime.class, ZonedDateTimeTypeHandler.class);
      this.register(Month.class, MonthTypeHandler.class);
      this.register(Year.class, YearTypeHandler.class);
      this.register(YearMonth.class, YearMonthTypeHandler.class);
      this.register(JapaneseDate.class, JapaneseDateTypeHandler.class);
    }
 

 ------------------------------------------------------------------------------------------------------------------

Mybatis返回值類型為List時,sql語句怎么寫

前言:

因為某個需求需要返回訪問數據庫的返回值是每條記錄的同一列,所以就想到了用集合。List<String>.然后就開始操作。。。

代碼一開始是這樣的

<!--查詢所有老師-->
<select id="findTeacherName" resultMap="java.lang.String" >
select
<include refid="Teacher_Name" />
from teacher
</select>
好像沒什么錯誤,而且項目運行了也沒報錯。重點是項目沒報錯,但接着想登錄的時候就失敗了,雖然登錄功能不在同一個XxxMapper.xml。但似乎訪問數據庫的功能都失敗。

這時候就很糾結了,因為也沒報錯。找了一整天登錄功能出現什么問題,還是沒解決。

后來想了想,把之前代碼刪掉,似乎可以了。於是看之前加的上面的代碼,看了也沒錯啊,返回值是java.lang.String。百度也很多說是這個類型,根據之前學的也是一樣。

終於。。。發現問題所在了

后來變成這樣

<!--查詢所有老師-->
<select id="findTeacherName" resultType="java.lang.String" >
select
<include refid="Teacher_Name" />
from teacher
</select>
紅色的才是重點。。。
---------------------
作者:小神兜兜
來源:CSDN
原文:https://blog.csdn.net/weixin_41746397/article/details/89430162


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM