前言
MyBatis在进行查询映射时,查询出来的每一个属性都是放在一个对应的Map里面的,其中键是属性名,值则是其对应的值。但当指定返回类型属性resultType的时候,MyBatis会将Map里面的键值对取出赋给resultType所指定的对象对应的属性。
而当我们提供的返回类型是resultMap的时候,这就需要自己再进一步的把它转化为对应的对象,这常用于级联查询以及缓存功能。
我们先用一个简单的例子来了解一下 resultMap 的使用。
场景
实体类中的某个属性在数据库中没有相应的字段,但可以通过其它字段来做业务逻辑获取到。
实体类文件:
User.java
package com.lks.domain;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by likaisong on 2019/2/24.
*/
public class User implements Serializable {
private int id;
private String name;
private int age;
private String county;
private Date date;
private Integer birthYear;
public User(){}
//resultMap 实现新字段赋值
public User(Integer age){
this.age = age;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy");
this.birthYear = Integer.valueOf(simpleDateFormat.format(new Date())) - age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getCounty() {
return county;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setCounty(String county) {
this.county = county;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date= date;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + ", county=" + county + ", date=" + date + ", birthYear=" + birthYear + "]";
}
}
其中birthYear属性在数据库中是没有相应字段的,但可以通过数据库中的age字段间接获取到,主要看User带参构造方法。
在userMapper.xml文件中配置resultMap,其中借助construct标签
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的-->
<mapper namespace="userMapper">
<!--resultMap 实现新字段赋值-->
<resultMap id="TypeChangeResultMap" type="com.lks.domain.User">
<constructor>
<arg column="age" javaType="Integer"/>
</constructor>
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="county" column="county"/>
<result property="date" column="date"/>
</resultMap>
<sql id="tableAllkey">
id AS "id",
name AS "name",
age AS "age",
county AS "county",
date AS "date"
</sql>
<select id="resultMapTypeChange" parameterType="int" resultMap="TypeChangeResultMap">
select <include refid="tableAllkey"/> from users where id=#{id}
</select>
</mapper>
此处对字段我是用了一个 sql 标签进行包裹,sql 字段可复用,也可直接写到 sql 语句的 select 后面,可自行选择。需要说明的一点是,如果数据库字段与实体类字段完全匹配,字段是可以不添加别名的,如果不是完全匹配,则必须添加别名,只用这样才能使数据库字段映射列到 JavaBean 的属性上。
编写测试类来校验结果:
TestMain.java
package com.lks.test;
import com.lks.domain.User;
import com.lks.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.testng.annotations.AfterTest;
import org.testng.annotations.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
/**
* Created by likaisong on 2019/2/24.
*/
public class TestMain {
private static SqlSession session;
private static SqlSession getSqlSession(String resource) {
if (resource == null || "".equals(resource)) {
return null;
}
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
return session;
}
@Test
public static void testResultMapNewKey() {
String resource = "mybatis-config.xml";
session = getSqlSession(resource);
String statement = "userMapper.resultMapTypeChange";
User user = session.selectOne(statement, 23);
System.out.println(user);
}
@AfterTest
public static void closeSession() {
session.close();
}
}
可以看到结果中我们取得了我们新增的字段birthYear。
ResultMap还有许多高级用法,尤其是级联查询以及缓存功能。