本文与之前的文章已经涵盖了了MyBatis所有的使用方法,所以这是最后一篇主们讲解MyBatis框架的文章了。我们都知道MyBatis的SQL语句是写在mapper.xml文件里面的,动态SQL指通过MyBatis提供的标签实现动态拼接SQL,在mapper的根元素下还有许多标签,请看下图,都有哪些标签
cache是MyBatis提供的二级缓存,目前几乎是已经作废的,我们可以通过第三方缓存框架ehcache等或分布式缓存进行替代。cache-ref用于引用其他mapper标签下的namespace属性,这样我们可以建立共享的全局的缓存配置。delete,insert,select,update四个标签分别用于写入相对数据操作的增删改查,parameterMap该标签已被废弃,接下来的版本中MyBatis官方明确表示该标签会被移除,resultMap用于手动映射Java对象与数据库中的表,文章有具体使用方法,最后我们来看一看sql这个标签的用法
使用起来非常简单,它的属性只有一个id,这个id名字我们可以随便取,用于被其他标签引用。sql标签可以成为sql片段,我们将所有的查询语句中公共的sql片段抽取出来,能够简化sql语句,在hibernate框架中的hql也是有一定的缩减。
然后我们在其他标签中创建include标签,其属性refid=sql片段的id名即可,那么这块include标签就等同于select*from.使用起来还是比较简单的。
在select标签内部其实还有一些标签
先看foreach标签,见名知意这应该是起到循环或迭代作用的一个标签。该标签能够解决批量查询的问题,假设我们要查询id为1,2,3的用户,sql的写法是select*fromuserwhereidin#{value};那么1,2,3如果作为入参被传进来呢,我们可以通过包装类,或者List集合,Integer数组的形式将参数传入。请看下图
图中做法是在包装类的内部封装了一个List集合,这里的传入参数就必须要写QueryVo,看下图mapper中是如何配置的
可以看到入参类型为QueryVo,但是要想表示QueryVo下ids集合中的元素就要用到foreach标签了,该标签常用在构建in语句时使用,它能够遍历集合中的元素,collection属性表示引用的集合名称,上图中我们创建的集合名为ids,那么这里写ids。item为本次迭代的变量名,可以随便取,内部的ONGL表达式取值时,需要通过item的名字,可以理解为#{item},separator为分隔符,我们in语句后的1,2,3全部用,分割,所以这里写,。open表示sql语句中集合元素起始部分本sql语句为select*fromuserwhereidin(1,2,3);这里以(开头,那么close就是结束部分,以)结束。以上都是为完成sql语句拼接所准备的,为了方便我们也可以将idin这段写入open中,理论上where后集合前的语句都可以写在open里。
我们必须要考虑其他情况如果传入的参数类型不是包装类而是其他集合该怎么写呢?假设这次我们传入一个List集合,看看会有什么变化
这时候我们的入参只有集合,那么collection的属性不能再写List的变量名了,而是需要指定类型list,一次类推set,数组用array,map等,都需要在这里指定类型,唯独包装类在collection写的是内部集合的变量名字。当我们传入的集合为map时,需要增加一个新的属性,index,用来表示map的K值,同样其他K,V结构的对象也是一样。OGNL表示要这样写,#{index},如果遍历的集合不是map时,index用来表示当前遍历的次数。
还有一个标签bind以从OGNL表达式中创建一个变量并将其绑定到上下文,请看下图
原本OGNL表达式中的内容通过pattern引用,可以写到value里面了,但是要注意写法很容易出问题,不过这真的是一个非常灵活的框架,实现一个目的有多种方法,这就是通过bind标签实现模糊查询。下面我们进行关联查询
先补充一下表连接的知识,以下方的sql语句为例
select*fromordersoleftjoinuseruono.user_id=u.id;
leftjoin查询的是orders表中所有记录与user表中的u.id=o.user_id的记录;
innerjoin查询到的是只有u.id=o.user_id的记录;
rightjoin查到的是user表中所有记录与user表中的u.id=o.user_id的记录;
fulljoin查到的是user表与orders表中所有记录。在mysql中不支持fulljoin全连接查询。
这是一个一对一查询在mapper中的写法首先看select标签中的内容,本次查询不需要入参,但是ResultType结果集在这种情形下已经是不适用了,因为我们会返回user的所有字段,此时结果集类型选择为Orders的话讲不能完成自动映射。只要是关联查询,结果集必然会用到resultMap手动映射,于是在上面创建resultMap标签,id与下面对应,类型就是Orders,如果左边的表为user的话,那么此处类型就是user。userId本是Orders里面的属性,但与数据库字段名不同所以需要手动映射,下面我们需要创建一个association标签,这个标签用于一对一关联。它的property属性是用在表示在Orders中用来表示User的变量名,所以为了让框架找到变量的类型,我们这里需要制定JavaType。然后通过单标签result来映射Orders下的user变量的属性。
这是它的java代码部分,很简单下面看一下查询结果
太多null了,是怎么回事呢?首先教大家怎么打印拼接后的sql语句,来检查是不是sql的问题。
SqlMapConfig中,全局参数配置加一条settingname=logImplvalue=LOG4J/,value的地方如果没有log4j包的话就按照我图中的写,有就写log4j即可。看到打印出的sql语句是没有问题的,那么问题一定出来映射上。我发现只要在resultMap中删除association标签,那么左表的数据都能正常显示,有了association就都变成null了,这是为什么?我们在做单表查询时,可以有选择的指定resultMap需要映射的字段,而做关联查询时则必须映射需要显示的字段,表示可以理解,如果两张表中有相同字段框架也是不知道到底该映射哪个,如果出现相同字段名必须取别名或者只显示其中一个。那么我在下面重新将为映射字段补全
查询结果
可以看到,除了数据库中本来就是null,所有信息都能正常显示了,真是一点不能省啊,这意味着如果有个字段也必须一一对应,谁都跑不了!
接下来我们看看一对多的情形
如果理解1对1关联查询,那么一对多,多对多也都是小菜一碟了。唯一的一处改动,我们将关联标签association换成了collection集合,而JavaType也变成ofType用来表示集合的泛型的类型。那么到底什么一对一与一对多有什么区别呢?当我们使用leftjoin时,需要根据实际业务需求来判断以左边的表为中心,是一对一还是一对多,还是多对多。结果如下
查询到8条数据,有条数数据与order。多对多的映射,表的左边一方不变,右边映射与一对多相同。到这里MyBatis的内容就基本结束了,MyBatis还有一个Sql类,类似hibernate的critrica对象,在java类的内部写sql语句,不过我们在mapper写sql已经足够了。再就是通过spring对MyBatis进行进一步整合,明天会讲解Spring框架的使用。持久层的框架还是相对比较简单的,但是大家一定要理解它的架构,在bug时能帮我们缩小问题范围,当然了基础足够扎实,bug几乎是不会出现的。希望各位能够在评论区积极留言进行讨论,也可以回复想要学习的知识!