一、关联关系 1、关联关系概念说明 一对一
夫妻关系,人和身份证号
一对多
用户和用户的订单,锁和钥匙
多对多
老师和学生,部门和员工
2、创建模型 ① 创建实体类 1 2 3 4 5 public class Customer { private Integer customerId; private String customerName; private List<Order> orderList;
1 2 3 4 5 public class Order { private Integer orderId; private String orderName; private Customer customer;
双向关联关系:双方都能够引用到对方
Customer 中能够引用 Order
Order 中能够引用 Customer
在双向关联关系中使用 toString()等方法时注意避免无限死循环。
单向关联关系:双方中只有一方能够引用到对方
Customer 中没有引用 Order
Order 中引用了 Customer
② 创建数据库表插入测试数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 CREATE TABLE `t_customer` ( `customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR (100 ), PRIMARY KEY (`customer_id`) ); CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR (100 ), `customer_id` INT , PRIMARY KEY (`order_id`) ); INSERT INTO `t_customer` (`customer_name`) VALUES ('c01' );INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1' , '1' );INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2' , '1' );INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3' , '1' );
实际开发时,一般在开发过程中,不给数据库表设置外键约束。
原因是避免调试不方便。
一般是功能开发完成,再加外键约束检查是否有 bug。
2、Mybatis 实现关联关系:对一 ① 创建 OrderMapper 接口 1 2 3 4 5 public interface OrderMapper { Order selectOrderWithCustomer (Integer orderId) ; }
② 创建 OrderMapper.xml 配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <resultMap id ="selectOrderWithCustomerResultMap" type ="com.atguigu.mybatis.entity.Order" > <id column ="order_id" property ="orderId" /> <result column ="order_name" property ="orderName" /> <association property ="customer" javaType ="com.atguigu.mybatis.entity.Customer" > <id column ="customer_id" property ="customerId" /> <result column ="customer_name" property ="customerName" /> </association > </resultMap > <select id ="selectOrderWithCustomer" resultMap ="selectOrderWithCustomerResultMap" > SELECT order_id,order_name,c.customer_id,customer_name FROM t_order o LEFT JOIN t_customer c ON o.customer_id=c.customer_id WHERE o.order_id=#{orderId} </select >
③ 在 Mybatis 全局配置文件中注册 Mapper 配置文件 1 2 3 4 5 <mappers > <mapper resource ="com/atguigu/mybatis/mapper/OrderMapper.xml" /> </mappers >
④junit 测试程序 1 2 3 4 5 6 7 8 9 10 11 @Test public void testRelationshipToOne () { SqlSession session = factory.openSession(); OrderMapper orderMapper = session.getMapper(OrderMapper.class); Order order = orderMapper.selectOrderWithCustomer(2 ); System.out.println("order = " + order); session.close(); }
⑤ 关键词 在“对一”关联关系中,我们的配置比较多,但是关键词就只有:association 和javaType
3、Mybatis 实现关联关系:对多 ① 创建 Mapper 接口 1 2 3 4 5 public interface CustomerMapper { Customer selectCustomerWithOrderList (Integer customerId) ; }
② 创建 CustomerMapper.xml 配置文件 注意:不要忘记在 Mybatis 全局配置文件中注册
③ 在 Mapper.xml 配置文件中配置关联关系和 SQL 语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <resultMap id ="selectCustomerWithOrderListResultMap" type ="com.atguigu.mybatis.entity.Customer" > <id column ="customer_id" property ="customerId" /> <result column ="customer_name" property ="customerName" /> <collection property ="orderList" ofType ="com.atguigu.mybatis.entity.Order" > <id column ="order_id" property ="orderId" /> <result column ="order_name" property ="orderName" /> </collection > </resultMap > <select id ="selectCustomerWithOrderList" resultMap ="selectCustomerWithOrderListResultMap" > SELECT c.customer_id,c.customer_name,o.order_id,o.order_name FROM t_customer c LEFT JOIN t_order o ON c.customer_id=o.customer_id WHERE c.customer_id=#{customerId} </select >
④junit 测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void testRelationshipToMulti () { SqlSession session = factory.openSession(); CustomerMapper customerMapper = session.getMapper(CustomerMapper.class); Customer customer = customerMapper.selectCustomerWithOrderList(1 ); System.out.println("customer.getCustomerId() = " + customer.getCustomerId()); System.out.println("customer.getCustomerName() = " + customer.getCustomerName()); List<Order> orderList = customer.getOrderList(); for (Order order : orderList) { System.out.println("order = " + order); } session.close(); }
⑤ 关键词 在“对多”关联关系中,同样有很多配置,但是提炼出来最关键的就是:“collection ”和“ofType ”
4、分步查询 ① 概念和需求 为了实现延迟加载,对 Customer 和 Order 的查询必须分开,分成两步来做,才能够实现。为此,我们需要单独查询 Order,也就是需要在 Mapper 配置文件中,单独编写查询 Order 集合数据的 SQL 语句。
② 具体操作 [1]编写查询 Customer 的 SQL 语句 1 2 3 4 5 <select id ="selectCustomerWithOrderList" resultMap ="selectCustomerWithOrderListResultMap" > select customer_id,customer_name from t_customer where customer_id=#{customerId} </select >
[2]编写查询 Order 的 SQL 语句 1 2 3 <select id ="selectOrderList" resultType ="com.atguigu.mybatis.entity.Order" > select order_id,order_name from t_order where customer_id=#{customer_id} </select >
[3]在 collection 标签中引用这条查询 Order 的 SQL 语句 1 2 3 4 5 6 7 8 <collection property="orderList" select="com.atguigu.mybatis.mapper.CustomerMapper.selectOrderList" column="customer_id"/>
如果 Mapper 接口中的抽象方法没有改变,那么 juni 测试也不变。执行结果如下:
1 2 3 4 5 6 7 8 9 10 DEBUG 11 -30 11 :10 :05 ,796 ==> Preparing: select customer_id,customer_name from t_customer where customer_id=? (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :10 :05 ,866 ==> Parameters: 1 (Integer) (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :10 :05 ,889 ====> Preparing: select order_id,order_name from t_order where customer_id=? (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :10 :05 ,890 ====> Parameters: 1 (Integer) (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :10 :05 ,895 <==== Total: 3 (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :10 :05 ,896 <== Total: 1 (BaseJdbcLogger.java:145 ) customer = c01 order = Order{orderId=1 , orderName='o1' } order = Order{orderId=2 , orderName='o2' } order = Order{orderId=3 , orderName='o3' }
③ 各个要素之间的对应关系
5、延迟加载 ① 概念 查询到 Customer 的时候,不一定会使用 Order 的 List 集合数据。如果 Order 的集合数据始终没有使用,那么这部分数据占用的内存就浪费了。对此,我们希望不一定会被用到的数据,能够在需要使用的时候再去查询。
例如:对 Customer 进行 1000 次查询中,其中只有 15 次会用到 Order 的集合数据,那么就在需要使用时才去查询能够大幅度节约内存空间。
延迟加载 的概念:对于实体类关联的属性到需要使用 时才查询。也叫懒加载 。
② 配置 在 Mybatis 全局配置文件中配置 settings
1 2 3 4 5 6 7 8 9 <settings > <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> </settings >
官方文档中对 aggressiveLazyLoading 属性的解释:
When enabled, an object with lazy loaded properties will be loaded entirely upon a call to any of the lazy properties.Otherwise, each property is loaded on demand.
③ 修改 junit 测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Test public void testSelectCustomerWithOrderList () throws InterruptedException { SqlSession session = sessionFactory.openSession(); CustomerMapper mapper = session.getMapper(CustomerMapper.class); Customer customer = mapper.selectCustomerWithOrderList(1 ); System.out.println("customer = " + customer.getCustomerName()); TimeUnit.SECONDS.sleep(5 ); List<Order> orderList = customer.getOrderList(); for (Order order : orderList) { System.out.println("order = " + order); } session.commit(); session.close(); }
效果:刚开始先查询 Customer 本身,需要用到 OrderList 的时候才发送 SQL 语句去查询
1 2 3 4 5 6 7 8 9 10 DEBUG 11 -30 11 :25 :31 ,127 ==> Preparing: select customer_id,customer_name from t_customer where customer_id=? (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :25 :31 ,193 ==> Parameters: 1 (Integer) (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :25 :31 ,314 <== Total: 1 (BaseJdbcLogger.java:145 ) customer = c01 DEBUG 11 -30 11 :25 :36 ,316 ==> Preparing: select order_id,order_name from t_order where customer_id=? (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :25 :36 ,316 ==> Parameters: 1 (Integer) (BaseJdbcLogger.java:145 ) DEBUG 11 -30 11 :25 :36 ,321 <== Total: 3 (BaseJdbcLogger.java:145 ) order = Order{orderId=1 , orderName='o1' } order = Order{orderId=2 , orderName='o2' } order = Order{orderId=3 , orderName='o3' }
6、关键词总结 我们是在“对多”关系中举例说明延迟加载的,在“对一”中配置方式基本一样。
关联关系 配置项关键词 所在配置文件 对一 association 标签/javaType 属性 Mapper 配置文件中的 resultMap 对多 collection 标签/ofType 属性 Mapper 配置文件中的 resultMap 对一分步 association 标签/select 属性 Mapper 配置文件中的 resultMap 对多分步 collection 标签/select 属性 Mapper 配置文件中的 resultMap 延迟加载 lazyLoadingEnabled 设置为 true aggressiveLazyLoading 设置为 false Mybatis 全局配置文件中的 settings
7、多对多关联需中间表 ① 如果不使用中间表
在某一个表中,使用一个字段保存多个“外键”值,这将导致无法使用 SQL 语句进行关联查询。
② 使用中间表
这样就可以使用 SQL 进行关联查询了。只是有可能需要三张表进行关联。
③ 中间表设置主键 [1]方案一:另外设置一个专门的主键字段
[2]方案二:使用联合主键
使用联合主键时,只要多个字段的组合不重复即可,单个字段内部是可以重复的。