MyBatis-关联映射

没有配置环境的可以先看MyBatis入门
这是工具类MyBatis工具类

简介

        在实际开发中,我们操作数据库的时候会涉及到多张表,而在面向对象中也就会关联到对象与对象之间的关系。针对多张表的查询MyBatis提供了关联映射,可以很好的处理对象与对象之间的关联关系。

在关系型数据库下表和表之间有三种关联方式:

  • 一对一:比如一个人只有一张身份证,一张身份证代表一个人
    在这里插入图片描述
  • 一对多:比如一个部门有多个员工,而一个员工只能在一个部门
    在这里插入图片描述
  • 多对多:比如购物系统,一个订单可以拥有多个商品,而一个商品也可以在多个订单中
    在这里插入图片描述

一对一

准备两张表
一张人类表(tbl_user),一张身份证表(tbl_card)

CREATE TABLE `tbl_user` (
  `u_id` int NOT NULL AUTO_INCREMENT,
  `u_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `u_age` int DEFAULT NULL,
  `u_sex` char(255) COLLATE utf8_vietnamese_ci DEFAULT NULL,
  `u_c_id` int DEFAULT NULL,
  PRIMARY KEY (`u_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_vietnamese_ci;


CREATE TABLE `tbl_card` (
  `c_id` int NOT NULL AUTO_INCREMENT,
  `c_card` varchar(255) COLLATE utf8_vietnamese_ci DEFAULT NULL,
  PRIMARY KEY (`c_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_vietnamese_ci;

在这里插入图片描述
在这里插入图片描述
给两张表都加上数据

然后创建对应实体类、Dao层、Dao实现类、mapper文件

实体类

public class TblUser {
	
	private int id;
	private String name;
	private int age;
	private char sex;
	private TblCard tCard;
	//省略set/get
}
public class TblCard {
	
	private int cid;
	private String card;
	//省略set/get
}

Dao层接口

public interface TblUserDao {
	
	/**
	 * 根据主键id查询
	 * @param id
	 * @return
	 */
	TblUser selectId(int id);
}
public interface TblCardDao {
	
	/**
	 * 根据id查询
	 * @param id
	 * @return
	 */
	TblCard selectId(int id);
}

Dao层实现类

public class TblUserDaoImpl implements TblUserDao{
	
	//使用工具类 减少重复的代码
	private TblUserDao tblUserDao = (TblUserDao)SqlUtils.getMapper(TblUserDao.class);

	@Override
	public TblUser selectId(int id) {
		// TODO Auto-generated method stub
		return tblUserDao.selectId(id);
	}
}

mapper文件

<?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.dao.TblCardDao">

	<resultMap type="tblCard" id="cardMap">
		<id column="c_id" property="cid"/>
		<result column="c_card" property="card"/>
	</resultMap>

	<select id="selectId" parameterType="int" resultMap="cardMap">
		select * from tbl_card where c_id = #{id}
	</select>
</mapper>
<mapper namespace="com.dao.TblUserDao">
	
	<resultMap type="tblUser" id="usermap">
		<id property="id" column="u_id"/>
		<result property="name" column="u_name"/>
		<result property="age" column="u_age"/>
		<result property="sex" column="u_sex"/>
		<association property="tCard" column="u_c_id" 
		select="com.dao.TblCardDao.selectId" javaType="tblCard"/>
	</resultMap>

	<select id="selectId" parameterType="int" resultMap="usermap">
		select * from tbl_user where u_id=#{id}
	</select>
	
</mapper>

resultMap和resultType都是代表返回的类型,resultMap更细致一点可以自己编写想返回的字段。

resultMap元素中,包含了一个association子元素,MyBatis就是通过该元素来处理一对一关联关系的。
在元素中,通常可以配置以下属性:

  • property:指定映射到的实体类对象属性,与表字段一一对应;
  • column:指定表中对应的字段;
  • javaType:指定映射到实体对象属性的类型;
  • select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询;
  • fetchType:指定在关联查询时是否启用延迟加载。该属性有lazy和eager两个属性值,默认值为lazy(即默认关联映射延迟加载)。

测试和结果:
添加测试工具

	 <dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>

首先声明一个全局对象用来方便调用Dao层方法

private TblUserDao tblUserDao = new TblUserDaoImpl(); 
@Test
	public void test1(){
		//一对一
		TblUser tblUser = tblUserDao.selectId(2);
		System.out.println(tblUser.getName());
		System.out.println(tblUser.getTCard().getCard());
	}
小龙女
431025255553902117

出现人名跟身份证就说明成功了

一对多

也是先准备两张表
一张部门表(tb_dept),一张员工表(tb_emp)

CREATE TABLE `tb_dept` (
  `d_id` int NOT NULL AUTO_INCREMENT,
  `d_name` varchar(255) COLLATE utf8_vietnamese_ci DEFAULT NULL,
  PRIMARY KEY (`d_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_vietnamese_ci;

CREATE TABLE `tb_emp` (
  `e_id` int NOT NULL AUTO_INCREMENT,
  `e_name` varchar(255) COLLATE utf8_vietnamese_ci DEFAULT NULL,
  `d_id` int DEFAULT NULL,
  PRIMARY KEY (`e_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_vietnamese_ci;

在这里插入图片描述
在这里插入图片描述

创建对应实体类、Dao层、Dao实现类、mapper文件

实体类

public class Dept {
	
	private int id;
	private String name;
	private List<Emp> arrEmp;	//  一个部门有多个员工
	//省略set/get
}
public class Emp {
	
	private int id;
	private String name;
	private Dept edept;		//一个员工对应一个部门
	//省略set/get
}

Dao层接口(注意如果我们要通过edept去查询emp的话,应该调用selectdetpId,通过d_id来查询)

public interface DeptDao {
	
	Dept selectId(int id);
}
public interface EmpDao {
	
	Emp selectId(int id);
	
	Emp selectdetpId(int detpId);
	
}

Dao层实现类

public class DeptDaoImpl implements DeptDao{
	
	private DeptDao deptDao = (DeptDao)SqlUtils.getMapper(DeptDao.class);

	@Override
	public Dept selectId(int id) {
		// TODO Auto-generated method stub
		return deptDao.selectId(id);
	}

}
public class EmpDaoImpl implements EmpDao {
	
	private EmpDao EmpDao = (EmpDao)SqlUtils.getMapper(EmpDao.class);
	

	@Override
	public Emp selectId(int id) {
		// TODO Auto-generated method stub
		return EmpDao.selectId(id);
	}

	@Override
	public Emp selectdetpId(int detpId) {
		// TODO Auto-generated method stub
		return EmpDao.selectdetpId(detpId);
	}

}

mapper文件

<?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.dao.EmpDao">

	<resultMap type="emp" id="empMap">
		<id property="id" column="e_id"/>
		<result property="name" column="e_name"/>
		<association property="edept" column="d_id"
		select="com.dao.DeptDao.selectId"
		javaType="dept"
		/>		
	</resultMap>

	<select id="selectId" parameterType="int" resultMap="empMap">
		select * from tb_emp where e_id = #{id}
	</select>
	
	
	<resultMap type="emp" id="demp">
		<id property="id" column="e_id"/>
		<result property="name" column="e_name"/>
	</resultMap>
	<select id="selectdetpId" parameterType="int" resultMap="demp">
		select * from tb_emp where d_id = #{detpId}
	</select>

</mapper>
<?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.dao.DeptDao">
	
	<resultMap type="dept" id="deptMap">
		<id property="id" column="d_id"/>
		<result property="name" column="d_name"/>
		<collection property="arrEmp" column="d_id"
		 	select="com.dao.EmpDao.selectdetpId" 
		 	javaType="java.util.ArrayList"/>
	</resultMap>
	
	<select id="selectId" parameterType="int" resultMap="deptMap">
		select * from tb_dept where d_id = #{id}
	</select>
</mapper>

这里又有一个注意的地方,在通过dept去关联查询emp的同时,不能又反过来关联查询dept,这是一个无限递归逻辑,会造成内存溢出的(相当于A去找B,B又去找A,A再找B…)

这里同样是在collection利用select属性通过namesqpce去找到EmpMapper中相关的查询方法。

测试和结果:

	private EmpDao empDao = new EmpDaoImpl();
	private DeptDao deptDao = new DeptDaoImpl();
@Test
	public void test2(){
		//一对多
		Dept dept = deptDao.selectId(2);
		List<Emp> list = dept.getArrEmp();
		for (Emp emp : list) {
			System.out.println(emp.getName());
		}
		System.out.println(dept.getName());
	}
	
	@Test 
	public void test3(){
		Emp emp = empDao.selectId(2);
		System.out.println(emp.getName());
		System.out.println(emp.getDept().getName());
	}
李四
老刘
岂可
sb
科技部
李四
科技部

多对多

准备三张表
订单表(tb_order),商品表(tb_product),中间表(tb_order_product_relation)

CREATE TABLE `tb_order` (
  `o_id` int NOT NULL AUTO_INCREMENT,
  `o_name` varchar(255) COLLATE utf8_vietnamese_ci DEFAULT NULL,
  PRIMARY KEY (`o_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_vietnamese_ci;

CREATE TABLE `tb_product` (
  `p_id` int NOT NULL AUTO_INCREMENT,
  `p_name` varchar(255) COLLATE utf8_vietnamese_ci DEFAULT NULL,
  PRIMARY KEY (`p_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_vietnamese_ci;

CREATE TABLE `tb_order_product_relation` (
  `r_id` int NOT NULL AUTO_INCREMENT,
  `o_id` int DEFAULT NULL,
  `p_id` int DEFAULT NULL,
  PRIMARY KEY (`r_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_vietnamese_ci;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建对应实体类,Dao层,Dao实现类,mapper文件

public class Order {
	
	private int id;
	private String name;
	
	private List<Product> arrProduct;
	//省略set/get
}
public class Product {
	
	private int id;
	private String name;
	
	private List<Order> arrOrder;
	//省略set/get
}

Dao层

public interface ProductDao {
	
	Product selectOrderId(int id);
}
public interface OrderDao {
	
	Order selectProdcutId(int id);
}

Dao实现类

package com.dao.impl;

import com.action.Order;
import com.dao.OrderDao;
import com.utils.SqlUtils;

public class OrderDaoImpl implements OrderDao {

	private OrderDao orderDao = (OrderDao)SqlUtils.getMapper(OrderDao.class);
	
	@Override
	public Order selectProdcutId(int id) {
		// TODO Auto-generated method stub
		return orderDao.selectProdcutId(id);
	}

}

因为只做订单的测试所以我只实现了订单的Dao。

mapper文件

<?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.dao.ProductDao">

		<resultMap type="product" id="productMap">
			<id property="id" column="p_id"/>
			<result property="name" column="p_name"/>
		</resultMap>
		
		<select id="selectOrderId" parameterType="int" resultMap="productMap">
			select * 
			from tb_product p 
			LEFT JOIN tb_order_product_relation r on p.p_id = r.p_id
			LEFT JOIN tb_order o on o.o_id = r.o_id
			where o.o_id = #{id}
		</select>
	
</mapper>
<?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.dao.OrderDao">
		
	<resultMap type="order" id="orderMap">
		<id property="id" column="o_id"/>
		<result property="name" column="o_name"/>
		<collection property="arrProduct"	column="o_id"
		javaType="java.util.ArrayList"
		ofType="product"
		select="com.dao.ProductDao.selectOrderId"
		fetchType="lazy"
		/>
	</resultMap>
	
	<select id="selectProdcutId" parameterType="int" resultMap="orderMap">
		select * from tb_order where o_id = #{id}
	</select>
</mapper>

这里的javaType是ArrayList,如果想设置List的泛型,使用ofType属性。

上面我写的一个订单里面有多个商品,你们可以根据我上面写的来再写一个一个商品在多个订单里面,逻辑是一样的。

测试和结果:

private OrderDao orderDao = new OrderDaoImpl();
@Test
	public void test4(){
		//多对多
		Order order = orderDao.selectProdcutId(1);
		System.out.println(order.getName());
		for (Product product: order.getArrProduct()) {
			System.out.println(product.getName());
		}
	}
张三的订单
可乐
脉动
娃哈哈
安慕希

记得在配置文件上加上

<!-- 配置映射接口 -->
	<mappers>
		<mapper resource="mapper/UserMapper.xml"/>
		
		<mapper resource="mapper/TblUserMapper.xml"/>
		<mapper resource="mapper/TblCardMapper.xml"/>
		
		<mapper resource="mapper/DeptMapper.xml"/>
		<mapper resource="mapper/EmpMapper.xml"/>
		
		<mapper resource="mapper/OrderMapper.xml"/>
		<mapper resource="mapper/ProductMapper.xml"/>
	</mappers>

总结

在mybatis中,不管表与表之间有多复杂都能通过关联映射去获取结果。核心就是collectionassociation,记住以下记点:

  • 当一个对象关联一个对象的时候就用association,当一个对象关联多个对象的时候就用collection
  • 要注意的是当A对象去关联B对象的同时,B对象不能在关联A对象,不然会导致一直循环关联,会造成内存溢出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值