网站备案名称的影响吗,稿定设计app免费版官方,成都各公司网站,建设银行激活社保卡网站三、MyBatis多表映射
3.1 多表映射概念
多表查询结果映射思路
前面说明中#xff0c;我全面梳理了单表的mybatis操作#xff01;但是开发中更多的是多表查询需求#xff0c;这种情况我们如何让进行处理#xff1f;MyBatis 思想是#xff1a;数据库不可能永远是你所想或…三、MyBatis多表映射
3.1 多表映射概念
多表查询结果映射思路
前面说明中我全面梳理了单表的mybatis操作但是开发中更多的是多表查询需求这种情况我们如何让进行处理MyBatis 思想是数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式可惜它们并不都是那样。 如果能有一种数据库映射模式完美适配所有的应用程序查询需求那就太好了而 ResultMap 就是 MyBatis 就是完美答案。
官方例子我们如何映射下面这个语句
!-- 非常复杂的语句 --
select idselectBlogDetails resultMapdetailedBlogResultMapselectB.id as blog_id,B.title as blog_title,B.author_id as blog_author_id,A.id as author_id,A.username as author_username,A.password as author_password,A.email as author_email,A.bio as author_bio,A.favourite_section as author_favourite_section,P.id as post_id,P.blog_id as post_blog_id,P.author_id as post_author_id,P.created_on as post_created_on,P.section as post_section,P.subject as post_subject,P.draft as draft,P.body as post_body,C.id as comment_id,C.post_id as comment_post_id,C.name as comment_name,C.comment as comment_text,T.id as tag_id,T.name as tag_namefrom Blog Bleft outer join Author A on B.author_id A.idleft outer join Post P on B.id P.blog_idleft outer join Comment C on P.id C.post_idleft outer join Post_Tag PT on PT.post_id P.idleft outer join Tag T on PT.tag_id T.idwhere B.id #{id}
/select
你可能想把它映射到一个智能的对象模型这个对象表示了一篇博客它由某位作者所写有很多的博文每篇博文有零或多条的评论和标签。 我们先来看看下面这个完整的例子它是一个非常复杂的结果映射假设作者博客博文评论和标签都是类型别名。 虽然它看起来令人望而生畏但其实非常简单。
!-- 非常复杂的结果映射 --
resultMap iddetailedBlogResultMap typeBlogconstructoridArg columnblog_id javaTypeint//constructorresult propertytitle columnblog_title/association propertyauthor javaTypeAuthorid propertyid columnauthor_id/result propertyusername columnauthor_username/result propertypassword columnauthor_password/result propertyemail columnauthor_email/result propertybio columnauthor_bio/result propertyfavouriteSection columnauthor_favourite_section//associationcollection propertyposts ofTypePostid propertyid columnpost_id/result propertysubject columnpost_subject/association propertyauthor javaTypeAuthor/collection propertycomments ofTypeCommentid propertyid columncomment_id//collectioncollection propertytags ofTypeTag id propertyid columntag_id//collection/collection
/resultMap
你现在可能看不懂接下来我们要学习将多表查询结果使用ResultMap标签映射到实体类对象上
我们的学习目标
多表查询语句使用多表结果承接实体类设计使用ResultMap完成多表结果映射
实体类设计方案
多表关系回顾双向查看
一对一夫妻关系人和身份证号一对多 | 多对一用户和用户的订单锁和钥匙多对多老师和学生部门和员工 实体类设计关系(查询)单向查看对一 夫妻一方对应另一方订单对应用户都是对一关系实体类设计对一关系下类中只要包含单个对方对象类型属性即可例如
public class Customer {private Integer customerId;private String customerName;}public class Order {private Integer orderId;private String orderName;private Customer customer;// 体现的是对一的关系}
对多: 用户对应的订单讲师对应的学生或者学生对应的讲师都是对多关系
实体类设计对多关系下类中只要包含对方类型集合属性即可
public class Customer {private Integer customerId;private String customerName;private ListOrder orderList;// 体现的是对多的关系
}public class Order {private Integer orderId;private String orderName;private Customer customer;// 体现的是对一的关系}//查询客户和客户对应的订单集合 不要管!
多表结果实体类设计小技巧
对一属性中包含对方对象对多属性中包含对方对象集合只有真实发生多表查询时才需要设计和修改实体类否则不提前设计和修改实体类无论多少张表联查实体类设计都是两两考虑在查询映射的时候只需要关注本次查询相关的属性例如查询订单和对应的客户就不要关注客户中的订单集合
多表映射案例准备
数据库
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。
实体类设计稍后会进行订单关联客户查询也会进行客户关联订单查询所以在这先练习设计。
Data
public class Customer {private Integer customerId;private String customerName;private ListOrder orderList;// 体现的是对多的关系} Data
public class Order {private Integer orderId;private String orderName;private Customer customer;// 体现的是对一的关系}
3.2 对一映射
需求说明根据ID查询订单以及订单关联的用户的信息
OrderMapper接口
public interface OrderMapper {Order selectOrderWithCustomer(Integer orderId);
}
OrderMapper.xml配置文件
!-- 创建resultMap实现“对一”关联关系映射 --
!-- id属性通常设置为这个resultMap所服务的那条SQL语句的id加上“ResultMap” --
!-- type属性要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 --
resultMap idselectOrderWithCustomerResultMap typeorder!-- 先设置Order自身属性和字段的对应关系 --id columnorder_id propertyorderId/result columnorder_name propertyorderName/!-- 使用association标签配置“对一”关联关系 --!-- property属性在Order类中对一的一端进行引用时使用的属性名 --!-- javaType属性一的一端类的全类名 --association propertycustomer javaTypecustomer!-- 配置Customer类的属性和字段名之间的对应关系 --id columncustomer_id propertycustomerId/result columncustomer_name propertycustomerName//association/resultMap!-- Order selectOrderWithCustomer(Integer orderId); --
select idselectOrderWithCustomer resultMapselectOrderWithCustomerResultMapSELECT order_id,order_name,c.customer_id,customer_nameFROM t_order oLEFT JOIN t_customer cON o.customer_idc.customer_idWHERE o.order_id#{orderId}/select
Mybatis全局注册Mapper文件
!-- 注册Mapper配置文件告诉Mybatis我们的Mapper配置文件的位置 --
mappers!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 --mapper resourcemappers/OrderMapper.xml//mappers
junit测试程序
Slf4j
public class MyBatisTest {private SqlSession session;// junit会在每一个Test方法前执行BeforeEach方法BeforeEachpublic void init() throws IOException {session new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(mybatis-config.xml)).openSession();}Testpublic void testRelationshipToOne() {OrderMapper orderMapper session.getMapper(OrderMapper.class);// 查询Order对象检查是否同时查询了关联的Customer对象Order order orderMapper.selectOrderWithCustomer(2);log.info(order order);}// junit会在每一个Test方法后执行AfterEach方法AfterEachpublic void clear() {session.commit();session.close();}
}
关键词在“对一”关联关系中我们的配置比较多但是关键词就只有association和javaType
3.3 对多映射
需求说明查询客户和客户关联的订单信息
CustomerMapper接口
public interface CustomerMapper {Customer selectCustomerWithOrderList(Integer customerId);}
CustomerMapper.xml文件
!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 --
resultMap idselectCustomerWithOrderListResultMaptypecustomer!-- 映射Customer本身的属性 --id columncustomer_id propertycustomerId/result columncustomer_name propertycustomerName/!-- collection标签映射“对多”的关联关系 --!-- property属性在Customer类中关联“多”的一端的属性名 --!-- ofType属性集合属性中元素的类型 --collection propertyorderList ofTypeorder!-- 映射Order的属性 --id columnorder_id propertyorderId/result columnorder_name propertyorderName//collection/resultMap!-- Customer selectCustomerWithOrderList(Integer customerId); --
select idselectCustomerWithOrderList resultMapselectCustomerWithOrderListResultMapSELECT c.customer_id,c.customer_name,o.order_id,o.order_nameFROM t_customer cLEFT JOIN t_order oON c.customer_ido.customer_idWHERE c.customer_id#{customerId}
/select
Mybatis全局注册Mapper文件
!-- 注册Mapper配置文件告诉Mybatis我们的Mapper配置文件的位置 --
mappers!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 --mapper resourcemappers/OrderMapper.xml/mapper resourcemappers/CustomerMapper.xml/
/mappers
junit测试程序
Test
public void testRelationshipToMulti() {CustomerMapper customerMapper session.getMapper(CustomerMapper.class);// 查询Customer对象同时将关联的Order集合查询出来Customer customer customerMapper.selectCustomerWithOrderList(1);log.info(customer.getCustomerId() customer.getCustomerId());log.info(customer.getCustomerName() customer.getCustomerName());ListOrder orderList customer.getOrderList();for (Order order : orderList) {log.info(order order);}
}
关键词在“对多”关联关系中同样有很多配置但是提炼出来最关键的就是“collection”和“ofType”
3.4 多表映射总结
3.4.1 多表映射优化 setting属性 属性含义 可选值 默认值 autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集无论是否嵌套。 NONE, PARTIAL, FULL PARTIAL
我们可以将autoMappingBehavior设置为full进行多表resultMap映射的时候可以省略符合列和属性命名映射规则列名属性名或者开启驼峰映射也可以自定映射的result标签
修改mybati-sconfig.xml:
!--开启resultMap自动映射 --
setting nameautoMappingBehavior valueFULL/
修改teacherMapper.xml
resultMap idteacherMap typeteacherid propertytId columnt_id /!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!--
!-- result propertytName columnt_name /--collection propertystudents ofTypestudent id propertysId columns_id /
!-- result propertysName columns_name /--/collection
/resultMap
3.4.2 多表映射总结 关联关系 配置项关键词 所在配置文件和具体位置 对一 association标签/javaType属性/property属性 Mapper配置文件中的resultMap标签内 对多 collection标签/ofType属性/property属性 Mapper配置文件中的resultMap标签内
四、MyBatis动态语句
4.1 动态语句需求和简介
经常遇到很多按照很多查询条件进行查询的情况比如智联招聘的职位搜索等。其中经常出现很多条件不取值的情况在后台应该如何完成最终的SQL语句呢 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架你应该能理解根据不同条件拼接 SQL 语句有多痛苦例如拼接时要确保不能忘记添加必要的空格还要注意去掉列表最后一个列名的逗号。利用动态 SQL可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式MyBatis 3 替换了之前的大部分元素大大精简了元素种类现在要学习的元素种类比原来的一半还要少。
4.2 if和where标签
使用动态 SQL 最常见情景是根据条件包含 where / if 子句的一部分。比如
!-- ListEmployee selectEmployeeByCondition(Employee employee); --
select idselectEmployeeByCondition resultTypeemployeeselect emp_id,emp_name,emp_salary from t_emp!-- where标签会自动去掉“标签体内前面多余的and/or” --where!-- 使用if标签让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句就看if标签判断的结果是否为true --!-- 在if标签的test属性中可以访问实体类的属性不可以访问数据库表的字段 --if testempName ! null!-- 在if标签内部需要访问接口的参数时还是正常写#{} --or emp_name#{empName}/ifif testempSalary gt; 2000or emp_salary#{empSalary}/if!--第一种情况所有条件都满足 WHERE emp_name? or emp_salary?第二种情况部分条件满足 WHERE emp_salary?第三种情况所有条件都不满足 没有where子句--/where
/select
4.3 set标签
!-- void updateEmployeeDynamic(Employee employee) --
update idupdateEmployeeDynamicupdate t_emp!-- set emp_name#{empName},emp_salary#{empSalary} --!-- 使用set标签动态管理set子句并且动态去掉两端多余的逗号 --setif testempName ! nullemp_name#{empName},/ifif testempSalary lt; 3000emp_salary#{empSalary},/if/setwhere emp_id#{empId}!--第一种情况所有条件都满足 SET emp_name?, emp_salary?第二种情况部分条件满足 SET emp_salary?第三种情况所有条件都不满足 update t_emp where emp_id?没有set子句的update语句会导致SQL语法错误--
/update
4.4 trim标签(了解)
使用trim标签控制条件部分两端是否包含某些字符
prefix属性指定要动态添加的前缀suffix属性指定要动态添加的后缀prefixOverrides属性指定要动态去掉的前缀使用“|”分隔有可能的多个值suffixOverrides属性指定要动态去掉的后缀使用“|”分隔有可能的多个值
!-- ListEmployee selectEmployeeByConditionByTrim(Employee employee) --
select idselectEmployeeByConditionByTrim resultTypecom.atguigu.mybatis.entity.Employeeselect emp_id,emp_name,emp_age,emp_salary,emp_genderfrom t_emp!-- prefix属性指定要动态添加的前缀 --!-- suffix属性指定要动态添加的后缀 --!-- prefixOverrides属性指定要动态去掉的前缀使用“|”分隔有可能的多个值 --!-- suffixOverrides属性指定要动态去掉的后缀使用“|”分隔有可能的多个值 --!-- 当前例子用where标签实现更简洁但是trim标签更灵活可以用在任何有需要的地方 --trim prefixwhere suffixOverridesand|orif testempName ! nullemp_name#{empName} and/ifif testempSalary gt; 3000emp_salary#{empSalary} and/ifif testempAge lt; 20emp_age#{empAge} or/ifif testempGendermaleemp_gender#{empGender}/if/trim
/select
4.5 choose/when/otherwise标签
在多个分支条件中仅执行一个。
从上到下依次执行条件判断遇到的第一个满足条件的分支会被采纳被采纳分支后面的分支都将不被考虑如果所有的when分支都不满足那么就执行otherwise分支
!-- ListEmployee selectEmployeeByConditionByChoose(Employee employee) --
select idselectEmployeeByConditionByChoose resultTypecom.atguigu.mybatis.entity.Employeeselect emp_id,emp_name,emp_salary from t_empwherechoosewhen testempName ! nullemp_name#{empName}/whenwhen testempSalary lt; 3000emp_salary lt; 3000/whenotherwise11/otherwise/choose!--第一种情况第一个when满足条件 where emp_name?第二种情况第二个when满足条件 where emp_salary 3000第三种情况两个when都不满足 where 11 执行了otherwise--
/select
4.6 foreach标签
基本用法
用批量插入举例
!--collection属性要遍历的集合item属性遍历集合的过程中能得到每一个具体对象在item属性中设置一个名字将来通过这个名字引用遍历出来的对象separator属性指定当foreach标签的标签体重复拼接字符串时各个标签体字符串之间的分隔符open属性指定整个循环把字符串拼好后字符串整体的前面要添加的字符串close属性指定整个循环把字符串拼好后字符串整体的后面要添加的字符串index属性这里起一个名字便于后面引用遍历List集合这里能够得到List集合的索引值遍历Map集合这里能够得到Map集合的key--
foreach collectionempList itememp separator, openvalues indexmyIndex!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象需要使用item属性声明的名称 --(#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
/foreach
批量更新时需要注意
上面批量插入的例子本质上是一条SQL语句而实现批量更新则需要多条SQL语句拼起来用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置
atguigu.dev.urljdbc:mysql:///mybatis-example?allowMultiQueriestrue
对应的foreach标签如下
!-- int updateEmployeeBatch(Param(empList) ListEmployee empList) --
update idupdateEmployeeBatchforeach collectionempList itememp separator;update t_emp set emp_name#{emp.empName} where emp_id#{emp.empId}/foreach
/update
关于foreach标签的collection属性
如果没有给接口中List类型的参数使用Param注解指定一个具体的名字那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来
Parameter empList not found. Available parameters are [arg0, collection, list]
在实际开发中为了避免隐晦的表达造成一定的误会建议使用Param注解明确声明变量的名称然后在foreach标签的collection属性中按照Param注解指定的名称来引用传入的参数。
4.7 sql片段
抽取重复的SQL片段
!-- 使用sql标签抽取重复出现的SQL片段 --
sql idmySelectSqlselect emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
/sql
引用已抽取的SQL片段
!-- 使用include标签引用声明的SQL片段 --
include refidmySelectSql/ 本文为学习笔记所参考文章均已附上链接若有疑问请私信
创作不易如果对你有点帮助的话麻烦点个赞支持一下
新手小白欢迎留言指正