资讯专栏INFORMATION COLUMN

MyBatis理解与掌握(关联查询)

MiracleWong / 1681人阅读

摘要:订单信息与订单明细为一对多关系。例如先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。作用将关联查询信息映射到一个对象中。

MyBatis理解与掌握(关联查询)

@(MyBatis)[Java, 框架, MyBatis]

一对一查询

案例:查询所有订单信息,关联查询下单用户信息

从Order的角度,一个订单对应一个用户:order----->user (一对一)
从User的角度,一个用户可以有多个订单:user------>order (多对一)

场景需求:查询订单,同时关联查询用户信息
-------------------------------表设计------------------------------------------
在t_orders(多方表)中增加一个字段user_id,作为外键

-------------------------------类设计------------------------------------------

public class Order {
  private int oId;
  private int number;
  private User user;
    //省略get、set方法
}
public class User {
  private Integer userId;
  private String userName;
    //省略get、set方法
}

-----------------------映射文件mapping.xml-----------------------

方法一:嵌套结果

关联元素处理“有一个”类型的关系。例如,在下面的中表示一个订单对应了一个用户。
association标签可用的属性如下:

property:对象属性的名称
javaType:对象属性的类型
column:所对应的外键字段名称
select:使用另一个查询封装的结果
columnPrefix:当连接多表时,你将不得不使用列别名来避免ResultSet中的重复列名。指定columnPrefix允许你映射列名到一个外部的结果集中。

 
 
  
  
  
  
 
 
  
  
  

执行过程:

DEBUG [main] - ==>  Preparing: select t_orders.order_id, t_orders.order_number, t_user.id, t_user.user_name, t_user.user_address from t_orders,t_user WHERE t_orders.user_id = t_user.id and t_orders.order_id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: order_id, order_number, id, user_name, user_address
TRACE [main] - <==        Row: 1, 30, 1, zhangsan, 北京
DEBUG [main] - <==      Total: 1
打印查询结果对象: Order [oId=1, number=30, user=User [userId=1, userName=zhangsan]]
方法二:嵌套查询

通过执行另外一个SQL映射语句来返回预期的复杂类型


  
 
  
  
  
  
 
  

---------------------------java代码---------------------

@Test
  public void test() {
    SqlSession session = DBUtil.getSession();
    Order order = session.selectOne("getOrder",1 );
    System.out.println("打印查询结果对象:"+order);
}

--------------------------sql执行过程-------------------------

DEBUG [main] - ==>  Preparing: select order_id, order_number, user_id from t_orders WHERE order_id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
TRACE [main] - <==    Columns: order_id, order_number, user_id
TRACE [main] - <==        Row: 1, 30, 3
DEBUG [main] - ====>  Preparing: select user_id as userId , user_name as userName from t_user WHERE user_id=?
DEBUG [main] - ====> Parameters: 3(Integer)
TRACE [main] - <====    Columns: userId, userName
TRACE [main] - <====        Row: 3, wangwu
DEBUG [main] - <====      Total: 1
DEBUG [main] - <==      Total: 1
打印查询结果对象:Order [oId=1, number=30, user=User [userId=3, userName=wangwu]]

__注意__:我们有两个查询语句:一个来加载order,另外一个来加载user。
这种方式很简单, 但会产生 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:

(1)你执行了一个多带带的 SQL 语句来获取结果列表(就是“+1”)。
(2)假设返回的是一个集合,集合中的每个元素对应了一条记录,你还需要执行了一个查询语句来为每条记录加载细节(就是“N”)。 

MyBatis 延迟加载是一种处理方式,然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加载,问题没有解决。

方法三:增加一个包含订单信息和用户信息的类,用这个类作为返回类型 resultType
public class OrdersCustom extends Orders {
            private String username ; // 用户名称
            private String address ; // 用户地址
            省略get、set方法
}

总结:定义专门的po类作为输出类型,其中定义了sql查询结果集所有的字段。此方法较为简单,企业中普遍使用。



一对多查询

案例:通过订单id查询订单信息及订单下的订单明细信息。
订单信息与订单明细为一对多关系。

---------------------------类设计----------------------------------------

  private int oId;
  private int number; //订单编号
  private User user; //用户信息
  private List orderdetails;  //订单详情信息
  //省略get、set方法
  }

