2026/3/7 13:30:13
网站建设
项目流程
企业建设网站公司哪家好,优惠活动推广文案,运城市住房和城乡建设局网站,如何建立网站会员系统吗Mybatis任务三#xff1a;加载策略及注解开发一 MyBatis加载策略 1.1 什么是延迟加载#xff1f; 问题 通过前面的学习#xff0c;我们已经掌握了Mybatis中一对一#xff0c;一对多#xff0c;多对多关系的配置及实现#xff0c;可以实现 对象的关联查询。实际开发过程中…Mybatis任务三加载策略及注解开发一MyBatis加载策略1.1什么是延迟加载问题通过前面的学习我们已经掌握了Mybatis中一对一一对多多对多关系的配置及实现可以实现 对象的关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加他的订单 信息。此时就是我们所说的延迟加载。举个栗子* 在一对多中当我们有一个用户它有个100个订单 在查询用户的时候要不要把关联的订单查出来 在查询订单的时候要不要把关联的用户查出来 * 回答 在查询用户时用户下的订单应该是什么时候用什么时候查询。 在查询订单时订单所属的用户信息应该是随着订单一起查询出来。延迟加载就是在需要用到数据时才进行加载不需要用到数据时就不加载数据。延迟加载也称懒加载。* 优点 先从单表查询需要时再从关联表去关联查询大大提高数据库性能因为查询单表要比关联查询多张表 速度要快。 * 缺点 因为只有当需要用到数据时才会进行数据库查询这样在大批量数据查询时因为查询工作也要消耗时 间所以可能造成用户等待时间变长造成用户体验下降。 * 在多表中 一对多多对多通常情况下采用延迟加载 一对一多对一通常情况下采用立即加载 * 注意 延迟加载是基于嵌套查询来实现的1.2实现1.2.1局部延迟加载在association和collection标签中都有一个fetchType属性通过修改它的值可以修改局部的加载策 略。!-- 开启一对多 延迟加载 -- resultMap iduserMap typeuser id columnid propertyid/id result columnusername propertyusername/result result columnpassword propertypassword/result result columnbirthday propertybirthday/result !-- fetchTypelazy 懒加载策略 fetchTypeeager 立即加载策略 -- collection propertyorderList ofTypeorder columnid selectcom.lagou.dao.OrderMapper.findByUid fetchTypelazy /collection /resultMap select idfindAll resultMapuserMap SELECT * FROM user /select1.2.2设置触发延迟加载的方法大家在配置了延迟加载策略后发现即使没有调用关联对象的任何方法但是在你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询。我们可以在配置文件中使用lazyLoadTriggerMethods配置项覆盖掉上面四个方法。settings !--所有方法都会延迟加载-- setting namelazyLoadTriggerMethods valuetoString()/ /settings1.2.3全局延迟加载在Mybatis的核心配置文件中可以使用setting标签修改全局的加载策略settings !--开启全局延迟加载功能-- setting namelazyLoadingEnabled valuetrue/ /settings注意局部的加载策略优先级高于全局的加载策略。!-- 关闭一对一 延迟加载 -- resultMap idorderMap typeorder id columnid propertyid/id result columnordertime propertyordertime/result result columntotal propertytotal/result !-- fetchTypelazy 懒加载策略 fetchTypeeager 立即加载策略 -- association propertyuser columnuid javaTypeuser selectcom.lagou.dao.UserMapper.findById fetchTypeeager /association /resultMap select idfindAll resultMaporderMap SELECT * from orders /select二MyBatis缓存2.1为什么使用缓存当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中。当用户再次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询。减少网络连接和数据库查询带来的损耗,从而提高我们的查询效率,减少高并发访问带来的系统性能问题。一句话概括经常查询一些不经常发生变化的数据使用缓存来提高查询效率。像大多数的持久化框架一样Mybatis也提供了缓存策略通过缓存策略来减少数据库的查询次数从而提高性能。Mybatis中缓存分为一级缓存二级缓存。2.2一级缓存2.2.1介绍一级缓存是SqlSession级别的缓存是默认开启的所以在参数和SQL完全一样的情况下我们使用同一个SqlSession对象调用一个Mapper方法往往只执行一次SQL因为使用SelSession第一次查询后MyBatis会将其放在缓存中以后再查询的时候如果没有声明需要刷新并且缓存没有超时的情况下SqlSession都会取出当前缓存的数据而不会再次发送SQL到数据库。4.2.2验证Test public void testOneCache() throws Exception { SqlSession sqlSession MyBatisUtils.openSession(); UserMapper userMapper sqlSession.getMapper(UserMapper.class); User user1 userMapper.findById(1); System.out.println(第一次查询的用户 user1); User user2 userMapper.findById(1); System.out.println(第二次查询的用户 user2); sqlSession.close(); }我们可以发现虽然在上面的代码中我们查询了两次但最后只执行了一次数据库操作这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在导致第二次查询id为1的记录时并没有发出sql语句从数据库中查询数据而是从一级缓存中查询。4.2.3分析一级缓存是SqlSession范围的缓存执行SqlSession的C增加U更新D删除操作或者调用clearCache()、commit()、close()方法都会清空缓存。1. 第一次发起查询用户id为41的用户信息先去找缓存中是否有id为41的用户信息如果没有从数据库 查询用户信息。 2. 得到用户信息将用户信息存储到一级缓存中。 3. 如果sqlSession去执行commit操作执行插入、更新、删除清空SqlSession中的一级缓存这 样做的目的为了让缓存中存储的是最新的信息避免脏读。 4. 第二次发起查询用户id为41的用户信息先去找缓存中是否有id为41的用户信息缓存中有直接从缓 存中获取用户信息。4.2.4清除Test public void testClearOneCache() throws Exception { SqlSession sqlSession MybatisUtils.openSession(); UserMapper userMapper sqlSession.getMapper(UserMapper.class); User user1 userMapper.findById(41); System.out.println(第一次查询的用户 user1); //调用sqlSession清除缓存的方法 sqlSession.clearCache(); User user2 userMapper.findById(41); System.out.println(第二次查询的用户 user2); }!-- 每次查询时都会清除缓存 -- select flushCachetrue/select2.3二级缓存2.3.1介绍二级缓存是namspace级别跨sqlSession的缓存是默认不开启的二级缓存的开启需要进行配置实现二级缓存的时候MyBatis要求返回的POJO必须是可序列化的。也就是要求实现Serializable接口配置方法很简单只需要在映射XML文件配置cache/就可以开启二级缓存了。2.3.2验证a配置核心配置文件settings !-- 因为cacheEnabled的取值默认就为true所以这一步可以省略不配置。 为true代表开启二级缓存为false代表不开启二级缓存。 -- setting namecacheEnabled valuetrue/ /settingsb配置UserMapper.xml映射mapper namespacecom.lagou.dao.UserMapper !--当前映射文件开启二级缓存-- cache/cache !-- select标签中设置useCache”true”代表当前这个statement要使用二级缓存。 如果不使用二级缓存可以设置为false 注意 针对每次查询都需要最新的数据sql要设置成useCachefalse禁用二级缓存。 -- select idfindById parameterTypeint resultTypeuser useCachetrue SELECT * FROM user where id #{id} /select /mapperc修改User实体public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; private ListRole roleList; private ListOrder orderList; }d测试结果Test public void testTwoCache() throws Exception { SqlSession sqlSession MyBatisUtils.openSession(); UserMapper userMapper sqlSession.getMapper(UserMapper.class); User user userMapper.findById(41); System.out.println(第一次查询的用户 user); sqlSession.close(); SqlSession sqlSession1 MyBatisUtils.openSession(); UserMapper userMapper1 sqlSession1.getMapper(UserMapper.class); User user1 userMapper1.findById(41); System.out.println(第二次查询的用户user1); sqlSession1.close(); }2.3.3分析二级缓存是mapper映射级别的缓存多个SqlSession去操作同一个Mapper映射的sql语句多个SqlSession可以共用二级缓存二级缓存是跨SqlSession的。1. 映射语句文件中的所有select语句将会被缓存。 2. 映射语句文件中的所有insert、update和delete语句会刷新缓存。2.3.4注意问题脏读mybatis的二级缓存因为是namespace级别所以在进行多表查询时会产生脏读问题2.4小结1. mybatis的缓存都不需要我们手动存储和获取数据。mybatis自动维护的。 2. mybatis开启了二级缓存后那么查询顺序二级缓存--》一级缓存--》数据库 2. 注意mybatis的二级缓存会存在脏读问题需要使用第三方的缓存技术解决问题。三MyBatis注解3.1 MyBatis常用注解这几年来注解开发越来越流行Mybatis也可以使用注解开发方式这样我们就可以减少编写Mapper映射文件了。我们先围绕一些基本的CRUD来学习再学习复杂映射多表操作。* Insert实现新增代替了insert/insert * Delete实现删除代替了delete/delete * Update实现更新代替了update/update * Select实现查询代替了select/select * Result实现结果集封装代替了result/result * Results可以与Result 一起使用封装多个结果集代替了resultMap/resultMap * One实现一对一结果集封装代替了association/association * Many实现一对多结果集封装代替了collection/collection3.2 MyBatis注解的增删改查【重点】3.2.1创建UserMapper接口public interface UserMapper { Select(SELECT * FROM user) public ListUser findAll(); Insert(INSERT INTO user(username,birthday,sex,address) VALUES(# {username},#{birthday},#{sex},#{address})) public void save(User user); Update(UPDATE user SET username #{username},birthday #{birthday},sex #{sex},address #{address} WHERE id #{id}) public void update(User user); Delete(DELETE FROM user where id #{id}) public void delete(Integer id); }3.2.2编写核心配置文件!--我们使用了注解替代的映射文件所以我们只需要加载使用了注解的Mapper接口即可-- mappers !--扫描使用注解的Mapper类-- mapper classcom.lagou.mapper.UserMapper/mapper /mappers!--或者指定扫描包含映射关系的接口所在的包也可以-- mappers !--扫描使用注解的Mapper类所在的包-- package namecom.lagou.mapper/package /mappers3.2.3测试代码public class TestUser extends TestBaseMapper { // 查询 Test public void testFindAll() throws Exception { UserMapper userMapper sqlSession.getMapper(UserMapper.class); ListUser list userMapper.findAll(); for (User user : list) { System.out.println(user); } } // 添加 Test public void testSave() throws Exception { UserMapper userMapper sqlSession.getMapper(UserMapper.class); User user new User(); user.setUsername(于谦); user.setBirthday(new Date()); user.setSex(男); user.setAddress(北京德云社); userMapper.save(user); } // 更新 Test public void testUpdate() throws Exception { UserMapper userMapper sqlSession.getMapper(UserMapper.class); User user new User(); user.setId(49); user.setUsername(郭德纲); user.setBirthday(new Date()); user.setSex(男); user.setAddress(北京德云社); userMapper.update(user); } // 删除 Test public void testDelete() throws Exception { UserMapper userMapper sqlSession.getMapper(UserMapper.class); userMapper.delete(49); } }3.3使用注解实现复杂映射开发之前我们在映射文件中通过配置resultMap、association、collection来实现复杂关系映射。使用注解开发后我们可以使用Results、ResultOne、Many注解组合完成复杂关系的配置。3.4一对一查询3.4.1介绍需求查询一个订单与此同时查询出该订单所属的用户一对一查询语句SELECT * FROM orders; SELECT * FROM user WHERE id #{订单的uid};3.4.2代码实现aOrderMapper接口public interface OrderMapper { Select(SELECT * FROM orders) Results({ Result(id true, column id, property id), Result(column ordertime, property ordertime), Result(column money, property money), Result(property user, javaType User.class, column uid, one One(select com.lagou.mapper.UserMapper.findById, fetchType FetchType.EAGER)) }) public ListOrder findAllWithUser(); }bUserMapper接口public interface UserMapper { Select(SELECT * FROM user WHERE id #{id}) public User findById(Integer id); }c测试代码Test public void testOrderWithUser() throws Exception { OrderMapper orderMapper sqlSession.getMapper(OrderMapper.class); ListOrder list orderMapper.findAllWithUser(); for (Order order : list) { System.out.println(order); } }3.5一对多查询3.5.1介绍需求查询一个用户与此同时查询出该用户具有的订单一对多查询语句SELECT * FROM user; SELECT * FROM orders where uid #{用户id};3.5.2代码实现aUserMapper接口public interface UserMapper { Select(SELECT * FROM user) Results({ Result(id true, column id, property id), Result(column brithday, property brithday), Result(column sex, property sex), Result(column address, property address), Result(property orderList, javaType List.class, column id , many Many(select com.lagou.mapper.OrderMapper.findByUid)) }) public ListUser findAllWithOrder(); }bOrderMapper接口public interface OrderMapper { Select(SELECT * FROM orders WHERE uid #{uid}) public ListOrder findByUid(Integer uid); }c测试代码Test public void testUserWithOrder() throws Exception { UserMapper userMapper sqlSession.getMapper(UserMapper.class); ListUser list userMapper.findAllWithOrder(); for (User user : list) { System.out.println(user); } }3.6多对多查询3.6.1介绍需求查询所有用户同时查询出该用户的所有角色多对多查询语句SELECT * FROM user; SELECT * FROM role r INNER JOIN user_role ur ON r.id ur.rid WHERE ur.uid #{用户id};3.6.2代码实现aUserMapper接口public interface UserMapper { Select(SELECT * FROM user) Results({ Result(id true, column id, property id), Result(column brithday, property brithday), Result(column sex, property sex), Result(column address, property address), Result(property roleList, javaType List.class, column id , many Many(select com.lagou.mapper.RoleMapper.findByUid)) }) public ListUser findAllWithRole(); }bRoleMapper接口public interface RoleMapper { Select(SELECT * FROM role r INNER JOIN user_role ur ON r.id ur.rid WHERE ur.uid #{uid}) public ListRole findByUid(Integer uid); }c测试代码Test public void testUserWithRole() throws Exception { UserMapper userMapper sqlSession.getMapper(UserMapper.class); ListUser list userMapper.findAllWithRole(); for (User user : list) { System.out.println(user); } }3.7基于注解的二级缓存5.7.1配置SqlMapConfig.xml文件开启二级缓存的支持settings !-- 因为cacheEnabled的取值默认就为true所以这一步可以省略不配置。 为true代表开启二级缓存为false代表不开启二级缓存。 -- setting namecacheEnabled valuetrue/ /settings3.7.2在Mapper接口中使用注解配置二级缓存CacheNamespace public interface UserMapper {...}3.8注解延迟加载不管是一对一还是一对多 在注解配置中都有fetchType的属性* fetchType FetchType.LAZY 表示懒加载 * fetchType FetchType.EAGER 表示立即加载 * fetchType FetchType.DEFAULT 表示使用全局配置3.9小结* 注解开发和xml配置优劣分析 1.注解开发和xml配置相比从开发效率来说注解编写更简单效率更高。 2.从可维护性来说注解如果要修改必须修改源码会导致维护成本增加。xml维护性更强。