在使用Mybatis中定义resultMap的时候,经常会遇到这三个比较常用的标签association、collection和discriminator,本篇博客就在上两遍博客的基础上,讲解如何使用这个三个标签。
下面依次进行介绍,首先看一下association:
先看一下两个不同的resultMap定义:
id="city" type="com.entities.City">
<id column="city_id" jdbcType="INTEGER" property="id"/>
<result column="city_name" jdbcType="CHAR" property="name"/>
<result column="city_countryCode" jdbcType="CHAR" property="countrycode"/>
<result column="city_district" jdbcType="CHAR" property="district"/>
<result column="city_population" jdbcType="INTEGER" property="population"/>
id="countryAndCity" type="com.entities.AssociationBean">
<id column="country_code" jdbcType="CHAR" property="code" />
<result column="country_name" jdbcType="CHAR" property="name" />
<result column="country_continent" jdbcType="CHAR" property="continent" />
<result column="country_region" jdbcType="CHAR" property="region" />
<result column="country_surfaceArea" jdbcType="REAL" property="surfacearea" />
<result column="country_indepYear" jdbcType="SMALLINT" property="indepyear" />
<result column="country_population" jdbcType="INTEGER" property="population" />
<result column="country_lifeExpectancy" jdbcType="REAL" property="lifeexpectancy" />
<result column="country_gnp" jdbcType="REAL" property="gnp" />
<result column="country_gnpOld" jdbcType="REAL" property="gnpold" />
<result column="country_localName" jdbcType="CHAR" property="localname" />
<result column="country_governmentForm" jdbcType="CHAR" property="governmentform" />
<result column="country_headOfState" jdbcType="CHAR" property="headofstate" />
<result column="country_capital" jdbcType="INTEGER" property="capital" />
<result column="country_code2" jdbcType="CHAR" property="code2" />
<result column="city_id" jdbcType="INTEGER" property="city_id"/>
<result column="city_name" jdbcType="CHAR" property="city_name"/>
<result column="city_countryCode" jdbcType="CHAR" property="city_countryCode"/>
<result column="city_district" jdbcType="CHAR" property="city_district"/>
<result column="city_population" jdbcType="INTEGER" property="city_population"/>
id="countryAndCity1" type="com.entities.AssociationBean1">
<id column="country_code" jdbcType="CHAR" property="code" />
<result column="country_name" jdbcType="CHAR" property="name" />
<result column="country_continent" jdbcType="CHAR" property="continent" />
<result column="country_region" jdbcType="CHAR" property="region" />
<result column="country_surfaceArea" jdbcType="REAL" property="surfacearea" />
<result column="country_indepYear" jdbcType="SMALLINT" property="indepyear" />
<result column="country_population" jdbcType="INTEGER" property="population" />
<result column="country_lifeExpectancy" jdbcType="REAL" property="lifeexpectancy" />
<result column="country_gnp" jdbcType="REAL" property="gnp" />
<result column="country_gnpOld" jdbcType="REAL" property="gnpold" />
<result column="country_localName" jdbcType="CHAR" property="localname" />
<result column="country_governmentForm" jdbcType="CHAR" property="governmentform" />
<result column="country_headOfState" jdbcType="CHAR" property="headofstate" />
<result column="country_capital" jdbcType="INTEGER" property="capital" />
<result column="country_code2" jdbcType="CHAR" property="code2" />
property="city" resultMap="city"/>
其实countryAndCity和countryAndCity1在实际对返回结果集的表示上是一致,都是对结果的封装,只是countryAndCity1将City表中的字段抽取出来进行封装成city对象,并将其作为一个属性存放在AssociationBean1对象中。
而countryAndCity只是将查询语句返回的所有字段映射为一个对象的内部属性,并没有对部分属性进行二次封装。类似的这种封装在联表查询中经常会遇到这种情况,下载看一下代码,以及完成的mapper语句
id="city" type="com.entities.City">
<id column="city_id" jdbcType="INTEGER" property="id"/>
<result column="city_name" jdbcType="CHAR" property="name"/>
<result column="city_countryCode" jdbcType="CHAR" property="countrycode"/>
<result column="city_district" jdbcType="CHAR" property="district"/>
<result column="city_population" jdbcType="INTEGER" property="population"/>
id="countryAndCity1" type="com.entities.AssociationBean1">
<id column="country_code" jdbcType="CHAR" property="code" />
<result column="country_name" jdbcType="CHAR" property="name" />
<result column="country_continent" jdbcType="CHAR" property="continent" />
<result column="country_region" jdbcType="CHAR" property="region" />
<result column="country_surfaceArea" jdbcType="REAL" property="surfacearea" />
<result column="country_indepYear" jdbcType="SMALLINT" property="indepyear" />
<result column="country_population" jdbcType="INTEGER" property="population" />
<result column="country_lifeExpectancy" jdbcType="REAL" property="lifeexpectancy" />
<result column="country_gnp" jdbcType="REAL" property="gnp" />
<result column="country_gnpOld" jdbcType="REAL" property="gnpold" />
<result column="country_localName" jdbcType="CHAR" property="localname" />
<result column="country_governmentForm" jdbcType="CHAR" property="governmentform" />
<result column="country_headOfState" jdbcType="CHAR" property="headofstate" />
<result column="country_capital" jdbcType="INTEGER" property="capital" />
<result column="country_code2" jdbcType="CHAR" property="code2" />
property="city" resultMap="city"/>
相关的service和controller代码:
//service:
public AssociationBean1 selectCountryAndCity(String code, String name){
Map param = new HashMap();
param.put("code",code);
param.put("name",name);
return countryMapper.selectCountryAndCity(param);
}
//controller:
@ApiOperation(value = "selectCountryAndCity")
@RequestMapping(value = "/countryAndCity",method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseDto selectCountryAndCity(@RequestParam(value = "code") @ApiParam(value = "国家代码") String code,
@RequestParam(value = "name") @ApiParam(value = "城市名称") String name){
AssociationBean1 bean = countryService.selectCountryAndCity(code,name);
ResponseDto dto = new ResponseDto();
dto.setData(bean);
dto.setTimestamp(String.valueOf(System.currentTimeMillis()));
dto.setStatus("200");
return dto;
}
基本上,通过以上代码,直接启动springboot应用,然后通过使用swagger提供的本地调试接口就可以测试项目中实现的接口是否正确了!
实际的运行结果如下:
{
"status": "200",
"timestamp": "1525593649226",
"data": {
"code": "NLD",
"name": "Netherlands",
"continent": null,
"region": "Western Europe",
"surfacearea": 41526,
"indepyear": 1581,
"population": 15864000,
"lifeexpectancy": 78.3,
"gnp": 371362,
"gnpold": 360478,
"localname": "Nederland",
"governmentform": "Constitutional Monarchy",
"headofstate": "Beatrix",
"capital": 5,
"code2": "NL",
"city": {
"id": 5,
"name": "Amsterdam",
"countrycode": "NLD",
"district": "Noord-Holland",
"population": 731200
}
}
}
从结果中可以看到,内部有五个字段被封装成了一个city对象。association的用法大概就是这样,在看一下collection的用法。
collection与association的用法比较类似,association适用于一对一的关联,而collection更多用于一对多的关联,例如在数据库中查询一个国家的所有被记录的城市,这个时候适合使用collection标签,具体用法如下:
id="city" type="com.entities.City">
<id column="city_id" jdbcType="INTEGER" property="id"/>
<result column="city_name" jdbcType="CHAR" property="name"/>
<result column="city_countryCode" jdbcType="CHAR" property="countrycode"/>
<result column="city_district" jdbcType="CHAR" property="district"/>
<result column="city_population" jdbcType="INTEGER" property="population"/>
id="countryAndCity2" type="com.entities.CollectionBean">
<id column="country_code" jdbcType="CHAR" property="code" />
<result column="country_name" jdbcType="CHAR" property="name" />
<result column="country_continent" jdbcType="CHAR" property="continent" />
<result column="country_region" jdbcType="CHAR" property="region" />
<result column="country_surfaceArea" jdbcType="REAL" property="surfacearea" />
<result column="country_indepYear" jdbcType="SMALLINT" property="indepyear" />
<result column="country_population" jdbcType="INTEGER" property="population" />
<result column="country_lifeExpectancy" jdbcType="REAL" property="lifeexpectancy" />
<result column="country_gnp" jdbcType="REAL" property="gnp" />
<result column="country_gnpOld" jdbcType="REAL" property="gnpold" />
<result column="country_localName" jdbcType="CHAR" property="localname" />
<result column="country_governmentForm" jdbcType="CHAR" property="governmentform" />
<result column="country_headOfState" jdbcType="CHAR" property="headofstate" />
<result column="country_capital" jdbcType="INTEGER" property="capital" />
<result column="country_code2" jdbcType="CHAR" property="code2" />
"list" resultMap="city" property="cities"/>
使用property指明在java对象中对应的属性名,使用javaType表明属性类型为一个list集合。
在select中使用时,只需要直接指定resultMap值为countryAndCity2即可,select语句如下:
<select id="selectAllCityByCountry" parameterType="map" resultMap="countryAndCity2">
SELECT country.Code as country_code,
country.Name AS country_name,
country.Continent AS country_contient,
country.Region as country_region,
country.SurfaceArea as country_surfaceArea,
country.IndepYear as country_indepYear,
country.Population as country_population,
country.LifeExpectancy as country_lifeExpectancy,
country.GNP as country_gnp,
country.GNPOld as country_gnpOld,
country.LocalName as country_localName,
country.GovernmentForm as country_governmentForm,
country.HeadOfState as country_headOfState,
country.Capital as country_capital,
country.Code2 as country_code2,
city.ID as city_id,
city.Name as city_name,
city.CountryCode as city_countryCode,
city.District as city_district,
city.Population as city_population
From country LEFT JOIN city ON country.code=city.countrycode WHERE country.code=#{code}
select>
相对应的service和controller代码如下:
//service:
public CollectionBean selectAllCityByCountry(String code){
Map param = new HashMap();
param.put("code",code);
return countryMapper.selectAllCityByCountry(param);
}
//controller:
@ApiOperation(value = "selectAllCityByCountry")
@RequestMapping(value = "/allCity",method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseDto selectAllCityByCountry(@RequestParam(value = "code") @ApiParam(value = "国家代码") String code){
CollectionBean bean = countryService.selectAllCityByCountry(code);
ResponseDto dto = new ResponseDto();
dto.setData(bean);
dto.setTimestamp(String.valueOf(System.currentTimeMillis()));
dto.setStatus("200");
return dto;
}
代码测试结果如下:
{
"status": "200",
"timestamp": "1525594129868",
"data": {
"code": "AFG",
"name": "Afghanistan",
"continent": null,
"region": "Southern and Central Asia",
"surfacearea": 652090,
"indepyear": 1919,
"population": 22720000,
"lifeexpectancy": 45.9,
"gnp": 5976,
"gnpold": null,
"localname": "Afganistan/Afqanestan",
"governmentform": "Islamic Emirate",
"headofstate": "Mohammad Omar",
"capital": 1,
"code2": "AF",
"cities": [
{
"id": 1,
"name": "Kabul",
"countrycode": "AFG",
"district": "Kabol",
"population": 1780000
},
{
"id": 2,
"name": "Qandahar",
"countrycode": "AFG",
"district": "Qandahar",
"population": 237500
},
{
"id": 3,
"name": "Herat",
"countrycode": "AFG",
"district": "Herat",
"population": 186800
},
{
"id": 4,
"name": "Mazar-e-Sharif",
"countrycode": "AFG",
"district": "Balkh",
"population": 127800
}
]
}
}
从实际的测试结果来看,mybatis已经自动的将所有的city自动封装成一个list!
关于最后一个discriminator用来根据查询返回的结果集不同而采用不同的ResultMap,具体的使用如下:
<resultMap id="countryAndCity3" type="com.entities.Country">
<discriminator column="country_indepYear" jdbcType="INTEGER" javaType="int">
<case value="1949" resultMap="countryAndCity2"/>
<case value="1776" resultMap="BaseResultMap"/>
discriminator>
resultMap>
两个resultMap之间的区别如下:
id="BaseResultMap" type="com.entities.Country">
<id column="country_code" jdbcType="CHAR" property="code" />
<result column="country_name" jdbcType="CHAR" property="name" />
<result column="country_continent" jdbcType="CHAR" property="continent" />
<result column="country_region" jdbcType="CHAR" property="region" />
<result column="country_surfaceArea" jdbcType="REAL" property="surfacearea" />
<result column="country_indepYear" jdbcType="SMALLINT" property="indepyear" />
<result column="country_population" jdbcType="INTEGER" property="population" />
<result column="country_lifeExpectancy" jdbcType="REAL" property="lifeexpectancy" />
<result column="country_gnp" jdbcType="REAL" property="gnp" />
<result column="country_gnpOld" jdbcType="REAL" property="gnpold" />
<result column="country_localName" jdbcType="CHAR" property="localname" />
<result column="country_governmentForm" jdbcType="CHAR" property="governmentform" />
<result column="country_headOfState" jdbcType="CHAR" property="headofstate" />
<result column="country_capital" jdbcType="INTEGER" property="capital" />
<result column="country_code2" jdbcType="CHAR" property="code2" />
id="city" type="com.entities.City">
<id column="city_id" jdbcType="INTEGER" property="id"/>
<result column="city_name" jdbcType="CHAR" property="name"/>
<result column="city_countryCode" jdbcType="CHAR" property="countrycode"/>
<result column="city_district" jdbcType="CHAR" property="district"/>
<result column="city_population" jdbcType="INTEGER" property="population"/>
id="countryAndCity2" type="com.entities.CollectionBean">
<id column="country_code" jdbcType="CHAR" property="code" />
<result column="country_name" jdbcType="CHAR" property="name" />
<result column="country_continent" jdbcType="CHAR" property="continent" />
<result column="country_region" jdbcType="CHAR" property="region" />
<result column="country_surfaceArea" jdbcType="REAL" property="surfacearea" />
<result column="country_indepYear" jdbcType="SMALLINT" property="indepyear" />
<result column="country_population" jdbcType="INTEGER" property="population" />
<result column="country_lifeExpectancy" jdbcType="REAL" property="lifeexpectancy" />
<result column="country_gnp" jdbcType="REAL" property="gnp" />
<result column="country_gnpOld" jdbcType="REAL" property="gnpold" />
<result column="country_localName" jdbcType="CHAR" property="localname" />
<result column="country_governmentForm" jdbcType="CHAR" property="governmentform" />
<result column="country_headOfState" jdbcType="CHAR" property="headofstate" />
<result column="country_capital" jdbcType="INTEGER" property="capital" />
<result column="country_code2" jdbcType="CHAR" property="code2" />
"list" resultMap="city" property="cities"/>
BaseResultMap只是包含了国家的相关信息,并不包含城市信息,而countryAndCity2不仅包含了国家信息,还包含了一个国家中的城市信息。具体的查询如下:
<select id="selectResultByIndepYear" parameterType="map" resultMap="countryAndCity3">
SELECT country.Code as country_code,
country.Name AS country_name,
country.Continent AS country_contient,
country.Region as country_region,
country.SurfaceArea as country_surfaceArea,
country.IndepYear as country_indepYear,
country.Population as country_population,
country.LifeExpectancy as country_lifeExpectancy,
country.GNP as country_gnp,
country.GNPOld as country_gnpOld,
country.LocalName as country_localName,
country.GovernmentForm as country_governmentForm,
country.HeadOfState as country_headOfState,
country.Capital as country_capital,
country.Code2 as country_code2,
city.ID as city_id,
city.Name as city_name,
city.CountryCode as city_countryCode,
city.District as city_district,
city.Population as city_population
From country LEFT JOIN city ON country.code=city.countrycode WHERE country.code=#{code}
select>
记住这里的BaseResultMap和countryAndCity2对应的javaBean存在一定的关系,也就是countryAndCity3对应的java类一定为BaseResultMap和countryAndCity2对应的java类的父类,如果不这样做的话,没有办法定义mapper接口的返回类型,在测试项目中CollectionBean继承Country类。
相关的service和controller代码如下:
//service:
public Country selectResultByIndepYear(String code){
Map param = new HashMap();
param.put("code",code);
return countryMapper.selectResultByIndepYear(param);
}
//controller:
@ApiOperation(value = "selectAllCityByCountry")
@RequestMapping(value = "/allResult",method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseDto selectResultByIndepYear(@RequestParam(value = "code") @ApiParam(value = "国家代码") String code){
Country bean = countryService.selectResultByIndepYear(code);
ResponseDto dto = new ResponseDto();
dto.setData(bean);
dto.setTimestamp(String.valueOf(System.currentTimeMillis()));
dto.setStatus("200");
return dto;
}
实际测试中,返回的结果如下:CHN的独立时间为-1512,USA的独立时间为1776,
指定code为CHN:
{
"status": "200",
"timestamp": "1525594968864",
"data": {
"code": "CHN",
"name": "China",
"continent": null,
"region": "Eastern Asia",
"surfacearea": 9572900,
"indepyear": 1949,
"population": 1277558000,
"lifeexpectancy": 71.4,
"gnp": 982268,
"gnpold": 917719,
"localname": "Zhongquo",
"governmentform": "People'sRepublic",
"headofstate": "Jiang Zemin",
"capital": 1891,
"code2": "CN",
"cities": [
{
"id": 1890,
"name": "Shanghai",
"countrycode": "CHN",
"district": "Shanghai",
"population": 9696300
},
{
"id": 1891,
"name": "Peking",
"countrycode": "CHN",
"district": "Peking",
"population": 7472000
}
......
}
返回结果只是展示了部分,可以看到实际返回的对象就是通过countryAndCity2指定的CollectionBean封装的,再来测试code=USA:
{
"status": "200",
"timestamp": "1525595092000",
"data": {
"code": "USA",
"name": "United States",
"continent": null,
"region": "North America",
"surfacearea": 9363520,
"indepyear": 1776,
"population": 278357000,
"lifeexpectancy": 77.1,
"gnp": 8510700,
"gnpold": 8110900,
"localname": "United States",
"governmentform": "Federal Republic",
"headofstate": "George W. Bush",
"capital": 3813,
"code2": "US"
}
}
实际返回的结果是通过BaseResultMap所指定的Country类进行封装的。
关于association、collection和discriminator的使用就演示到这里,测试中所使用的数据库可以通过点击world.sql下载