-------------------------映射文件-----------------------------

方法一:嵌套结果

  
     
     
     
       
       
     
     
     
       
       
       
     
  

使用继承extends,上面的可以改写为:



     
     
     
       
       
     
  
 
  
     
     
       
       
       
     
  
打印查询结果对象:Order [oId=1, number=30000001, user=User [userId=3, userName=wangwu],
orderdetails=[Orderdetail [odId=1, itemsId=2, itemsNum=30], Orderdetail [odId=2, itemsId=1, itemsNum=32]]]
方法二:嵌套查询

 
  
     
     
     
     
     
  
 
  
 
  

---------------------------java代码---------------------

@Test
  public void test() {
    SqlSession session = DBUtil.getSession();
    Order order = session.selectOne("getOrder",1 );
    System.out.println("打印查询结果对象:"+order);
}

------------------------sql执行结果-----------------------

打印查询结果对象:Order [oId=1, number=30000001, user=User [userId=3, userName=wangwu],
orderdetails=[Orderdetail [odId=1, itemsId=2, itemsNum=30], Orderdetail [odId=2, itemsId=1, itemsNum=32]]]



多对多查询

案例:查询用户购买的商品信息
需要查询所有用户信息,关联查询订单及订单明细信息,订单明细信息中关联查询商品信息

分析:
需要关联查询映射的信息是:订单、订单明细、商品信息

订单:一个用户对应多个订单,使用 collection 映射到用户对象的订单列表属性中

订单明细:一个订单对应多个明细,使用 collection 映射到订单对象中的明细属性中

商品信息: 一个订单明细对应一个商品, 使用 association 映射到订单明细对象的商品属性中。

---------------------------类设计-----------------------------------

public class User {
  private Integer userId;
  private String userName;
  private List orders;  //订单列表
}
 
public class Order {
  private int oId;
  private int number; //订单编号
  private List orderdetails;  //订单详情信息
}
 
public class Orderdetail {
  private int odId;
  private Items item; //商品信息
  private int itemsNum; //商品数量
}
 
public class Items {
  private int itemId;
  private String itemName; //商品名称
}

-----------------------------映射文件--------------------------------------


 
  
     
     
     
       
       
       
          
          
          
            
            
          
       
     
  

---------------------------java代码---------------------

@Test
  public void test() {
    SqlSession session = DBUtil.getSession();
    List users = session.selectList("getUser");
    for (User user : users) {
      System.out.println("打印用户信息:"+user);
    }
  }

------------------------sql执行结果-----------------------

打印用户信息:User [userId=1, userName=zhangsan, orders=[Order [oId=1, number=30000001, orderdetails=[Orderdetail [odId=1, item=Items [itemId=2, itemName=香蕉], itemsNum=30], Orderdetail [odId=2, item=Items [itemId=1, itemName=苹果], itemsNum=32]]], Order [oId=3, number=30000003, orderdetails=[Orderdetail [odId=3, item=Items [itemId=3, itemName=橘子], itemsNum=25]]]]]
打印用户信息:User [userId=3, userName=wangwu, orders=[Order [oId=4, number=30000004, orderdetails=[Orderdetail [odId=4, item=Items [itemId=3, itemName=橘子], itemsNum=45]]]]]
自关联

延迟加载

什么是延迟加载

resultMap可实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

例如:先从单表查询,需要时再从关联表去关联查询,大大 提高数据库性能 ,因为查询单表要比关联查询多张表速度要快。

mybatis框架默认不支持延迟加载功能,如果想要使用,需要启用延迟加载功能


默认框架会采用侵入式的延迟加载:

如果查询主动方数据,而不使用,那么关联数据是不会被查询的。

如果使用了主动方的数据,那么关联数据即使没有使用也会被查询。

可以禁用侵入式延迟加载功能

 

综上所述,在主配置文件mybatis-config.xml中做如下配置:

  
    
    
  

注意一个异常:Caused by: org.xml.sax.SAXParseException; lineNumber: 36; columnNumber: 17; 元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"。
Alt text
在关联的元素(association ,collection ,discriminator)中,我们存在一个属性: fetchType来决定是否需要延迟加载,如果配置它,它将覆盖掉原有在MyBatis设置的策略。

对于它而言,它有两个取值:

