1.4(Mybatis学习笔记)关联映射


一、一对一

  mybatis处理一对一主要通过中的元素来处理。

  元素主要使用方方式有两种:  


<
association property = "card" column = "card_id" javaType = "com.mybatis.associateMapping.IdCard" select = "com.mybatis.mapper.IdCardMapper.findCodeById" />

<association property = "card" javaType = "com.mybatis.associateMapping.IdCard">
    
    
</association>

  首先我们构建两张表,表结构如下:

  tb_idcard

  tb_person

  建表语句:

CREATE TABLE `tb_idcard` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(18) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `tb_person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` varchar(8) DEFAULT NULL,
  `card_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `card_id` (`card_id`),
  CONSTRAINT `tb_person_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `tb_idcard` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

  tb_person表中的card_id与tb_card的id关联起来。

  现在通过id查询person信息,同时使用这个id查询对应card信息。

IdCard.java

public class IdCard {
    private Integer id;
    private String code;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    @Override
    public String toString() {
        return "IdCard [id=" + id + ", code=" + code + "]";
    }
}

Person.java

public class Person {
    private Integer id;
    private String name;
    private Integer age;
    private String sex;
    private IdCard card;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public IdCard getCard() {
        return card;
    }
    public void setCard(IdCard card) {
        this.card = card;
    }
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + ", card=" + card + "]";
    }
    
}

IdCardMapper.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 = "com.mybatis.mapper.IdCardMapper" >
        <select id = "findCodeById" parameterType = "Integer" resultType = "com.mybatis.associateMapping.IdCard">
            select * from tb_idcard where id = #{id}
        select>
    mapper>

PersonMapper.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 = "com.mybatis.mapper.PersonMapper" >
        <select id = "findPersonById" parameterType = "Integer" resultMap = "IdCardWithPersonResult">
            select * from tb_person where id = #{id}
        select>
        
        <resultMap type="com.mybatis.associateMapping.Person" id = "IdCardWithPersonResult">
            <id property="id" column="id" />
            <result property="name" column="name"/>
            <result property="age" column="age"/>
            <result property="sex" column="sex"/>
            <association property = "card" column = "card_id" javaType = "com.mybatis.associateMapping.IdCard"
                        select = "com.mybatis.mapper.IdCardMapper.findCodeById" />
        resultMap>
    mapper>
通过id查询的同时,resultMap中有一个嵌套查询会根据id查询idCard同时将查询的结果绑定到Person类的card属性上。

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 resource = "db.properties">
    properties>
    
    <typeAliases>
        <package name="com.mybatis.associateMapping"/>
    typeAliases>
    
    
    <environments default = "mysql">
    
        <environment id = "my">
        
            <transactionManager type = "JDBC"/>
            
            <dataSource type = "POOLED">
                <property name = "driver" value = "${jdbc.driver}" />
                <property name = "url" value = "${jdbc.url}" />
                <property name = "username" value = "${jdbc.username}" />
                <property name = "password" value = "${jdbc.password}" />
            dataSource>
        environment>
    environments>
    
    <mappers>
    
        <mapper resource = "com/mybatis/mapper/CustomerMapper.xml"/>
        
        <mapper resource = "com/mybatis/mapper/IdCardMapper.xml"/>
        <mapper resource = "com/mybatis/mapper/PersonMapper.xml"/>
    mappers>
configuration>


MyBatisSessionFactory.java (工具类,用于获取SqlSessionFactory)
import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisSessionFactory {
    public static SqlSessionFactory getSqlSessionFactory(String sourceFile) throws IOException {
        String resource = sourceFile;
        //获取配置文件输入流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过配置文件输入流构建sqlSessionFactory,
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"my");
        return sqlSessionFactory;
    }
}

测试:

import java.io.IOException;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.my.util.MyBatisSessionFactory;

public class TestMapper {
    public static void main(String[] args) throws IOException {
        SqlSessionFactory sqlSessionFactory = MyBatisSessionFactory.getSqlSessionFactory("mybatis-config.xml");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Person person = sqlSession.selectOne("com.mybatis.mapper.PersonMapper.findPersonById",1);
        System.out.println(person);
        sqlSession.commit();
        sqlSession.close();
    }
}

通过id查询person后,通过resultMap建立返回映射时,使用id查询code同时将返回的IdCard绑定到Person中的card属性上。 

