• mybatis标签之——关联映射


    关联关系是面向对象分析、面向对象设计最重要的知识。合理的关联映射将大大简化持久层数据的访问。关联关系大致分为以下三类:

    • 一对一
    • 一对多
    • 多对多

    1. 一对一

    一对一关系推荐使用唯一主外键关联,即两张表使用外键关联关系,由于是一对一关联,因此还需要给外键列增加unique唯一约束。

    -- 身份证表
    create table tb_card {
    id int primary key auto_increment,
    code varchar(18)
    };
    insert into tb_card (code) values('370782199502124589');
    -- 个人表 create table tb_person { id int primary key auto_increment, name varchar(18), sex varchar(18), age int, card_id int unique, --card_id 唯一约束,且作为外键 foreign key (card_id) references tb_card (id) };
    insert into tb_person (name,sex,age,card_id) values('jack','',23,1);

    代码中创建Card对象和Person的对象(此处省略get/set方法)

    public class Card implements Serializable {
        
        private Integer id;  // 主键id
        private String code; // 身份证编号
        ···
    }            
    public class Person implements Serializable {
    
        private Integer id;  // 主键id
        private String name; // 姓名
        private String sex;  // 性别
        private Integer age; // 年龄
        
        // 人和身份证是一对一的关系,即一个人只有一个身份证,在Person类中定义card属性,来映射一对一关系
        private Card card; 
        ···
    }

     映射xml文件如下

    CardMapper.xml

    <!-- namespace指用户自定义的命名空间。 -->
    <mapper namespace="org.zang.mapper.CardMapper">
        <!-- 根据id查询Card,返回Card对象 -->
      <select id="selectCardById" parameterType="int" resultType="org.zang.domain.Card">
          SELECT * from tb_card where id = #{id} 
      </select>
    </mapper>

    PersonMapper.xml

    <mapper namespace="org.zang.mapper.PersonMapper">
     <!-- 映射Peson对象的resultMap -->
        <resultMap type="org.zang.domain.Person" id="personMapper">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="sex" column="sex"/>
            <result property="age" column="age"/>
    
            <!-- 一对一关联映射:association   -->
            <association property="card" column="card_id"
            select="org.zang.mapper.CardMapper.selectCardById" 
            javaType="org.zang.domain.Card"/>
        </resultMap>
    
     <!-- 根据id查询Person,返回resultMap -->
      <select id="selectPersonById" parameterType="int" 
          resultMap="personMapper">
          SELECT * from tb_person where id = #{id} 
      </select>
    </mapper>

    可以看到,personMapper中使用<association.../>元素来映射一对一的关联关系。

    2. 一对多

    数据库中一对多关系通常使用主外键关联,外键列应该在多方,即多方维护关系。

    -- 班级表 (一)
    create table tb_clazz(
    id int primary key auto_increment,
    code varchar(18),
    name varchar(18)
    );
    insert into tb_clazz(code,name) values('j1601','java就业班'); -- 学生表 (多) create table tb_student( id int primary key auto_increment, name varchar(18), sex varchar(18), age int, clazz_id int, --clazz_id 作为外键参照tb_clazz的主键id foreign key (clazz_id) references tb_clazz(id) );
    insert into tb_student(name,sex,age,clazz_id) values('jack','',23,1); insert into tb_student(name,sex,age,clazz_id) values('rose','',18,1); insert into tb_student(name,sex,age,clazz_id) values('tom','',21,1); insert into tb_student(name,sex,age,clazz_id) values('alice','',20,1);

    代码中创建Clazz对象和Student的对象(此处省略get/set方法)

    public class Clazz implements Serializable {
        
        private Integer id; // 班级id,主键
        private String code; // 班级编号
        private String name; // 班级名称
        
        // 班级和学生是一对多的关系,即一个班级可以有多个学生
        private List<Student> students;
       ···
    }
    public class Student implements Serializable {
    
        private Integer id; // 学生id,主键
        private String name; // 姓名
        private String sex;  // 性别
        private Integer age; // 年龄
        
        // 学生和班级是多对一的关系,即一个学生只属于一个班级
        private Clazz clazz;
       ···
    }

    映射文件如下

    ClazzMapper.xml

    <mapper namespace="org.zang.mapper.ClazzMapper">
    
        <!-- 映射Clazz对象的resultMap -->
        <resultMap type="org.zang.domain.Clazz" id="clazzResultMap">
            <id property="id" column="id"/>
            <result property="code" column="code"/>
            <result property="name" column="name"/>
            
            <!-- 一对多关联映射:collection fetchType="lazy"表示懒加载  -->
            <collection property="students" javaType="ArrayList" column="id" ofType="org.zang.domain.Student" 
            select="org.zang.mapper.StudentMapper.selectStudentByClazzId" fetchType="lazy">
              <id property="id" column="id"/>
              <result property="name" column="name"/>
              <result property="sex" column="sex"/>
              <result property="age" column="age"/>
          </collection>
        </resultMap>
        
        <!-- 根据id查询班级信息,返回resultMap -->
          <select id="selectClazzById" parameterType="int" resultMap="clazzResultMap">
              SELECT * FROM tb_clazz  WHERE id = #{id}
          </select>
    
    </mapper>

    由于student属性是一个List集合,所有clazzResultMap中使用了<collection.../>元素映射一对多的关联关系。

    此外还通过 fetchType="lazy" 来设置懒加载。fatch机制更多的是为了性能考虑,如果查询班级时确认会访问班级的所有学生,则该属性应该设置为eager;否则应该设置为lazy;正常情况下,一对多所关联的集合对象,都应该被设置成lazy。

    使用懒加载还应该在mybatis-config.xml中增加如下配置

    <settings>
    <!-- 要使延迟加载生效必须配置下面两个属性 -->
         <setting name="lazyLoadingEnabled" value="true"/>
         <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    StudentMapper.xml

    <mapper namespace="org.zang.mapper.StudentMapper">
    
    <!-- 映射Student对象的resultMap -->
        <resultMap type="org.zang.domain.Student" id="studentResultMap">
            <id property="id" column="id"/>
              <result property="name" column="name"/>
              <result property="sex" column="sex"/>
              <result property="age" column="age"/>
    <!-- 多对一关联映射:association --> <association property="clazz" javaType="org.zang.domain.Clazz"> <id property="id" column="id"/> <result property="code" column="code"/> <result property="name" column="name"/> </association> </resultMap> <!-- 根据id查询学生信息,多表连接,返回resultMap --> <select id="selectStudentById" parameterType="int" resultMap="studentResultMap"> SELECT * FROM tb_clazz c,tb_student s WHERE c.id = s.clazz_id AND s.id = #{id} </select> <!-- 根据班级id查询学生信息,返回resultMap --> <select id="selectStudentByClazzId" parameterType="int" resultMap="studentResultMap"> SELECT * FROM tb_student WHERE clazz_id = #{id} </select> </mapper>

    studentResultMap中使用<association.../>元素映射多对一的关联关系。因为<select id="selectStudentById".../>的sql语句是一条多表连接,关联tb_clazz表的同时查询了班级数据,所以<association.../>只是简单的装载数据。

    tips:在实际开发中,由于一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载;而多对一只是关联到一个对象,所以通常使用多表连接直接提取出数据。

    3. 多对多

    在一个购物系统中,一个用户可以有多个订单,这是一对多的关系;一个订单中可以有多种商品,一种商品也可以属于多个不同的订单,订单和商品就是多对多的关系。

    对于数据库中多对多的关系建议使用一个中间表来维护关系,中间表中的订单id作为外键参照订单表的id,商品id作为外键参照商品表的id。

    -- 用户表
    create table tb_user(
    id int primary key auto_increment,
    username varchar(18),
    loginname varchar(18),
    password varchar(18),
    phone varchar(18),
    address varchar(18)
    );
    insert into tb_user(username,loginname,password,phone,address) values('杰克','jack','123456','13920001616','广州'); -- 商品表 create table tb_article( id int primary key auto_increment, name varchar(18), price double, remark varchar(18) ); insert into tb_article(name,price,remark) values('疯狂java讲义',108.9,'李刚老师经典著作'); insert into tb_article(name,price,remark) values('疯狂android讲义',99.9,'李刚老师经典著作'); insert into tb_article(name,price,remark) values('疯狂ios讲义',89.9,'李刚老师经典著作'); insert into tb_article(name,price,remark) values('springmvc+mybatis企业开发',69.9,'肖文吉老师经典著作'); -- 订单表 create table tb_order( id int primary key auto_increment, code varchar(32), total double, user_id int, foreign key (user_id) references tb_user(id) ); insert into tb_order(code,total,user_id) values('6aa3fa359ff14619b77fab5990940a2d',388.6,1); insert into tb_order(code,total,user_id) values('6aa3fa359ff14619b77fab5990940b3c',217.8,1); --创建订单表 create table tb_item( order_id int, article_id int, amount int, primary key(order_id,article_id), foreign key (order_id) references tb_order(id), foreign key (article_id) references tb_article(id) ); insert into tb_item(order_id,article_id,amount) values(1,1,1); insert into tb_item(order_id,article_id,amount) values(1,2,1); insert into tb_item(order_id,article_id,amount) values(1,3,2); insert into tb_item(order_id,article_id,amount) values(2,4,2); insert into tb_item(order_id,article_id,amount) values(2,1,1);

    tb_order表的user_id作为外键参照tb_user表的主键id。tb_item表作为中间表,用来维护tb_article和tb_order的多对多关系,tb_item表的order_id作为外键参照tb_order表的主键id,article_id作为外键参照tb_article表的主键id。

    代码中创建User对象、Order对象和Article的对象(此处省略get/set方法)

    public class User implements Serializable{
        
        private Integer id;  // 用户id,主键
        private String username;  // 用户名
        private String loginname; // 登录名
        private String password;  // 密码
        private String phone;    // 联系电话
        private String address;  // 收货地址
        
        // 用户和订单是一对多的关系,即一个用户可以有多个订单
        private List<Order> orders;
       ···
    }
    public class Order implements Serializable {
    
        private Integer id;  // 订单id,主键
        private String code;  // 订单编号
        private Double total; // 订单总金额
        
        // 订单和用户是多对一的关系,即一个订单只属于一个用户
        private User user;
        
        // 订单和商品是多对多的关系,即一个订单可以包含多种商品
        private List<Article> articles;
      ···
    }
    public class Article implements Serializable {
        
        private Integer id;        // 商品id,主键
        private String name;    // 商品名称
        private Double price;    // 商品价格
        private String remark;    // 商品描述
        
        // 商品和订单是多对多的关系,即一种商品可以包含在多个订单中
        private List<Order> orders;
       ···
    }

    映射文件如下

    UserMapper.xml

    <mapper namespace="org.zang.mapper.UserMapper">
    
        <resultMap type="org.zang.domain.User" id="userResultMap">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="loginname" column="loginname"/>
            <result property="password" column="password"/>
            <result property="phone" column="phone"/>
            <result property="address" column="address"/>
    <!-- 一对多关联映射:collection --> <collection property="orders" javaType="ArrayList" column="id" ofType="org.zang.domain.User" select="org.zang.mapper.OrderMapper.selectOrderByUserId" fetchType="lazy"> <id property="id" column="id"/> <result property="code" column="code"/> <result property="total" column="total"/> </collection> </resultMap> <select id="selectUserById" parameterType="int" resultMap="userResultMap"> SELECT * FROM tb_user WHERE id = #{id} </select> </mapper>

    OrderMapper.xml

    <mapper namespace="org.zang.mapper.OrderMapper">
    
        <resultMap type="org.zang.domain.Order" id="orderResultMap">
            <id property="id" column="oid"/>
              <result property="code" column="code"/>
              <result property="total" column="total"/>
            <!-- 多对一关联映射:association   -->
            <association property="user" javaType="org.zang.domain.User">
                <id property="id" column="id"/>
                <result property="username" column="username"/>
                <result property="loginname" column="loginname"/>
                <result property="password" column="password"/>
                <result property="phone" column="phone"/>
                <result property="address" column="address"/>
            </association>
            <!-- 多对多映射的关键:collection   -->
            <collection property="articles" javaType="ArrayList" column="oid" ofType="org.zang.domain.Article"
          select="org.zang.mapper.ArticleMapper.selectArticleByOrderId" fetchType="lazy">
              <id property="id" column="id"/>
              <result property="name" column="name"/>
              <result property="price" column="price"/>
              <result property="remark" column="remark"/>
          </collection>
        </resultMap>
        
        <!-- 注意,如果查询出来的列同名,例如tb_user表的id和tb_order表的id都是id,同名,需要使用别名区分 -->
      <select id="selectOrderById" parameterType="int" resultMap="orderResultMap">
          SELECT u.*,o.id AS oid,CODE,total,user_id
           FROM tb_user u,tb_order o
          WHERE u.id = o.user_id
           AND o.id = #{id}
      </select>
      
      <!-- 根据userid查询订单 -->
      <select id="selectOrderByUserId" parameterType="int" resultType="org.zang.domain.Order">
          SELECT * FROM tb_order WHERE user_id = #{id}
      </select>
      
    </mapper>

    ArticleMapper.xml

    <mapper namespace="org.zang.mapper.ArticleMapper">
      
      <select id="selectArticleByOrderId" parameterType="int" resultType="org.zang.domain.Article">
          SELECT * FROM tb_article WHERE id IN ( 
            SELECT article_id FROM tb_item WHERE order_id = #{id} 
        ) 
      </select>
      
    </mapper>

    相关映射的含义上面已解释,关联映射最主要的是能够用好如下两个标签:

    • *  对多关联映射:collection
    • *  对一关联映射:association 

    此外注意:一对多使用的都是lazy(懒加载)。

  • 相关阅读:
    python中__init__和__call__的区别
    scrapy在start_requests中传入参数给parser
    crawl: error: Unrecognized output format 'json''
    判断一个点是否在多边形区域内部 / 判断一个给定位置是否位于某个城市内部
    scrapy爬取页面不完全的解决办法
    module 'tensorflow._api.v2.train' has no attribute 'rmspropoptimizer'
    tf.image.resize处理后的图片无法使用plt.imshow正常显示
    如何将tensor大于某个值为1,小于某个值为0
    Blas GEMV launch failed: m=3, n=10000
    sql 批量插入 insert
  • 原文地址:https://www.cnblogs.com/zjfjava/p/8911238.html
Copyright © 2020-2023  润新知