* lazy: 延迟加载(默认)
* eager:即刻加载

由它来决定是否需要延迟或者即刻加载。

总结 resultType

作用:

将查询结果按照 sql 列名 pojo 属性名一致性映射到 pojo 中。

场合:

常见一些明细记录的展示, 比如用户购买商品明细, 将关联查询信息全部展示在页面时,此时可直接使用 resultType 将每一条记录映射到 pojo 中, 在前端页面遍历 list ( list 中是 pojo )即可。

resultMap

使用 association 和 collection 完成一对一和一对多高级映射 (对结果有特殊的映射要求) 。

association

作用:

将关联查询信息映射到一个 pojo 对象中。

场合:

为了方便查询关联信息可以使用 association 将关联订单信息映射为用户对象的 pojo 属性中,比如:查询订单及关联用户信息。使用 resultType 无法将查询结果映射到 pojo 对象的 pojo 属性中,根据对结果集查询遍历的需要选择使用 resultType 还是 resultMap 。

collection

作用:

将关联查询信息映射到一个 list 集合中。

场合:

为了方便查询遍历关联信息可以使用 collection 将关联信息映射到 list 集合中,比如:查询用户权限范围模块及模块下的菜单,可使用 collection 将模块映射到模块 list 中,将菜单列表映射到模块对象的菜单 list 属性中, 这样的作的目的也是方便对查询结果集进行遍历查询。

如果使用 resultType 无法将查询结果映射到 list 集合中。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77174.html

相关文章

  • 70 个 Spring 最常见面试题,Java 晋升必会

    摘要:容器自动完成装载,默认的方式是这部分重点在常用模块的使用以及的底层实现原理。 对于那些想面试高级 Java 岗位的同学来说,除了算法属于比较「天方夜谭」的题目外,剩下针对实际工作的题目就属于真正的本事了,热门技术的细节和难点成为了主要考察的内容。 这里说「天方夜谭」并不是说算法没用,不切实际,而是想说算法平时其实很少用到,甚至面试官都对自己出的算法题一知半解。 这里总结打磨了 70 道...

    Ashin 评论0 收藏0
  • MyBatis理解掌握(简介)

    摘要:语句在代码中硬编码,造成代码不易于维护,实际应用变化的可能较大,变动需要改变代码。对结果集解析存在硬编码查询列名,变化导致解析代码变化,系统不易于维护,如果能将数据库记录封装成对象解析比较方便。 MyBatis理解与掌握(简介) @(MyBatis)[Java, 框架, MyBatis] 简介   Mybatis是一个数据持久层框架,MyBatis消除了几乎所有的JDBC代码和参数的手...

    Pocher 评论0 收藏0
  • MyBatis理解掌握(缓存)

    摘要:理解与掌握缓存框架一级缓存默认就可以使用框架处理缓存是依赖映射,的内部缓存使用一个,为语句。一级缓存的作用域是一个,一旦发生变化,一级缓存失败在同一个中,执行相同的查询,第一次会去查询数据库,并写到缓存中第二次直接从缓存中取。 MyBatis理解与掌握(缓存) @(MyBatis)[Java, 框架, MyBatis] 一级缓存(SqlSession) 默认就可以使用 框架处理缓存是 ...

    马龙驹 评论0 收藏0
  • MyBatis理解掌握(输入输出)

    摘要:输入参数类型指定输入参数类型,通过从输入对象中获取参数值放置在中。查询结果处理指定输出结果类型,将查询结果的一行记录数据映射为指定类型的对象。 MyBatis理解与掌握(输入与输出) @(MyBatis)[Java, 框架, MyBatis] 占位符和拼接 {}:占位符 能防止sql注入问题,所一能尽量用#{}就尽量用#{}用来传入参数,sql在解析的时候会加上 当成字符串来解析 ,...

    why_rookie 评论0 收藏0
  • MyBatis 源码分析系列文章导读

    摘要:本文速览本篇文章是我为接下来的源码分析系列文章写的一个导读文章。年该项目从基金会迁出,并改名为。同期,停止维护。符号所在的行则是表示的执行结果。同时,使用无需处理受检异常,比如。另外,把写在配置文件中,进行集中管理,利于维护。 1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章。本篇文章从 MyBatis 是什么(what),为什么要使用(why),...

    weizx 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<