insert、update、delete语句的返回值类型

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
 public Object execute(SqlSession sqlSession, Object[] args) {

 Object result;<br/>
 switch (command.getType()) {<br/>
   case INSERT: {<br/>
   Object param = method.convertArgsToSqlCommandParam(args);<br/>
     result = rowCountResult(sqlSession.insert(command.getName(), param));<br/>
     break;<br/>
   }<br/>
   case UPDATE: {<br/>
     Object param = method.convertArgsToSqlCommandParam(args);<br/>
     result = rowCountResult(sqlSession.update(command.getName(), param));<br/>
     break;<br/>
   }<br/>
   case DELETE: {<br/>
     Object param = method.convertArgsToSqlCommandParam(args);<br/>
     result = rowCountResult(sqlSession.delete(command.getName(), param));<br/>
     break;<br/>
   }<br/>
   case SELECT:<br/>
     if (method.returnsVoid() &amp;&amp; method.hasResultHandler()) {<br/>
       executeWithResultHandler(sqlSession, args);<br/>
       result = null;<br/>
     } else if (method.returnsMany()) {<br/>
       result = executeForMany(sqlSession, args);<br/>
     } else if (method.returnsMap()) {<br/>
       result = executeForMap(sqlSession, args);<br/>
     } else if (method.returnsCursor()) {<br/>
       result = executeForCursor(sqlSession, args);<br/>
     } else {<br/>
       Object param = method.convertArgsToSqlCommandParam(args);<br/>
       result = sqlSession.selectOne(command.getName(), param);<br/>
     }<br/>
     break;<br/>
   case FLUSH:<br/>
     result = sqlSession.flushStatements();<br/>
     break;<br/>
   default:<br/>
     throw new BindingException(&#34;Unknown execution method for: &#34; + command.getName());<br/>
 }<br/>
 if (result == null &amp;&amp; method.getReturnType().isPrimitive() &amp;&amp; !method.returnsVoid()) {<br/>
   throw new BindingException(&#34;Mapper method &#39;&#34; + command.getName()<br/>
       + &#34; attempted to return null from a method with a primitive return type (&#34; + method.getReturnType() + &#34;).&#34;);<br/>
 }<br/>
 return result;<br/>

} private Object rowCountResult(int rowCount) {

 final Object result;<br/>
 if (method.returnsVoid()) {<br/>
   result = null;<br/>
 } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {<br/>
   result = rowCount;<br/>
 } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {<br/>
   result = (long)rowCount;<br/>
 } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {<br/>
   result = rowCount &gt; 0;<br/>
 } else {<br/>
   throw new BindingException(&#34;Mapper method &#39;&#34; + command.getName() + &#34;&#39; has an unsupported return type: &#34; + method.getReturnType());<br/>
 }<br/>
 return result;<br/>

}

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&lt;T&gt;、Set&lt;T&gt;、即可,注意此时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便能够实现自动映射。如:

    &lt;select id=“selectActorById”  resultType=“canger.study.chapter04.bean.Actor”&gt;

    select actor_id as id, first_name as firstName ,last_name as lastName<br/>
    from actor<br/>
    where actor_id=#{id}<br/>
&lt;/select&gt;</pre>

public class Actor {

Long id;<br/>
String firstName;<br/>
String lastName;<br/>

}

需要特别说明的有3个地方:

  1. 返回值Bean无需为属性设置getter/setter方法,Mybatis依然能够为其赋值;
  2. 如果column名称和Bean的属性名只有部分相同,那么只有名称相同的属性会被赋值,Bean依然会被创建;
  3. 如果column名称与所有Bean的属性名都不相同,则select语句会返回null值,即使数据库中存在符合查询条件的记录;

设置mapUnderscoreToCamelCase属性实现自动映射

    &lt;settings&gt;

    &lt;setting name=&#34;mapUnderscoreToCamelCase&#34; value=&#34;true&#34;/&gt;<br/>
&lt;/settings&gt;</pre>

  • 如果数据库采用了严格的under score命名规则,则通过设置mapUnderscoreToCamelCase属性并可实现column到实体类property的自动映射;
  • 实际项目中,可将命名不规范的column通过resultMap进行显示声明,符合命名规范的进行自动映射

通过resultMap标签指定自定义类型的映射方式

需要在mapper映射文件中定义 resultMap标签

    &lt;resultMap id=“actorResultMap” type=“canger.study.chapter04.bean.Actor”&gt;

    &lt;id column=&#34;id&#34; property=&#34;id&#34; jdbcType=&#34;BIGINT&#34; javaType=&#34;long&#34; typeHandler=&#34;org.apache.ibatis.type.LongTypeHandler&#34;/&gt;<br/>
    &lt;result column=&#34;firstName&#34; property=&#34;firstName&#34; jdbcType=&#34;VARCHAR&#34; javaType=&#34;string&#34;<br/>
            typeHandler=&#34;org.apache.ibatis.type.StringTypeHandler&#34;/&gt;<br/>
    &lt;result column=&#34;lastName&#34; property=&#34;lastName&#34; jdbcType=&#34;VARCHAR&#34; javaType=&#34;string&#34;<br/>
            typeHandler=&#34;org.apache.ibatis.type.StringTypeHandler&#34;/&gt;<br/>
&lt;/resultMap&gt;</pre>

    &lt;select id=“selectActorById”  resultMap=“actorResultMap”&gt;

    select actor_id as id, first_name as firstName ,last_name as lastName<br/>
    from actor<br/>
    where actor_id=#{id}<br/>
&lt;/select&gt;</pre>

需要注意的几点

  • 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注册的默认TypeHandler参见类 org.apache.ibatis.type.TypeHandlerRegistry,具体如下
public TypeHandlerRegistry() {

register(Boolean.class, new BooleanTypeHandler());<br/>
register(boolean.class, new BooleanTypeHandler());<br/>
register(JdbcType.BOOLEAN, new BooleanTypeHandler());<br/>
register(JdbcType.BIT, new BooleanTypeHandler());

register(Byte.class, new ByteTypeHandler());

register(byte.class, new ByteTypeHandler());<br/>
register(JdbcType.TINYINT, new ByteTypeHandler());

register(Short.class, new ShortTypeHandler());

register(short.class, new ShortTypeHandler());<br/>
register(JdbcType.SMALLINT, new ShortTypeHandler());

register(Integer.class, new IntegerTypeHandler());

register(int.class, new IntegerTypeHandler());<br/>
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());<br/>
register(JdbcType.FLOAT, new FloatTypeHandler());

register(Double.class, new DoubleTypeHandler());

register(double.class, new DoubleTypeHandler());<br/>
register(JdbcType.DOUBLE, new DoubleTypeHandler());

register(Reader.class, new ClobReaderTypeHandler());

register(String.class, new StringTypeHandler());<br/>
register(String.class, JdbcType.CHAR, new StringTypeHandler());<br/>
register(String.class, JdbcType.CLOB, new ClobTypeHandler());<br/>
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());<br/>
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());<br/>
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());<br/>
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());<br/>
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());<br/>
register(JdbcType.CHAR, new StringTypeHandler());<br/>
register(JdbcType.VARCHAR, new StringTypeHandler());<br/>
register(JdbcType.CLOB, new ClobTypeHandler());<br/>
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());<br/>
register(JdbcType.NVARCHAR, new NStringTypeHandler());<br/>
register(JdbcType.NCHAR, new NStringTypeHandler());<br/>
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());<br/>
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());<br/>
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

register(InputStream.class, new BlobInputStreamTypeHandler());

register(Byte[].class, new ByteObjectArrayTypeHandler());<br/>
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());<br/>
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());<br/>
register(byte[].class, new ByteArrayTypeHandler());<br/>
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());<br/>
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());<br/>
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());<br/>
register(JdbcType.BLOB, new BlobTypeHandler());

register(Object.class, UNKNOWN_TYPE_HANDLER);

register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);<br/>
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

register(Date.class, new DateTypeHandler());

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

register(java.sql.Date.class, new SqlDateTypeHandler());

register(java.sql.Time.class, new SqlTimeTypeHandler());<br/>
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

// mybatis-typehandlers-jsr310

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