老生常谈 MyBatis 复杂查询
一对一查询
在实际开发中,经常会遇到一对一查询,一对多查询等。这里我们先来看一对一查询。例如:每本书都有一个作者,作者都有自己的属性,根据这个,我来定义两个实体类:
publicclassBook{ privateIntegerid; privateStringname; privateAuthorauthor; //省略getter/setter }
publicclassAuthor{ privateIntegerid; privateStringname; privateIntegerage; //省略getter/setter }
然后,在数据库中,添加两张表:
CREATEDATABASE/*!32312IFNOTEXISTS*/`test01`/*!40100DEFAULTCHARACTERSETutf8mb4COLLATEutf8mb4_general_ci*//*!80016DEFAULTENCRYPTION='N'*/; USE`test01`; /*Tablestructurefortable`author`*/ DROPTABLEIFEXISTS`author`; CREATETABLE`author`( `id`int(11)NOTNULLAUTO_INCREMENT, `name`varchar(255)COLLATEutf8mb4_general_ciDEFAULTNULL, `age`int(11)DEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_general_ci; /*Dataforthetable`author`*/ /*Tablestructurefortable`book`*/ DROPTABLEIFEXISTS`book`; CREATETABLE`book`( `id`int(11)NOTNULLAUTO_INCREMENT, `name`varchar(255)COLLATEutf8mb4_general_ciDEFAULTNULL, `aid`int(11)DEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_general_ci;
添加成功后,我们新建一个BookMapper,BookMapper中定义了一个查询Book的方法,但是我希望查出来Book的同时,也能查出来它的Author:
publicinterfaceBookMapper{ BookgetBookById(Integerid); }
再定义一个BookMapper.xml,内容如下:
SELECTb.*,a.`age`ASaage,a.`id`ASaid,a.`name`ASanameFROMbookb,authoraWHEREb.`aid`=a.`id`ANDb.`id`=#{id}
在这个查询SQL中,首先应该做好一对一查询,然后,返回值一定要定义成resultMap,注意,这里千万不能写错。然后,在resultMap中,来定义查询结果的映射关系。其中,association节点用来描述一对一的关系。这个节点中的内容,和resultMap一样,也是id,result等,在这个节点中,我们还可以继续描述一对一。
由于在实际项目中,每次返回的数据类型可能都会有差异,这就需要定义多个resultMap,而这多个resultMap中,又有一部份属性是相同的,所以,我们可以将相同的部分抽出来,做成一个公共的模板,然后被其他resultMap继承,优化之后的mapper如下:
SELECTb.*,a.`age`ASaage,a.`id`ASaid,a.`name`ASanameFROMbookb,authoraWHEREb.`aid`=a.`id`ANDb.`id`=#{id}
懒加载
上面这种加载方式,是一次性的读取到所有数据。然后在resultMap中做映射。如果一对一的属性使用不是很频繁,可能偶尔用一下,这种情况下,我们也可以启用懒加载。
懒加载:就是先查询book,查询book的过程中,不去查询author,当用户第一次调用了book中的author属性后,再去查询author。例如,我们再来定义一个Book的查询方法:
BookgetBookById2(Integerid); AuthorgetAuthorById(Integerid);
接下来,在mapper中定义相应的SQL:
select*frombookwhereid=#{id}; select*fromauthorwhereid=#{aid};
这里,定义association的时候,不直接指定映射的字段,而是指定要执行的方法,通过select字段来指定,column表示执行方法时传递的参数字段,最后的fetchType表示开启懒加载。当然,要使用懒加载,还需在全局配置中开启:
一对多查询
一对多查询,也是一个非常典型的使用场景。比如用户和角色的关系,一个用户可以具备多个角色。首先我们准备三个表:
SETFOREIGN_KEY_CHECKS=0; ------------------------------ --Tablestructureforrole ------------------------------ DROPTABLEIFEXISTS`role`; CREATETABLE`role`( `id`int(11)NOTNULLAUTO_INCREMENT, `name`varchar(32)DEFAULTNULL, `nameZh`varchar(32)DEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf8; ------------------------------ --Recordsofrole ------------------------------ INSERTINTO`role`VALUES('1','dba','数据库管理员'); INSERTINTO`role`VALUES('2','admin','系统管理员'); INSERTINTO`role`VALUES('3','user','用户'); ------------------------------ --Tablestructureforuser ------------------------------ DROPTABLEIFEXISTS`user`; CREATETABLE`user`( `id`int(11)NOTNULLAUTO_INCREMENT, `username`varchar(32)DEFAULTNULL, `password`varchar(255)DEFAULTNULL, `enabled`tinyint(1)DEFAULTNULL, `locked`tinyint(1)DEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf8; ------------------------------ --Recordsofuser ------------------------------ INSERTINTO`user`VALUES('1','root','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq','1','0'); INSERTINTO`user`VALUES('2','admin','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq','1','0'); INSERTINTO`user`VALUES('3','sang','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq','1','0'); ------------------------------ --Tablestructureforuser_role ------------------------------ DROPTABLEIFEXISTS`user_role`; CREATETABLE`user_role`( `id`int(11)NOTNULLAUTO_INCREMENT, `uid`int(11)DEFAULTNULL, `rid`int(11)DEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBAUTO_INCREMENT=5DEFAULTCHARSET=utf8; ------------------------------ --Recordsofuser_role ------------------------------ INSERTINTO`user_role`VALUES('1','1','1'); INSERTINTO`user_role`VALUES('2','1','2'); INSERTINTO`user_role`VALUES('3','2','2'); INSERTINTO`user_role`VALUES('4','3','3'); SETFOREIGN_KEY_CHECKS=1;
这三个表中,有用户表,角色表以及用户角色关联表,其中用户角色关联表用来描述用户和角色之间的关系,他们是一对多的关系。然后,根据这三个表,创建两个实体类:
publicclassUser{ privateIntegerid; privateStringusername; privateStringpassword; privateListroles; //省略setter/getter }
publicclassRole{ privateIntegerid; privateStringname; privateStringnameZh; //省略setter/getter }
接下来,定义一个根据id查询用户的方法:
UsergetUserById(Integerid);
然后,定义该方法的实现:
SELECTu.*,r.`id`ASrid,r.`name`ASrname,r.`nameZh`ASrnameZhFROMUSERu,roler,user_roleurWHEREu.`id`=ur.`uid`ANDur.`rid`=r.`id`ANDu.`id`=#{id}
在resultMap中,通过collection节点来描述集合的映射关系。在映射时,会自动将一的一方数据集合并,然后将多的一方放到集合中,能实现这一点,靠的就是id属性。当然,这个一对多,也可以做成懒加载的形式,那我们首先提供一个角色查询的方法:
ListgetRolesByUid(Integerid);
然后,在XML文件中,处理懒加载:
select*fromuserwhereid=#{id}; SELECTr.*FROMroler,user_roleurWHEREr.`id`=ur.`rid`ANDur.`uid`=#{id}
定义完成之后,我们的查询操作就实现了懒加载功能。
查询缓存
MyBatis一级缓存
Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
publicclassMain2{ publicstaticvoidmain(String[]args){ SqlSessionFactoryinstance=SqlSessionFactoryUtils.getInstance(); SqlSessionsqlSession=instance.openSession(); BookMappermapper=sqlSession.getMapper(BookMapper.class); UserMapperuserMapper=sqlSession.getMapper(UserMapper.class); Useruser=userMapper.getUserById(1); user=userMapper.getUserById(1); user=userMapper.getUserById(1); System.out.println(user.getUsername()); } }
多次查询,只执行一次SQL。但是注意,如果开启了一个新的SqlSession,则新的SqlSession无法就是之前的缓存,必须是同一个SqlSession中,缓存才有效。
MyBatis二级缓存
Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。
到此这篇关于老生常谈MyBatis复杂查询的文章就介绍到这了,更多相关MyBatis复杂查询内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!