没有配置环境的可以先看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中,不管表与表之间有多复杂都能通过关联映射去获取结果。核心就是collection和association,记住以下记点:
- 当一个对象关联一个对象的时候就用association,当一个对象关联多个对象的时候就用collection
- 要注意的是当A对象去关联B对象的同时,B对象不能在关联A对象,不然会导致一直循环关联,会造成内存溢出。