Mybatis实现一对一和一对多关系(案例版)

目录

1.  一对一关系和一对多关系

2.准备

2.1创建两张表

2.2 创建工程

2.3 创建实体类

2.4 创建Mybatis-config.xml文件

2.5 创建SessionFactory的Util类

3. 实现一对一

3.1 创建订单类的sql映射文件:OrderMapper.xml

3.2 创建接口类:OrderMapper

3.3 测试类:MapperTest

4.实现一对多


 

项目结构图

环境:

开发工具:Intellij idea 2018.3版本

JDK:1.8

数据库:Mysql 8.0.11

Mybatis:3.4.1

 

 

在讲案例前先明确什么是一对一,一对多。

1.  一对一关系和一对多关系

(以下来自于黑马程序员的视频笔记) 

  • 什么关系属于一对一

     一个身份证对应着一个公民,一个公民也对应着一张身份证

  • 一对一的建表原则:

两种对应方式:

唯一外键对应:在任意一方加一个外键,然后加个unique约束条件。本文中采用这种

主键对应:就是主键对主键

  • 什么样关系属于一对多?

   一个用户对应多个订单,一个订单只能属于一个用户

   一个客户对应多个联系人,一个联系人只能属于某一个客户。

  • 一对多的建表原则:

       在多的一方创建外键指向一的一方的主键

 


 这里用到的是两张表,订单表(Orders)和用户表(User)。一个订单对应一个用户,一个用户可以对应着多个订单。虽然这是一对多关系,但是如果从订单角度看,一个订单只能对应一个用户。勉强可以算作一对一,本文中为了缩减代码量,就直接用两个表演示一对一和一对多。下面进入案例。有些代码是重复的,因此得先把这些准备工作做了

2.准备

2.1创建两张表

  创建User表,插入三条数据