上例是采用嵌套查询来实现的,下面我们使用第二种方法嵌套结果来实现。

只需修改PersonMapper.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 = "com.mybatis.mapper.PersonMapper" >
        <select id = "findPersonById" parameterType = "Integer" resultMap = "IdCardWithPersonResult">
            
            select p.*, idcard.code       
            from tb_person p,tb_idcard idcard
            where p.card_id = idcard.id and p.id = #{id}
        select>
        
        <resultMap type="com.mybatis.associateMapping.Person" id = "IdCardWithPersonResult">
            <id property="id" column="id" />
            <result property="name" column="name"/>
            <result property="age" column="age"/>
            <result property="sex" column="sex"/>
            
            <association property = "card" javaType =  "IdCard">
                <id property = "id" column = "id"/>   
                <result property = "code" column = "code" />
            association>
        resultMap>
    mapper>

 二、一对多

   mybatis处理一对一主要通过中的元素来处理。

   元素主要使用方方式有两种:  


<
collection property = "orderList" ofType = "com.mybatis.oneN.Order"> <id property = "id" column = "orders_id"/> <result property = "number" column = "number" /> collection>

首先创建两张表

tb_user

tb_orders

建表语句:

CREATE TABLE `tb_user` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL,
  `address` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `tb_orders` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `number` varchar(32) NOT NULL,
  `user_id` int(32) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  CONSTRAINT `tb_orders_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

orders中关联了用户id,即代表该商品被某一用户购买。

Order.java

public class Order {
    private Integer id;
    private String number;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    @Override
    public String toString() {
        return "Order [id=" + id + ", number=" + number + "]";
    }
}

User.java

import java.util.List;

public class User {
    private Integer id;
    private String username;
    private String address;
    private List orderList; //用户购物清单
    
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    public User(Integer id, String username, String address, List orderList) {
        super();
        this.id = id;
        this.username = username;
        this.address = address;
        this.orderList = orderList;
    }
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public List getOrderList() {
        return orderList;
    }
    
    public void setOrderList(List orderList) {
        this.orderList = orderList;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", address=" + address + ", orderList=" + orderList + "]";
    }
    
}

UserMapper.xml   (mybatis-config.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 = "com.mybatis.mapper.UserMapper" >
        <select id = "findUserWithOrders" parameterType = "Integer" resultMap = "OrdersWithUserResult">
            
            select u.*, o.id as orders_id,o.number
            from tb_user u,tb_orders o 
            where u.id = o.user_id  and u.id = #{id}
        select>
        
        <resultMap type="com.mybatis.oneN.User" id = "OrdersWithUserResult">
            <id property="id" column="id" />
            <result property="username" column="username"/>
            <result property="address" column="address"/>
            <collection property = "orderList" ofType =  "com.mybatis.oneN.Order">
                <id property = "id" column = "orders_id"/>
                <result property = "number" column = "number" />
            collection>
        resultMap>
    mapper>

将嵌套结果修改为嵌套调用

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">
    
    <mapper namespace = "com.mybatis.mapper.UserMapper" >
        <select id = "findUserWithOrders" parameterType = "Integer" resultMap = "OrdersWithUserResult">
            select * from tb_user where id = #{id}
        select>
        
        <resultMap type="com.mybatis.oneN.User" id = "OrdersWithUserResult">
            <id property="id" column="id" />
            <result property="username" column="username"/>
            <result property="address" column="address"/>
            <collection property = "orderList"  column = "id" ofType =  "com.mybatis.oneN.Order"
                select = "com.mybatis.mapper.OrdersMapper.findOrderByUserId"
                />
        resultMap>
    mapper>

OrdersMapper.xml


        <resultMap type="com.mybatis.oneN.Order" id="OrderOneNResult" >
            <id property = "id" column = "id"/>
            <result property = "number" column="nuber"/>
        resultMap>


        <select id = "findOrderByUserId" parameterType = "Integer" resultMap = "OrderOneNResult">
            select * from tb_orders where user_id = #{id}
        select>

三、多对多

  一个订单可以包含多个商品,同样一个商品也可以被多个订单包含。

  这种多对多关系一般需要一个中间表,用于关联两者。

    如上图所示,1号订单中有1,3号商品。3号商品被1,3号订单预定。两者之间通过一个中间表联系起来。

      现在我们想通过输入订单id查询该订单所包含的商品信息。例如查询一号商品订单。

    由于有一个中间表,所以我们先需要通过订单id查询中间表中Order_id为1对应的Product_id的值(1,3)

    然后通过Product_id中的(1,3)查询商品表中id为(1,3)的商品的详细信息。

  Product.java  (商品类,一个商品可以和多个订单关联)

import java.util.List;

public class Product {
    private Integer id;    //商品ID
    private String name;   //商品名称
    private Double price;  //商品价格
    private List orders; //一个商品中可包含多个订单
    public Product() {
        // TODO Auto-generated constructor stub
    }
    
    public Product(Integer id, String name, Double price, List orders) {
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.orders = orders;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
    public List getOrders() {
        return orders;
    }
    public void setOrders(List orders) {
        this.orders = orders;
    }

    @Override
    public String toString() {
        return "Product [id=" + id + ", name=" + name + ", price=" + price + "]";
    }
}

  Order.java(订单类,一个订单可以关联多个商品) 

import java.util.List;

public class Order {
    private Integer id;   //订单id
    private String number;//订单编号
    private List products; //一个订单关联多个商品
    
    public List getProducts() {
        return products;
    }
    public void setProducts(List products) {
        this.products = products;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    @Override
    public String toString() {
        return "Order [id=" + id + ", number=" + number + ", products=" + products + "]";
    }
}

  OrdersMapper.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 = "com.mybatis.mapper.OrdersMapper">
        
        <resultMap type="com.mybatis.MN.Order" id="OrdersWithProdecutResult">
            
            <id property = "id" column  = "id"/>
            <result property = "number" column = "number" />
            
            
            <collection property = "products" column = "id"
            ofType="com.mybatis.MN.Product"
                select = "com.mybatis.mapper.ProductMapper.findProductById">
            collection>
        resultMap>
        
        
        <select id="findOrdersWithProduct" parameterType = "Integer" 
        resultMap = "OrdersWithProdecutResult">
            select * from tb_orders where id = #{id}
        select>
        
    mapper>

该文件将订单id所对应的订单相关详细查询出来,并通过resultMap映射到Order类中,

Order对象中的 List products通过调用com.mybatis.mapper.ProductMapper.findProductById(ProductMapper.xml中)进行查询填充。

ProductMapper.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 = "com.mybatis.mapper.ProductMapper">
        <select id="findProductById" parameterType = "Integer" 
        resultType = "com.mybatis.MN.Product">
            
            select * from tb_product where id in (
                
                select product_id from tb_ordersitem where order_id = #{id}
            )
        select>
        
    mapper>

 该文件将订单id作为查询参数,查询中间表中order_id为1所对应的product_id。(例如订单id为1,查询商品id为1,3)

然后将查询的product_id(1,3)作为参数,查询tb_orders中对应商品详细信息。

这样就通过订单id查询到了该订单对应商品的id,并将商品填充到Order类中的List products属性中。

测试:

import java.io.IOException;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.my.util.MyBatisSessionFactory;

public class TestMapper {
    public static void main(String[] args) throws IOException {
        SqlSessionFactory sessionFactory = MyBatisSessionFactory.getSqlSessionFactory("mybatis-config.xml");
        SqlSession sqlSession = sessionFactory.openSession();
        Order order = sqlSession.selectOne("com.mybatis.mapper.OrdersMapper.findOrdersWithProduct", 1);
        System.out.println(order);
        sqlSession.commit();
        sqlSession.close();
    }
}

使用嵌套结果方式:

  OrdersMapper.xml

    <resultMap type="com.mybatis.MN.Order" id="OrdersWithProdecutResult">
            
            <id property = "id" column  = "id"/>
            <result property = "number" column = "number" />
            
            
            <collection property = "products" ofType="com.mybatis.MN.Product">
                <id property = "id" column = "pid"/>
                <result property = "name" column = "name"/>
                <result property = "price" column = "price"/>
            collection>
        resultMap>

    
        <select id="findOrdersWithProduct" parameterType = "Integer" 
        resultMap = "OrdersWithProdecutResult">
            select o.*,p.id as pid,p.name,p.price
            from tb_orders o, tb_product p, tb_ordersitem oi
            where oi.order_id = o.id
            and oi.product_id = p.id
            and o.id = #{id}
        select>