飘易博客(作者:Flymorn)
订阅《飘易博客》RSS,第一时间查看最新文章!
飘易首页 | 留言本 | 关于我 | 订阅Feed

Springboot+mybatis的association、collection的使用&只返回部分字段

Author:飘易 Source:飘易
Categories:JAVA开发 PostTime:2019-12-23 17:37:07
正 文:

在java项目中,某些实体类之间肯定有关键关系,比如一对一,一对多等。在hibernate 中用one to one和one to many,而mybatis 中就用association和collection。

【association】: 一对一关联(has one)

【collection】:一对多关联(has many)

注意,只有在做select查询时才会用到这两个标签,都有三种用法,且用法类似。


一、association的三种用法

先看如下代码(省略set、get方法):

public class User {
    private Integer userId;
    private String userName;
    private Integer age;
    private Card card;//一个人一张身份证,1对1
}


public class Card {
    private Integer cardId;
    private String cardNum;//身份证号
    private String address;//地址
}
public interface UserDao {
    /**
     * 通过userId查询user信息
     * @param userId
     * @return
     */
    User queryById(int userId);
}
 <select id="queryById" parameterType="int" resultMap="userMap">
        SELECT u.user_name,u.age,c.card_id,c.card_num,c.address
        FROM tb_user u,tb_card c
        WHERE u.card_id=c.card_id
        AND
        u.user_id=#{userId}
</select>

以上是实体类、dao层的设计以及在UserDao.xml中queryById方法的sql语句的编写,因为不论用association的哪种方式,sql语句都是一样的写,不同的只是userMap的写法,所以这里先给出这段代码。User询Card是一对一关系,在数据库中,tb_user表通过外键card_id关联tb_card表。下面分别用association的三种用法来实现queryById方法。


1、第一种用法:association中使用select

这种方法需要再定义CardDao.java,如下:

public interface CardDao {
    Card queryCardById(int cardId);
}

在CardDao.xml中实现该方法:

 <select id="queryCardById" parameterType="int" resultType="Card">
        SELECT *
        FROM tb_card
        WHERE card_id=#{cardId}
 </select>

然后再看UserDao.xml中是如何引用这个方法的:

<resultMap type="User" id="userMap">
    <result property="userName" 
         column="user_name"/>
    <result property="age" 
         column="age"/>
   <association property="card" 
         column="card_id" 
         select="com.zhu.ssm.dao.
         CardDao.queryCardById">
   </association>
</resultMap>

在这里直接通过select引用CardDao的queryById方法。个人感觉这种方法比较麻烦,因为还要在CardDao里定义queryCardById方法并且实现再引用才有用,不过这种方法思路清晰,易于理解。


2、第二种方法,嵌套resultMap

<resultMap type="Card" id="cardMap">
      <id property="cardId" 
            column="card_id"/>
      <result property="cardNum" 
                   column="card_num"/>
      <result property="address" 
                   column="address"/>
</resultMap>


<resultMap type="User" id="userMap">
     <result property="userName" 
                  column="user_name"/>
     <result property="age" 
                  column="age"/>
     <association property="card" 
                             resultMap="cardMap">
     </association>
</resultMap>

第二种方法就是在UserDao.xml中先定义一个Card的resultMap,然后在User的resultMap的association标签中通过resultMap="cardMap"引用。这种方法相比于第一种方法较为简单。


3、第三种方法:嵌套resultMap简化版

<resultMap type="User" id="userMap">
   <result property="userName" 
                  column="user_name"/>
   <result property="age" 
                  column="age"/>
   <association 
          property="card"
          column="card_id" 
          javaType="Card">
      <id property="cardId" 
                column="card_id"/>
      <result property="cardNum"
                       column="card_num"/>
      <result property="address" 
                       column="address"/>
    </association>
</resultMap>

这种方法就把Card的resultMap定义在了association 标签里面,通过javaType来指定是哪个类的resultMap,个人认为这种方法最简单,缺点就是cardMap不能复用。具体用哪种方法,视情况而定。


