mybatis中association、collection和discriminator的使用

在使用Mybatis中定义resultMap的时候,经常会遇到这三个比较常用的标签association、collection和discriminator,本篇博客就在上两遍博客的基础上,讲解如何使用这个三个标签。

下面依次进行介绍,首先看一下association:
先看一下两个不同的resultMap定义:

<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"/>
    </resultMap>
    <resultMap 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"/>
    </resultMap>
    <resultMap 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" />
        <association property="city" resultMap="city"/>
    </resultMap>

其实countryAndCity和countryAndCity1在实际对返回结果集的表示上是一致,都是对结果的封装,只是countryAndCity1将City表中的字段抽取出来进行封装成city对象,并将其作为一个属性存放在AssociationBean1对象中。
而countryAndCity只是将查询语句返回的所有字段映射为一个对象的内部属性,并没有对部分属性进行二次封装。类似的这种封装在联表查询中经常会遇到这种情况,下载看一下代码,以及完成的mapper语句

    <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"/>
    </resultMap>
    <resultMap 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" />
        <association property="city" resultMap="city"/>
    </resultMap>
    <select id="selectCountryAndCity" parameterType="map" resultMap="countryAndCity1">
        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} AND city.name=#{name}

    </select>

相关的service和controller代码:

//service:
public AssociationBean1 selectCountryAndCity(String code, String name){
        Map<String,String> param = new HashMap<String, String>();
        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标签,具体用法如下:

    <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"/>
    </resultMap>
    <resultMap 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" />
        <collection javaType="list" resultMap="city" property="cities"/>
    </resultMap>

使用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<String,String> param = new HashMap<String, String>();
        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,具体的使用如下:

    <!--判断返回的独立时间,如果独立时间为-1512,则使用countryAndCity2,如果为1776则使用BaseResultMap-->
    <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之间的区别如下:

<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" />
  </resultMap>
    <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"/>
    </resultMap>
    <resultMap 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" />
        <collection javaType="list" resultMap="city" property="cities"/>
    </resultMap>

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<String,String> param = new HashMap<String, String>();
        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下载

猜你喜欢

转载自blog.csdn.net/u011043551/article/details/80215639