MyBatis中Mapper的返回值類型


insert、update、delete語句的返回值類型

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

在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);
    }

 


免責聲明!

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



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