二、collection的三种用法:

一个土豪有多个手机,看如下代码:

User实体类

public class User{
  private Integer userId;
    private String userName;
    private Integer age;
    private List<MobilePhone> mobilePhone;//土豪,多个手机,1对多
}

手机类

public class MobilePhone {
    private Integer mobilePhoneId;
    private String brand;//品牌
    private double price;//价格
    private User user;//主人
}

dao层

public interface UserDao {
    /**
     * 通过userId查询user信息
     * @param userId
     * @return
     */
    User queryById(int userId);
}

UserDao.xml中的select查询语句

<select id="queryById" parameterType="int" resultMap="userMap">
        SELECT u.user_name,u.age,
                       m.brand,m.price
        FROM tb_user u,tb_mobile_phone m
        WHERE m.user_id=u.user_id
        AND
        u.user_id=#{userId}
</select>

数据库中,tb_mobile_phone中user_id作为外键。那么下面来看resultMap如何定义:


1、第一种方法:用select,跟association 中使用select类似:

先定义 MobilePhoneDao.java

public interface MobilePhoneDao {
    List<MobilePhone> queryMbByUserId(int userId);
}

然后实现该方法 MobilePhoneDao.xml

<resultMap type="MobilePhone"
                    id="mobilePhoneMap">
     <id property="mobilePhoneId" 
           column="user_id"/>
     <result property="brand" 
                  column="brand"/>
     <result property="price" 
                  column="price"/>
     <association property="user" 
               column="user_id" select=
      "com.zhu.ssm.dao.UserDao.queryById">
     </association>
</resultMap>
<select id="queryMbByUserId" parameterType="int" resultMap="mobilePhoneMap">
        SELECT brand,price
        FROM tb_mobile_phone
        WHERE user_id=#{userId}
</select>

做好以上准备工作,那就可以在UserDao.xml中引用了

<resultMap type="User" id="userMap">
        <id property="userId" column="user_id"/>
        <result property="userName" 
                     column="user_name"/>
    <result property="age" 
                     column="age"/>
    <collection property="mobilePhone" 
                column="user_id" 
                select="com.zhu.ssm.dao
       .MobilePhoneDao.queryMbByUserId">
        </collection>
</resultMap>

 

这种方法和association的第一种用法几乎是一样的不同之处就是mobilePhMap中用到了association ,queryMbByUserId中要使用mobilePhoneMap,而不能直接使用resultType。


2、第二种方法:嵌套resultMap

<resultMap type="MobilePhone" id="mobilephoneMap">
         <id column="mobile_phone_id" property="mobilePhoneId"/>
         <result column="brand" property="brand" />
         <result column="price" property="price" /></resultMap>
    <resultMap type="User" id="userMap">
        <result property="userName" column="user_name"/>
        <result property="age" column="age"/>
        <collection property="mobilePhone" resultMap="mobilephoneMap" >
        </collection>
</resultMap>

定义好这两个resultMap,再引用UserMap就行了。


3、第三种方法:嵌套resultMap简化版

<resultMap type="User" id="userMap">
        <result property="userName" column="user_name"/>
     <result property="age" column="age"/>
     <collection property="mobilePhone"
                  column="user_id" 
                 ofType="MobilePhone">
      <id column="mobile_phone_id" property="mobilePhoneId" />
      <result column="brand" 
                       property="brand" />
      <result column="price"
                      property="price" />
 </collection>
</resultMap>

这种方法需要注意,一定要有ofType,collection 装的元素类型是啥ofType的值就是啥,这个一定不能少。


注意:

所有resultMap中的type、select 标签中的resultType以及association中的javaType,collection中的ofType,这里只写了类名,是因为在mybatis-config.xml中配置了typeAliases,否则就要写该类的全类名。配置如下:

<typeAliases>
<packagename="com.zhu.smm.entity"/>
</typeAliases>

