Mybatis自动映射和手动映射:namespace,resultMap和resultType & 自动映射规则

最近在使用mybatis时,对于mapper.xml中的<mapper>,手动映射,自动映射有些疑惑,对namespaceresultMapresultType的作用也有点疑惑。

小总结:

  • 映射文件中的namespace是用于绑定Dao接口的,即面向接口编程。当你的namespace绑定接口后,你可以不用写接口实现类,mybatis会通过该绑定自动帮你找到对应要执行的SQL语句。
  • resultMap是Mybatis最强大的元素,支持自定义,而resultType直接返回对象类型,但两者不能同时存在resultMap可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中。通过<association><collection分别实现一对一关联一对多关联

mybatis 中自动映射主要有 2 种配置:

  • 一种是全局的配置,对应用中所有的 resultMap 起效,这个是在 mybatis 配置文件(mybatis-config.xml)中进行设置的;
<settings>
    <setting name="autoMappingBehavior" value="自动映射规则(NONE/PARTIAL/FULL)"/>
</settings>
  • 另外一种是通过resultMap 的 autoMapping 属性进行配置。mybatis 判断某个 resultMap 是否开启自动映射配置的时候,会先查找自身的 autoMapping 属性,如果这个属性设置值了,就直接用这个属性的值,如果resultMap 元素的 autoMapping 属性没有配置,则走全局配置的自动映射规则。
<resultMap id="orderModelMap2" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true">
</resultMap>

自动映射小总结:

  1. 所谓自动映射,就是 mybatis 会自动按照列名和 Model 中同名的字段column=property)进行映射赋值,不用手动编写映射规则。但是columnproperty不完全一致时,是否支持自动映射呢?最近在做项目时,为了图方便,javaBean中的属性和数据库表的字段都设置为大写,但是我担心这样的属性命名并不符合javaBean的驼峰命名规范,担心mybatis不能将column和property实现自动映射。但是当我了解了mybatis官网提供的自动映射的解释,我放心了。

这里mybatis的自动映射规则可参照官网:https://mybatis.org/mybatis-3/sqlmap-xml.html#Auto-mapping

When auto-mapping results MyBatis will get the column name and look for a property with the same name ignoring case. That means that if a column named ID and property named id are found, MyBatis will set the id property with the ID column value.

mybatis在自动映射时,会根据column去寻找忽略大小写的、和column名字保持一致的property,即column=IDproperty=id可以实现自动映射(column,property全转成小写再比较)。

Usually database columns are named using uppercase letters and underscores between words and java properties often follow the camelcase naming covention. To enable the auto-mapping between them set the setting mapUnderscoreToCamelCase to true.

在设计数据库表时,字段通常用大写字母,以及下划线来命名;而对于java属性通常采用驼峰命名法,为了实现两者的自动匹配,这里需要将mapUnderscoreToCamelCase设置为True;

Auto-mapping works even when there is an specific result map. When this happens, for each result map, all columns that are present in the ResultSet that have not a manual mapping will be auto-mapped, then manual mappings will be processed.

在手动映射时,那些未手动配置的column-property对,mybatis会进行自动映射

  1. 在 resultMap 中指定了 autoMapping 属性值为 true 时,就会开启自动映射。
  2. 设置全局自动映射,需要在 mybatis 全局配置文件中设置 autoMappingBehavior
  3. 写 mapper.xml 的时候,建议将映射的配置都给写上去,这样能够杜绝一些隐患,使我们的系统更稳定。

这里补充一个很容易疏忽的bug:

<select id="findTrainingManagePersonal" resultMap="baseMap" parameterType="com.dpf.trainingManage.entity.TrainingManagePersonal">
        SELECT * FROM training_management_personnel
        WHERE DEL_STATUS = '0'
        AND ID = #{id}
    </select>

上面的语句在数据库中有正常查出来,但是会报如下的错误:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d24573e] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@63ac0733] will not be managed by Spring
==>  Preparing: select * from training_plan where ID = ? AND DEL_STATUS = '0'
==> Parameters: 1(Integer)
<==    Columns: ID, PNAME, START_TIME, END_TIME, ORGANIZATION_ID, PERSONNEL_ID, PREQUIRE, CREATE_BY, CREATE_DATE, UPDATE_BY, UPDATE_DATE, CREATE_NAME, UPDATE_NAME, DEL_STATUS
<==        Row: 1, wangxiaoxi1, 2021-12-14 00:00:00, 2021-12-14 00:00:00, null, null, null, null, null, null, null, null, null, 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d24573e]
2021-12-14 17:24:47.457 ERROR 16004 --- [nio-9090-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: Error attempting to get column 'END_TIME' from result set.  Cause: java.sql.SQLDataException: Cannot determine value type from string '2021-12-14 00:00:00'
; Cannot determine value type from string '2021-12-14 00:00:00'; nested exception is java.sql.SQLDataException: Cannot determine value type from string '2021-12-14 00:00:00'] with root cause

com.mysql.cj.exceptions.DataConversionException: Cannot determine value type from string '2021-12-14 00:00:00'

主要原因很可能是在实体类中加入了有参构造,使得mybatis在装配对象时用的是有参构造器``

所以解决思路是:

  • 一是去掉@AllArgConstructor,恢复默认的无参构造器
  • 二是在实体类中手动添加无参构造器,实现方法的重载,否则@AllArgConstructor会创建有参构造器来重写原来的无参构造器

参考

  • Mybatis的namespace作用参考 Mybatis的mapper标签 namespace属性说明
  • Mybatis的resultType和resultMap比较参考 mybaties中resultMap和resultType的区别
  • Mybatis的resultMap作用参考 Mybatis:resultMap的使用总结
<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>
<association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
<result  column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property须为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />  
</collection>
</resultMap>
  • Mybatis自动映射参考 Mybatis手动映射和自动映射