在使用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和相應表字段的對應。
- <!-- 訂單查詢關聯用戶的resultMap
- 將整個查詢的結果映射到cn.itcast.mybatis.po.Orders中
- -->
- <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserResultMap">
- <!-- 配置映射的訂單信息 -->
- <!-- id:指定查詢列中的唯 一標識,訂單信息的中的唯 一標識,如果有多個列組成唯一標識,配置多個id
- column:訂單信息的唯 一標識 列
- property:訂單信息的唯 一標識 列所映射到Orders中哪個屬性
- -->
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property="note"/>
- </resultMap>
resultMap對於一對一表連接的處理方式通常為在主表的pojo中添加嵌套另一個表的pojo,然后在mapper.xml中采用association節點元素進行對另一個表的連接處理。例如:
- <!-- 訂單查詢關聯用戶的resultMap
- 將整個查詢的結果映射到cn.itcast.mybatis.po.Orders中
- -->
- <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserResultMap">
- <!-- 配置映射的訂單信息 -->
- <!-- id:指定查詢列中的唯 一標識,訂單信息的中的唯 一標識,如果有多個列組成唯一標識,配置多個id
- column:訂單信息的唯 一標識 列
- property:訂單信息的唯 一標識 列所映射到Orders中哪個屬性
- -->
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property=note/>
- <!-- 配置映射的關聯的用戶信息 -->
- <!-- association:用於映射關聯查詢單個對象的信息
- property:要將關聯查詢的用戶信息映射到Orders中哪個屬性
- -->
- <association property="user" javaType="cn.itcast.mybatis.po.User">
- <!-- id:關聯查詢用戶的唯 一標識
- column:指定唯 一標識用戶信息的列
- javaType:映射到user的哪個屬性
- -->
- <id column="user_id" property="id"/>
- <result column="username" property="username"/>
- <result column="sex" property="sex"/>
- <result column="address" property="address"/>
- </association>
- </resultMap>
若是一對多的表連接方式,比如訂單表和訂單明細表即為一對多連接,若是不對sql語句進行處理,由於一個訂單對應多條訂單明細,因此查詢出的結果對於訂單表數據來說將會出現重復,例如:
resultMap的處理方式為在訂單表數據的pojo中添加一個list,list中為訂單明細表的屬性,在mapper.xml中采用如下的處理方式:
- !-- 訂單及訂單明細的resultMap
- 使用extends繼承,不用在中配置訂單信息和用戶信息的映射
- -->
- <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">
- <!-- 訂單信息 -->
- <!-- 用戶信息 -->
- <!-- 使用extends繼承,不用在中配置訂單信息和用戶信息的映射 -->
- <!-- 訂單明細信息
- 一個訂單關聯查詢出了多條明細,要使用collection進行映射
- collection:對關聯查詢到多條記錄映射到集合對象中
- property:將關聯查詢到多條記錄映射到cn.itcast.mybatis.po.Orders哪個屬性
- ofType:指定映射到list集合屬性中pojo的類型
- -->
- <collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
- <!-- id:訂單明細唯 一標識
- property:要將訂單明細的唯 一標識 映射到cn.itcast.mybatis.po.Orderdetail的哪個屬性
- -->
- <id column="orderdetail_id" property="id"/>
- <result column="items_id" property="itemsId"/>
- <result column="items_num" property="itemsNum"/>
- <result column="orders_id" property="ordersId"/>
- </collection>
- </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個地方:
- 返回值Bean無需為屬性設置getter/setter方法,Mybatis依然能夠為其賦值;
- 如果column名稱和Bean的屬性名只有部分相同,那么只有名稱相同的屬性會被賦值,Bean依然會被創建;
- 如果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