CREATE TABLE `user` (
                      `uid` int(11) NOT NULL AUTO_INCREMENT,
                      `username` varchar(32) NOT NULL COMMENT '用户名称',
                      `birthday` date DEFAULT NULL COMMENT '生日',
                      `sex` char(1) DEFAULT NULL COMMENT '性别',
                      `address` varchar(256) DEFAULT NULL COMMENT '地址',
                      PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=UTF8MB4;

INSERT INTO `user` VALUES ('1', '王五', null, '2', null);
INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('16', '张小明', null, '1', '河南郑州');

 

创建Orders表,插入两条数据

CREATE TABLE `orders` (
                        `id` int(11) NOT NULL AUTO_INCREMENT,
                        `user_id` int(11) NOT NULL COMMENT '下单用户id',
                        `number` varchar(32) NOT NULL COMMENT '订单号',
                        `createtime` datetime NOT NULL COMMENT '创建订单时间',
                        `note` varchar(100) DEFAULT NULL COMMENT '备注',
                        PRIMARY KEY (`id`),
                        KEY `FK_orders_1` (`user_id`),
                        CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=UTF8MB4;

INSERT INTO `orders` VALUES ('3', '1', '1000010', '2015-02-04 13:22:35', null);
INSERT INTO `orders` VALUES ('4', '1', '1000011', '2015-02-03 13:22:41', null);

2.2 创建工程

可以创建java工程也可以创建javaEE项目,反正测试使用Junit。具体如何创建可以参考我另外一篇文章https://blog.csdn.net/Vibugs/article/details/85246420

项目结构图

 

导入jar包:

 

2.3 创建实体类

创建Order实体类

根据一对一的实体类原则,在没有外键的一方实体类中包含外键一方的对象 , 因此Order类中要有User的对象

 

创建User类

 根据一对多的建立实体类的原则,在没有外键的一方(user)的实体类创建另一方的List集合(List<order>),因此 User类中带有order类的list集合。

 

2.4 创建Mybatis-config.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 通过properties导入外部配置文件-->
    <properties resource="MySql.properties"/>

    <!--配置别名
      * 1:可以直接设置别名;
      * 2:如果实体类的别名太多了,可以将实体类都放在一个包下,然后这里直接导入包就好了 
      -->
   <typeAliases>
       <typeAlias type="cn.svllen.mybatis.domain.User" alias="user"/>
      <!-- <package name="cn.svllen.mybatis.domain" /> -->

   </typeAliases>

    <!-- 和spring整合后 environment配置将废除-->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事务管理-->
            <transactionManager type="JDBC"/>

            <!-- 数据库连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>

            </dataSource>
        </environment>
    </environments>
    
    <!--引入map文件 -->
    <mappers>
        <mapper resource="cn/svllen/mybatis/domain/UserMapper.xml"/>
        <mapper resource="cn/svllen/mybatis/domain/OrderMapper.xml"/>
    </mappers>
</configuration>

MySql.properties配置文件

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false
jdbc.username=root
jdbc.password=12315

2.5 创建SessionFactory的Util类

package cn.svllen.mybatis.util;

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 java.io.InputStream;

/**
 * @program: MybatisDay01
 * @description: 创建SessionFactory的util类
 * @author: svllen
 * @create: 2019-03-23 10:52
 **/

public class SessionFactoryUtil {

    public static SqlSessionFactory sqlSessionFactory;

    static{
        try{
            InputStream inputStream = Resources.getResourceAsStream("Mybatis-config.xml");
            //构建工厂
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static SqlSession getCurrentSession(){
        return sqlSessionFactory.openSession();
    }
}

 

 

3. 实现一对一

需求:使用一对一的查找方式,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息。在sql映射文件中用resultMap标签输出

实现思路:根据一对一的建表原则,在任意一方(订单表)建立唯一外键指向另一方的主键,确定要查询的唯一订单唯一用户。再根据一对一建立实体类的原则,在没有外键的一方实体类中包含外键一方的对象,显示时通过这个对象获取到关联的用户信息。

实现过程:sql查询语句中设置两个条件:

    条件1 订单的外键user_id =用户的主键id 

    条件2: 订单的主键id=你要查询的值。

结果用resultMap结果集返回,类型是Order型,order实体类中有user对象,因此关联的用户信息就可以从里面获取到(即order.getUser()) 。实体类时关联了,但是映射的时候还是得指明关联关系,因此需要在resultMap标签中使用association标签指定。

 

3.1 创建订单类的sql映射文件:OrderMapper.xml

<?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="cn.svllen.mybatis.domain.OrderMapper">

   <!-- 使用一对一的查找方式,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息-->
    <select id="OneToOneByOrder"  parameterType="int" resultMap="oneToOneOrderResultMap">
        SELECT
            o.id as order_id,
            user_id, number, createtime, note,
            u.id as user_id,
            username, birthday, sex, address
        FROM orders o, user u WHERE o.user_id = u.id and o.id=#{id}

    </select>
    <!-- 使用resultMap输出结果-->
    <resultMap id="oneToOneOrderResultMap" type="cn.svllen.mybatis.domain.Order" >
        <!--要输出的Order的信息 -->
        <id column="order_id" property="id" />
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
        <!-- 一对一的关系映射,需要使用association标签
         * property:对象属性的名称
         * javaType:对象属性的类型
         * column:所对应的外键字段名称
         * select:使用另一个查询封装的结果
        -->
        <association property="user" javaType="User" column="user_id" resultMap="UserMap" />
    </resultMap>

    <!-- 将一对一的User抽出来,封装重用-->
    <resultMap id="UserMap" type="User">
        <!--要输出的User的信息 -->
        <id column="user_id" property="uId" javaType="int"  />
        <result column="username" property="username"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
    </resultMap>

</mapper>

 

select标签:

             1.我给两个表的主键id都起了别名,因为两个表的主键都是一样的名称,当你把结果映射到resultMap的时候会出现显示错误的bug。具体可查看我另一篇文章 https://blog.csdn.net/Vibugs/article/details/86540185

             2.我们的查询条件有两个,一个是通过order_id查询用户信息,第二是通过外键查询用户信息。因此在sql语句中查询条件是 o.user_id = u.id(即订单表中的user_id外键 = 用户表中的id主键)。但是要插入匹配符#{}的应该是订单的id主键,毕竟我们是通过订单id查询订单信息。

  • 属性:id的值就是OrderMapper接口类 的方法名,parameterType是输入的参数,这里是int型。resultMap:将结果以结果集的形式显示。
  • SQL语句:这里有两个点需要知道。

       

resultMap标签:

属性

id当前命名空间中的一个唯一标识,用于标识一个结果映射。
type类的完全限定名, 或者一个类型别名(就是决定resultMap的类型)
autoMapping如果设置这个属性,MyBatis将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)

字段

  • constructor - 用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联本身可以是一个 resultMap 元素,或者从别处引用一个
  • collection – 一个复杂类型的集合
    • 嵌套结果映射 – 集合本身可以是一个 resultMap 元素,或者从别处引用一个
  • discriminator – 使用结果值来决定使用哪个 resultMap
    • case – 基于某些值的结果映射
      • 嵌套结果映射 – case 本身可以是一个 resultMap 元素,因此可以具有相同的结构和元素,或者从别处引用一个

本例中两个resultMap的type不一样,select标签指定的oneToOneOrderResultMap是Order类型的,而association关联的UserMap是User类型的。那输出时怎么输出呢,是输出order对象还是user对象呢。其实是输出Order对象,我们在Order实体类中不是定义了user对象吗,可以直接通过order.getUser.getXxx()方法输出User信息。

 

association标签:

这个标签上面代码注释里面有对它几个属性的解释。在这里就不多说了

记得将OrderMapper.xml导入到Mybatis-config.xml文件中

 

 3.2 创建接口类:OrderMapper

package cn.svllen.mybatis.domain;


/**
 * @program: MybatisDay01
 * @description: orderMapper的接口类,用来跟OrderMapper.xml关联起来。里面的一个select标签(也可以回其他标签,insert,update)对应这里的一个方法
 * @author: svllen
 * @create: 2019-03-26 15:30
 **/

public interface OrderMapper {

    //使用一对一的查找方式,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息
    Order OneToOneByOrder(int orderId);

    
}

 

3.3 测试类:MapperTest

 
public class MapperTest {


/**
    * @Description: 一对一的测试,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息
    * @Param:
    * @return:
    * @Author: svllen
    * @Date: 2019/3/27
    */
    @Test
    public void OneToOneByOrderTest(){
        SqlSession sqlSession = SessionFactoryUtil.getCurrentSession();
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
        int orderId = 4;
        //查询结果
        Order order = orderMapper.OneToOneByOrder(orderId);
        //显示结果
        System.out.println("查询到的订单为:" + order);
        System.out.println("该订单用户为:" + order.getUser());
        sqlSession.commit();
        sqlSession.close();
    }

}

结果:

 

4.实现一对多

需求:通过输入用户id查询多个订单信息,用户信息也要返回

实现思路:根据一对多建表原则(在order创建外键指向user的主键),通过order的外键确定要查找的唯一用户至少一个的订单的关系,再根据一对多建实体类的原则,在user实体类中有order的List<order>集合,通过这个集合获取唯一用户下的所有订单。

过程很相似,直接附上步骤

创建userMapper接口类:

package cn.svllen.mybatis.domain;



import java.util.List;

/**
 * @program: MybatisDay01
 * @description: usermapper的接口类,实现Mapper动态代理开发。取代传统的Dao,DaoImpl这种开发模式。与userMapper形成关联
 * @author: svllen
 * @create: 2019-03-25 10:03
 **/

public interface UserMapper {

    /*为了跟mapper.xml文件产生关联,这里有四个原则
    * 1. 接口方法名等于id名
    * 2. 返回值类型与resultType的一样
    * 3. 方法的形参跟parameterType一样
    * 4. 在.xml文件中,命名空间的值是接口类的全路径
     */

   

    //测试一对多的关系,通过输入用户id查询多个订单信息,用户信息也要返回
    User oneToManyByUser(int userId);






}

 

创建userMapper.xml

<?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">
<!--写sql语句的 -->
<mapper namespace="cn.svllen.mybatis.domain.UserMapper">
 <!--一对多的测试,通过输入用户id查询多个订单信息,用户信息也要返回 -->
    <select id="oneToManyByUser" parameterType="int" resultMap="userResultMap">
        select
        o.id as order_id,
        user_id, number, createtime, note,
        u.id as user_id,
        username, birthday, sex, address
        FROM orders o ,user u  WHERE o.user_id = u.id and u.id=#{id}
    </select>

    <resultMap id="userResultMap" type="user">
        <id column="user_id" property="uId"/>
        <result column="username" property="username"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
        <!-- 一对多的关系。使用collection标签
           * property:指定集合属性的名(User实体类中list集合名)
           * ofType:指的是集合中元素的值,一般是类的完全限定名
        -->
        <collection property="orderList" ofType="order" resultMap="orderResultMap" />
    </resultMap>

    <!--可重用的order的resultMap -->
    <resultMap id="orderResultMap" type="order">
        <id column="order_id" property="id" />
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
    </resultMap>

</mapper>

 

创建测试方法:

 /**
    * @Description: 一对多的测试, 通过输入用户id查询多个订单信息,用户信息也要返回
    * @Param:
    * @return:
    * @Author: svllen
    * @Date: 2019/3/29
    */
    @Test
    public void oneToManyByUserTest(){
        SqlSession sqlSession = SessionFactoryUtil.getCurrentSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //设置查询条件
        int userId = 16;
        User user = userMapper.oneToManyByUser(userId);
        System.out.println("你查到的用户信息为:" + user +"\n该用户下的订单有:");
        List<Order> orderList = user.getOrderList();
        for (Order order : orderList){
            System.out.println(order);
        }
        sqlSession.commit();
        sqlSession.close();
    }

 

测试结果:

 

结束语:希望各位点赞 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值