目录
Mybatis的CRUD Mybatis的缓存及注解开发 源码 五、懒加载和缓存 5.1 加载(查询)时机 在一对多中,当我们有一个用户,他有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
在查询用户的时候,用户的账户信息,什么时候使用,什么时候进行查询,这种的叫做延迟加载 ,也叫懒加载 。
在查询账户的时候,账户的所属用户应该是随着账户查询时,一起查询出来,这种的叫做立即加载
通常情况
第五章以前的例子,全部都是立即加载,下面介绍懒加载 。
association懒加载 查询账户时,将所属用户一并查出来,属于一对一。
AccountDao.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="top.meethigher.demo10.dao.AccountDao" > <resultMap id ="accountUserMap" type ="Account" > <id property ="aid" column ="aid" > </id > <result property ="uid" column ="uid" > </result > <result property ="money" column ="money" > </result > <association property ="user" column ="uid" javaType ="user" select ="top.meethigher.demo10.dao.UserDao.findById" > </association > </resultMap > <select id ="findAll" resultMap ="accountUserMap" > select * from account; </select > </mapper >
UserDao.xml
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="top.meethigher.demo10.dao.UserDao" > <select id ="findById" resultType ="User" parameterType ="Integer" > select * from user where uid=#{id}; </select > </mapper >
同时要在主配置文件中开启懒加载,该属性可以在上面5.1中,找官网链接进去看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <settings > <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> </settings > <typeAliases > </typeAliases > <environments default ="mysql" > </environments > <mappers > </mappers > </configuration >
运行结果
collection懒加载 查询用户时,对账户列表进行懒加载,属于一对多。
UserDao.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="top.meethigher.demo10.dao.UserDao" > <resultMap id ="userAccountMap" type ="User" > <id property ="uid" column ="uid" > </id > <result property ="username" column ="username" > </result > <result property ="sex" column ="sex" > </result > <result property ="birthday" column ="birthday" > </result > <result property ="address" column ="address" > </result > <collection property ="accounts" column ="uid" ofType ="Account" select ="top.meethigher.demo10.dao.AccountDao.findById" > </collection > </resultMap > <select id ="findAll" resultMap ="userAccountMap" > select * from user; </select > </mapper >
AccountDao.xml
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="top.meethigher.demo10.dao.AccountDao" > <select id ="findById" parameterType ="Integer" resultType ="Account" > select * from account where uid=#{uid}; </select > </mapper >
总结
实现原理就是在使用到对象时,就会去resultMap中,找到其对应的配置,进行查询
注意association中使用javaType,collection中使用ofType
5.2 缓存 存在于内存中的临时数据
作用:减少和数据库的交互次数,提高执行效率
适用缓存的数据
经常查询的数据 不经常改变 数据正确与否对最终结果影响不大的 不适用缓存的数据
经常改变的数据 数据的正确与否对最终结果影响很大的(比如商品库存、银行汇率、股市牌价) 一级缓存 测试一级缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class CacheTest { private InputStream is; private SqlSession sqlSession; private UserDao userDao; private SqlSessionFactory factory; @Before public void init () { try { is = Resources.getResourceAsStream("Mybatis.xml" ); } catch (IOException e) { e.printStackTrace(); } factory = new SqlSessionFactoryBuilder().build(is); sqlSession = factory.openSession(); userDao= sqlSession.getMapper(UserDao.class ) ; } @After public void destroy () throws IOException { sqlSession.commit(); if (sqlSession != null ) sqlSession.close(); if (is != null ) is.close(); } @Test public void testFirstLevelCache () { User byId = userDao.findById(1 ); System.out.println(byId); User byId1 = userDao.findById(1 ); System.out.println(byId1); System.out.println(byId==byId1); sqlSession.clearCache(); userDao=sqlSession.getMapper(UserDao.class ) ; byId1=userDao.findById(1 ); System.out.println(byId1); System.out.println(byId==byId1); } }
二级缓存 概念:mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。 使用步骤让mybatis框架,支持二级缓存。在主配置文件中配置,默认是开启的,具体看官网文档 让当前的映射文件,支持二级缓存。 让当前的操作支持二级缓存(在select标签中配置) 第一步,主配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <settings > <setting name ="cacheEnabled" value ="true" /> </settings > <typeAliases > <package name ="top.meethigher.demo11.domain" /> </typeAliases > <environments default ="mysql" > </environments > <mappers > <package name ="top.meethigher.demo11.dao" /> </mappers > </configuration >
第二步与第三步,映射配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="top.meethigher.demo11.dao.UserDao" > <cache /> <select id ="findById" resultType ="User" parameterType ="Integer" useCache ="true" > select * from user where uid=#{id}; </select > </mapper >
测试二级缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class SecondCacheTest { private InputStream in; private SqlSessionFactory factory; @Before public void init () throws Exception { in = Resources.getResourceAsStream("Mybatis.xml" ); factory = new SqlSessionFactoryBuilder().build(in); } @After public void destroy () throws Exception { in.close(); } @Test public void testSecondLevelCache () { SqlSession sqlSession1 = factory.openSession(); UserDao dao1 = sqlSession1.getMapper(UserDao.class ) ; User user1 = dao1.findById(1 ); System.out.println(user1); sqlSession1.close(); SqlSession sqlSession2 = factory.openSession(); UserDao dao2 = sqlSession2.getMapper(UserDao.class ) ; User user2 = dao2.findById(1 ); System.out.println(user2); sqlSession2.close(); System.out.println(user1 == user2); } }
注意
使用二级缓存,所缓存的类一定要实现java.io.Serializable接口,这样就可以使用序列化来保存对象 二级缓存有过期时间,缓存存活时间到达一定值,就会进行清理,参考 六、注解开发 6.1 单表CRUD 修改主配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <typeAliases > <package name ="top.meethigher.demo12.domain" /> </typeAliases > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis?useSSL=false& useUnicode=true& characterEncoding=UTF-8& serverTimezone=Asia/Shanghai" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments > <mappers > <package name ="top.meethigher.demo12.dao" /> </mappers > </configuration >
注解有四个
当mybatis采用注解开发时,如果又配置了xml,那么项目就报错。
mybatis不知应该采用哪种方式进行加载。
所以在整个开发中,要不全中注解,要不全用xml
UserDao.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public interface UserDao { @Select ("select * from user" ) List<User> findAll () ; @Select ("select * from user where uid=${id}" ) User findById (Integer id) ; @Insert ("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})" ) void saveUser (User user) ; @Update ("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where uid=#{uid}" ) void updateUser (User user) ; @Delete ("delete from user where uid=${id}" ) void deleteUser (Integer id) ; @Select ("select * from user where username like #{name}" ) List<User> findByName (String name) ; @Select ("select count(*) from user" ) int findTotal () ; }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public class MybatisTest12 { private InputStream is; private SqlSession sqlSession; private UserDao userDao; @Before public void init () { try { is = Resources.getResourceAsStream("Mybatis.xml" ); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); sqlSession = factory.openSession(); userDao = sqlSession.getMapper(UserDao.class ) ; } @After public void destroy () throws IOException { sqlSession.commit(); if (sqlSession != null ) sqlSession.close(); if (is != null ) is.close(); } @Test public void testFindAll () { List<User> all = userDao.findAll(); for (User u : all) { System.out.println(u); } } @Test public void testSaveUser () { User user = new User(); user.setUsername("孙尚香" ); user.setAddress("王者荣耀" ); user.setSex("女" ); user.setBirthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" ).format(new Date())); System.out.println(user); userDao.saveUser(user); } @Test public void testUpdateUser () { User user = new User(); user.setUid(2 ); user.setAddress("腾讯" ); user.setUsername("肖战" ); userDao.updateUser(user); } @Test public void testDeleteUser () { userDao.deleteUser(4 ); } @Test public void testFindById () { User byId = userDao.findById(1 ); System.out.println(byId); } @Test public void testFindByName () { List<User> byName = userDao.findByName("%香%" ); System.out.println(byName); } @Test public void testFindTotal () { int total = userDao.findTotal(); System.out.println(total); } }
6.2 别名 如果实体类和数据库中的属性不一致,
在xml中,可以直接通过设置映射配置文件中的resultMap来设置别名。
在注解中,提供了@Results注解
在注解中,也提供了@ResultMap用来引用对应关系
value:对应关系@Results的id,可以引用多个 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public interface UserDao { @Select ("select * from user" ) @Results (id="userMap" ,value={ @Result (id=true ,column = "uid" ,property = "uid" ), @Result (column="username" ,property = "username" ), @Result (column="birthday" ,property = "birthday" ), @Result (column="sex" ,property = "sex" ), @Result (column="address" ,property = "address" ), }) List<User> findAll () ; @Select ("select * from user where uid=${id}" ) @ResultMap ("userMap" ) User findById (Integer id) ; @Select ("select * from user where username like #{name}" ) @ResultMap (value={"userMap" }) List<User> findByName (String name) ; }
注意
如果只是单次使用,value=其实是可以省略的。但一般不会,一般会设置id和value。
@Result中,id默认值为false,所以id=false可以省略。
6.3 多表查询 一对一、多对一
FetchType两个属性
EAGER:立即加载,通常对一的都是立即加载 LAZY:懒加载,通过对多的都是懒加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public interface AccountDao { @Select ("select * from account" ) @Results (id = "accountMap" , value = { @Result (id = true , column = "aid" , property = "aid" ), @Result (column = "uid" , property = "uid" ), @Result (column = "money" , property = "money" ), @Result (column = "uid" , property = "user" , one = @One (select = "top.meethigher.demo13.dao.UserDao.findById" , fetchType = FetchType.EAGER)) }) List<Account> findAll () ; @Select ("select * from account where uid=#{id}" ) Account findById (Integer id) ; }
一对多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public interface UserDao { @Select ("select * from user" ) @Results (id = "userMap" , value = { @Result (id = true , column = "uid" , property = "uid" ), @Result (column = "username" , property = "username" ), @Result (column = "birthday" , property = "birthday" ), @Result (column = "sex" , property = "sex" ), @Result (column = "address" , property = "address" ), @Result (column = "uid" , property = "accounts" , many = @Many (select = "top.meethigher.demo13.dao.AccountDao.findById" , fetchType = FetchType.LAZY)) }) List<User> findAll () ; @Select ("select * from user where uid=${id}" ) @ResultMap ("userMap" ) User findById (Integer id) ; }
6.4 缓存配置 一级缓存其实是不用担心的,同一个sqlSession,基本就没问题
测试一级缓存
1 2 3 4 5 6 @Test public void testFirstLevelCache () { User byId = userDao.findById(1 ); User byId1 = userDao.findById(1 ); System.out.println(byId==byId1); }
二级缓存
开启全局缓存,配置文件,默认就是开启的,可以不用管 通过注解开启dao接口缓存 1 2 3 4 @CacheNamespace (blocking = true )public interface UserDao { }
测试二级缓存
1 2 3 4 5 6 7 8 9 10 11 @Test public void testSecondLevelCache () { User byId = userDao.findById(1 ); System.out.println(byId); sqlSession.close(); sqlSession = factory.openSession(); userDao=sqlSession.getMapper(UserDao.class ) ; User byId1 = userDao.findById(1 ); System.out.println(byId1); System.out.println(byId==byId1); }