Mybatis中ResultMap解决属性名和数据库字段名不一致问题
作者:Stephen GS
前言
我们Pojo类的属性名和数据库中的字段名不一致的现象时有发生,简单的情况我们可以开启驼峰命名法解决大小写问题,但是遇到其它非大小写问题,我们就不得不使用Mybatis中的结果集映射resultMap。
1. 字段名不一致
数据库中的字段
我们项目中实体类的字段
public class User { private int id; private String name; private String password;
解决方法:
第一种方式: 起别名
pwd as password
<select id="getUserById" parameterType="int" resultType="com.gs.pojo.User"> select id,name,pwd as password from user1 where id = #{id}; </select>
第二种方式: 结果集映射 resultMap
使用resultMap匹配pojo类中属性名与数据库中字段的值。
其中column为数据库中的字段,property为实体类中的属性
<!--结果集映射--> <resultMap id="UserMap" type="user"> <!--column数据库中的字段,property实体类中的属性--> <result column="id" property="id"/> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="getUserById" parameterType="int" resultMap="UserMap"> select * from user1 where id = #{id}; </select>
官方文档有这么一段话:
- resultMap元素是MyBatis中最重要强大的元素
- ResultMap的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
- ResultMap的优秀之处在于你完全可以不用显示的配置字段名已经相同的属性
根据官方文档,上面的结果集映射可以简化为:
<!--结果集映射--> <resultMap id="UserMap" type="user"> <!--column数据库中的字段,property实体类中的属性--> <result column="pwd" property="password"/> </resultMap> <select id="getUserById" parameterType="int" resultMap="UserMap"> select * from user1 where id = #{id}; </select>
2. 多对一处理
上面讲述的只是普通字段名不一致的问题,使用结果集映射解决相对简单。而在Mybatis中关于使用结果集映射解决的问题比较出名的有多对一的处理和一对多的处理(这两种问题是相对而言的)
我们生活中有很多相关的例子,这里以老师和学生为例,对于学生而言,多个学生拥有一个共同的老师,这属于多对一问题;对于老师而言,一个老师拥有多个学生,这又是一个一对多的问题。
相关解析:
对于学生而言,属于关联现象,多个老师关联一个老师 (多对一)
对于老师而言,属于集合现象, 一个老师拥有很多学生(一对多)
测试环境搭建;
[1] 建立一个老师表和学生表
CREATE TABLE `teacher`( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) )ENGINE = INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher(`id`,`name`) VALUES(1,'zs'); CREATE TABLE `student`( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY(`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher`(`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO `student`(`id`,`name`,`tid`) VALUES('1','小明','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES('2','小红','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES('3','小张','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES('4','小李','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES('5','小工','1');
[2] 新建一个Maven项目,导入相关依赖,并编写实体类及其映射
搭建后项目目录为:
<1> 导入依赖,并解决打包问题
<!--导入依赖--> <dependencies> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency> </dependencies> <!--解决导出项目时resources的xml未打包问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
<2> 编写数据库配置文件 db.properties
driver = com.mysql.jdbc.Driver url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username = root
<3> 编写Mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--导入外部配置文件--> <properties resource="db.properties"/> <!-- <settings> <!–标准的日志工厂实现–> <!–<setting name="logImpl" value="STDOUT_LOGGING"/>–> <setting name="logImpl" value="LOG4J"/> </settings>--> <!--可以给实体类起别名--> <typeAliases> <package name="com.gs.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/gs/dao/TeacherMapper.xml"/> <mapper resource="com/gs/dao/StudentMapper.xml"/> <!--<mapper class="com.com.com.gs.dao.TeacherMapper"/> <mapper class="com.com.com.gs.dao.StudentMapper"/>--> </mappers> </configuration>
<4> 编写实体类
这里的Student类中有一个老师的字段,这正是与我们数据库中不一致的地方,也是这次问题解决的重点
Student.class
package com.gs.pojo; import lombok.Data; /** * @Auther: Gs * @Date: 2020/6/9 * @Description: com.com.com.gs.pojo * @version: 1.0 */ @Data public class Student { private int id; private String name; //组合进来一个老师 private Teacher teacher; }
Teacher.class
package com.gs.pojo; import lombok.Data; /** * @Auther: Gs * @Date: 2020/6/9 * @Description: com.com.com.gs.pojo * @version: 1.0 */ @Data public class Teacher { private int id; private String name; }
<5> 编写接口 StudentMapper
public interface StudentMapper { //查询所有的学生信息,以及对应的的老师的信息 public List<Student> getStudent(); }
<6> 使用resultMap解决问题
[1] 按结果嵌套查询 使用association字段来匹配结果集中Teacher中的字段属性
<!--按照结果嵌套处理--> <select id="getStudent" resultMap="studentMapper2"> select s.id sid, s.name sname, t.name tname from student s, teacher t where s.tid = t.id </select> <resultMap id="studentMapper2" type="student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
解析:一开始看这sql语句肯定很绕,我们先从原生的sql语句出发,编写出能联表查询的语句(因为学生类里有老师这个实体属性)
这里我们使用别名以及联表查询得到学号,学生姓名,老师姓名,不难发现我们使用简单的reultType已经不能解决这个多属性问题(resultType适合解决单一实体类,字段名和数据库属性一一对应的情况),所以我们想到使用结果集映射 resultMap.
select s.id sid, s.name sname, t.name tname from student s, teacher t where s.tid = t.id
resultMap中前两项的字段名和属性名比较好映射,直接是学生的id,学生的姓名,可是最后一项的属性时一个Teacher对象,所以我们必须使用关联association 来嵌套老师这个实体类的属性。相关语句为:
<association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association>
那么完整的隐射语句为:
<resultMap id="studentMapper2" type="student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
最后我们再把这两个语句拼接就能得到我们最终的语句了。
[2] 按照查询嵌套处理 (理解起来相对复杂)
<!--思路: 1.查询所有的学生信息 2.根据查询出来的学生的tid,寻找对应的老师 子查询 --> <select id="getStudent" resultMap="studentMapper"> select * from student; </select> <resultMap id="studentMapper" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <!--复杂的属性,我们需要单独处理 对象: association 集合: collection --> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher"> select * from teacher where id = #{id}; </select>
解析:看着语句更加晕了,这其实相当于我们原生Sql的子查询,这里分三步走,把原本的结果集在通过查询进行封装。
<1> 编写简单的学生查询
<select id="getStudent" resultMap="studentMapper"> select * from student; </select>
<2> 完成相关实体类属性和数据库字段的映射,前两个依旧简单,关键还是Teacher这个实体类,这次我们不嵌套属性,而是通过一次子查询解决问题 (property对应实体类属性,column对应数据库名称,javaType标明类型,select表示使用查询的方法)
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
这里使用 getTeacher的方法获取新的属性值
<select id="getTeacher" resultType="Teacher"> select * from teacher where id = #{id}; </select>
完整的映射
<resultMap id="studentMapper" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <!--复杂的属性,我们需要单独处理 对象: association 集合: collection --> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap>
最后,我们把这三句sql进行拼装,就能得到我们上面最终的结果了。
3. 一对多处理
同样是上面的例子,对老师而言,就是一对多的关系
[1] 环境搭建和上面的一样
[2] 测试的实体类的变化
Student
@Data public class Student { //学生id private int id; //学生姓名 private String name; //教师id private int tid; }
Teacher
@Data public class Teacher { private int id; private String name; //一个老师有多个学生 private List<Student> students; }
[3] 业务需求:获取指定老师下所有学生及老师自身的信息
<1> 编写接口
//获取指定老师下所有学生及老师的信息 Teacher getTeacher(@Param("tid") int id);
<2> 解决方式依旧是两种,一种是按结果嵌套查询(即所谓的连接查询),另外一种是按照查询嵌套处理(即所谓的子查询)
第一种方式:按结果嵌套查询
编写连接查询的sql语句
<!--按结果嵌套查询--> <select id="getTeacher" resultMap="teacherMap"> select s.id sid, s.name sname, t.id tid, t.name tname from student s, teacher t where s.tid = t.id and t.id = #{tid} </select>
通过结果集映射找到相应的属性(由于是一对多关系,所以使用集合类型 collection)
<resultMap id="teacherMap" type="teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!--复杂的属性:我们需要单独处理对象, 对象:association 集合:collection javaType="" 指定属性类型 ofType 指定属性中的泛型类型 --> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>
相关解析:
复杂的属性:我们需要单独处理对象, 多对一时使用 association,一对多是使用collection;
若是普通的pojo类使用 javaType="" 指定属性类型,若是泛型类型则要使用ofType 指定属性中的泛型类型
3) 我们拼接一下上面两段代码即得到我们的结果
<!--按结果嵌套查询--> <select id="getTeacher" resultMap="teacherMap"> select s.id sid, s.name sname, t.id tid, t.name tname from student s, teacher t where s.tid = t.id and t.id = #{tid} </select> <resultMap id="teacherMap" type="teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!--复杂的属性:我们需要单独处理对象, 对象:association 集合:collection javaType="" 指定属性类型 ofType 指定属性中的泛型类型 --> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>
第二种方式:按照查询嵌套处理
使用子查询进行拼接
<1> 先查出指定老师的信息
<!--子查询--> <select id="getTeacher" resultMap="teacherMap2"> select * from teacher where id = #{tid} </select>
<2> 由于查询的信息里有学生对象信息,所以得进行结果集映射
<resultMap id="teacherMap2" type="Teacher"> <result property="id" column="id"/> <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/> </resultMap>
解析:
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/>
property表明要查询的实体类属性名,javaType表明查询的类型,ofType表明泛型的类型,select表明使用查询的方式,这里比较疑惑的应该是column的值,它对应的是数据库中的列,那它应该使用什么呢,我们不难发现,我们要查询的是老师下的所有学生,而学生表示通过老师id进行关联的,那么它对应的值应该是数据库的教师id的列名
<3> 最后编写通过老师id查询学生的方法.getStudent
<select id="getStudent" resultType="Student"> select * from student where tid=#{id} </select>
<4> 最后我们把上面的代码块进行拼接就能满足我们的业务需求了。
<!-- =============================== --> <!--子查询--> <select id="getTeacher" resultMap="teacherMap2"> select * from teacher where id = #{tid} </select> <resultMap id="teacherMap2" type="Teacher"> <result property="id" column="id"/> <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/> </resultMap> <select id="getStudent" resultType="Student"> select * from student where tid=#{id} </select>
小结
解决属性名不一致的问题大体上分为三种情况。简单的名字不同,类型相同我们可以起别名解决。对于名字不同,类型也不同,我们只能使用结果集映射解决,这种方式又分为两种情况,一种是多对一,一种是一对多,我们在解决多对一时,可以联想学生和老师的关系,多个学生关联一个老师,那么我们使用association(关联)这个关键词,而反过来,一个老师下有多个学生,属于集合关系,那么我们可以使用collection(集合)。
到此这篇关于Mybatis中ResultMap解决属性名和数据库字段名不一致问题的文章就介绍到这了,更多相关Mybatis 属性名和数据库字段名不一致内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!