或者在 文件里配置:

mybatis.type-aliases-package=com.example.demo


三、关联关系中只返回部分字段

如果我们希望在关联关系中只返回某个模型的部分字段,譬如用户在密码字段不希望返回,或者模型有上百个字段,不希望全部返回所有的字段给前端,那么怎么办呢? 


这个时候,飘易建议大家可以使用 Map 类型,直接返回给前端。


新闻表 News (其中字段cat_id关联到新闻分类表的id)

新闻分类表 NewsCategories

 

News模型中添加:

    /* 自定义属性 */
    private NewsCategories cat;
    public NewsCategories getCat(){ return cat; }
    public void setCat(NewsCategories cat){ this.cat = cat; }
    /* 自定义属性 - 部分字段 */
    private Map catMap;
    public Map getCatMap(){ return catMap; }
    public void setCatMap(Map catMap){ this.catMap = catMap; }


NewsMapper.xml中添加:

  <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.example.demo.model.News">
    <result column="content" jdbcType="LONGVARCHAR" property="content" />
    <!-- 一对一关联关系 -->
    <association property="cat" column="cat_id" select="cat"/>
    <association property="catMap" column="cat_id" select="catMap"/>
  </resultMap>
  <select id="cat" parameterType="java.lang.Integer" resultType="com.example.demo.model.NewsCategories" >
    select * from news_categories where id = #{id,jdbcType=INTEGER}
  </select>
  <select id="catMap" parameterType="java.lang.Integer" resultType="java.util.Map" >
    select id,title,keywords from news_categories where id = #{id,jdbcType=INTEGER}
  </select>


NewsCategories模型中添加:

    // 自定义
    private List<News> newsList;
    public List<News> getNewsList(){ return newsList; }
    public void setNewsList(List<News> newsList){ this.newsList = newsList; }
    // 自定义 - Map 部分字段
    private List<Map> newsMapList;
    public List<Map> getNewsMapList(){ return newsMapList; }
    public void setNewsMapList(List<Map> newsMapList){ this.newsMapList = newsMapList; }


NewsCategoriesMapper.xml 中添加:

  <resultMap id="BaseResultMap" type="com.example.demo.model.NewsCategories">
      ...
    <!--一对多关联关系-->
    <collection property="newsList" column="id" select="queryNewsList"/>
    <collection property="newsMapList" column="id" select="queryNewsMapList"/>
  </resultMap>
  <select id="queryNewsList" parameterType="int" resultType="com.example.demo.model.News">
    SELECT * FROM news WHERE cat_id=#{id}
  </select>
  <select id="queryNewsMapList" parameterType="int" resultType="java.util.Map">
    SELECT id,title,keywords FROM news WHERE cat_id=#{id}
  </select>


这样配置后,我们就可以通过 java.util.Map 和 select 语句筛选出指定的一部分字段啦。



总结:

1、association表示的是has one的关系,一对一时使用。user has one card,所以在user的resultMap中接收card时应该用association;

2、collection表示的是has many的关系,一对多时使用。user has many mobilePhone,所以在user的resultMap中接收mobilePhone时应该用collection 。

3、都有三种用法,且非常类似,resultMap要复用建议第二种方法,不需要复用建议第三种方法。

4、特别注意表中主键字段要有所区分,不能都写成id,要写成user_id、card_id,反正要有所区分,不然查询的时候会查不到完整的数据。



【参考】

1、mybatis的association以及collection的用法


作者:飘易
来源:飘易
版权所有。转载时必须以链接形式注明作者和原始出处及本声明。
上一篇:没有了
下一篇:微信小程序开发之授权权限管理
0条评论 “Springboot+mybatis的association、collection的使用&只返回部分字段”
No Comment .
发表评论
名称(*必填)
邮件(选填)
网站(选填)

记住我,下次回复时不用重新输入个人信息
© 2007-2019 飘易博客 Www.Piaoyi.Org 原创文章版权由飘易所有