一:Mybatis中的延迟加载 1.延迟加载概念 1.延迟加载 概念:在真正使用数据时才发起查询,不用的时候就不查询 。也叫按需加载(懒加载)
2.立即加载 概念:不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。 多对一,一对一:通常情况下我们都是采用立即加载。
2.延迟加载的配置 我们需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。 根据API得知:配置后可延迟加载,如需查询时调用,配置value参数即可
<settings> <setting name="lazyLoadingEnabled" value="true" /> <setting name="aggressiveLazyLoading" value="false" /> </settings>
3.延迟加载实现 主要是通过 association、collection 实现一对一及一对多映射 。association、collection 具备延迟加载功能。
(1)association实现 在实现 查询账户信息同时查询用户信息 时 一对一
在账户的映射文件中(IAccountDao.xml)
select : 填写我们要调用的 select 映射的 id column : 填写我们要传递给 select 映射的参数
<!-- 建立对应关系 --> <resultMap type="account" id="accountMap" > <id column="aid" property="id" /> <result column="uid" property="uid" /> <result column="money" property="money" /> <!-- 它是用于指定从表方的调用实体属性的 --> <association property="user" javaType="user" select="com.itheima.dao.IUserDao.findById" column="uid" > </association> </resultMap> <select id="findAll" resultMap="accountMap" > select * from account </select>
而用户的映射配置文件中只需要简单的进行实现,参数用uid
<!-- 根据 id 查询 --> <select id="findById" resultType="user" parameterType="int" > select * from user where id = #{uid} </select>
(2)collection实现 同样我们也可以在一对多关系配置的结点中配置延迟加载策略。 < collection > 结点中也有 select 属性,column 属性。
<resultMap type ="user" id ="userMap" > <id column ="id" property ="id" > </id > <result column ="username" property ="username" /> <result column ="address" property ="address" /> <result column ="sex" property ="sex" /> <result column ="birthday" property ="birthday" /> <collection property ="accounts" ofType ="account" select ="com.itheima.dao.IAccountDao.findByUid" column ="id" > </collection > </resultMap >
而账户的配置映射文件中,
<select id ="findByUid" resultType ="account" parameterType ="int" > select * from account where uid = #{uid} </select >
二:Mybatis中的缓存 像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,减少和数据库的交互次数,提高执行效率。
经常查询并且不经常改变的,数据的正确与否对最终结果影响不大的 适用于缓存
1.一级缓存 它指的是Mybatis中SqlSession对象的缓存 。当我们执行查询之后,查询的结果会同时存入倒SqlSession为我们提供一块区域中。该区域的结构是一个Map。
当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用 当SqlSession对象消失时,mybatis的一级缓存也就消失了。
注:一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。
@Test public void testFindById () { User user = userDao.findById(41 ); System.out.println("第一次查询的用户:" +user); User user2 = userDao.findById(41 ); System.out.println("第二次查询用户:" +user2); System.out.println(user == user2); }
我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作 ,这就是 Mybatis 提供给我们的一级缓存在起作用了。
因为一级缓存的存在, 导致第二次查询 id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。
如果 sqlSession 去执行 commit 操作(执行插入、更新、删除) ,清空 SqlSession 中的一级缓存 ,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
@Test public void testClearlCache () { User user1 = userDao.findById(41 ); System.out.println(user1); user1.setUsername("update user clear cache" ); user1.setAddress("北京市海淀区" ); userDao.updateUser(user1); User user2 = userDao.findById(41 ); System.out.println(user2); System.out.println(user1 == user2); }
当执行sqlSession.close()后 ,再次获取sqlSession并查询id=41的User对象时,又重新执行了sql语句,从数据库进行了查询操作。
2.二级缓存 它指的是Mybatis中SqlSessionFactory对象的缓存 。由同一个SqlSessionFactory对象创建SqlSession共享其缓存。
如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交(即执行增删改操作), 将会清空该 mapper 映射下的二级缓存区域的数据 。
sqlSession2 去执行查询操作 ,查找与 sqlSession1 相同的用户信息,首先会去缓存中找是否存在数据 如果存在直接从缓存中取出数据。
二级缓存开启与关闭 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<setting name ="cacheEnabled" value ="true" /> </settings >
因为cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置 为 true 代表开启二级缓存;为false 代表不开启二级缓存
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
<cache > 标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。<mapper namespace ="com.itheima.dao.IUserDao" > <cache > </cache > </mapper >
第三步:让当前的操作支持二级缓存(在select标签中配置)
<select id ="findById" resultType ="user" parameterType ="int" useCache ="true" > select * from user where id = #{uid} </select > 将 UserDao.xml 映射文件中的<select > 标签中设置 useCache=”true”代表当前这个 statement 要使用 二级缓存,如果不使用二级缓存可以设置为 false。
注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
第四步:测试二级缓存
@Test public void testFirstLevelCache () { SqlSession sqlSession1 = factory.openSession(); IUserDao dao1 = sqlSession1.getMapper(IUserDao.class ) ; User user1 = dao1.findById(41 ); System.out.println(user1); sqlSession1.close(); SqlSession sqlSession2 = factory.openSession(); IUserDao dao2 = sqlSession2.getMapper(IUserDao.class ) ; User user2 = dao2.findById(41 ); System.out.println(user2); sqlSession2.close(); System.out.println(user1 == user2); }
我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存 ,再去执行第二次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。(factory里面数据依然存在)
三:Mybatis中的注解开发 1.环境搭建 创建SqlMapConfig.xml主配置文件,由于注解开发则不需要映射配置文件 。
<configuration > <properties resource ="jdbcConfig.properties" > </properties > <typeAliases > <package name ="com.itheima.domain" /> </typeAliases > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" > </property > <property name ="url" value ="${jdbc.url}" > </property > <property name ="username" value ="${jdbc.username}" > </property > <property name ="password" value ="${jdbc.password}" > </property > </dataSource > </environment > </environments > <mappers > <package name ="com.itheima.dao" > </package > </mappers > </configuration >
持久层接口使用注解
public interface IUserDao { @Select (value = "select * from user" ) List<User> findAll () ; ...}
然后进行典型的测试test即可
@Test public void testFindAll () { List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); }
2.单表CRUD操作(代理Dao方式) 如若我们实体类的属性名和数据库表的列名不一致 。则需要配置 @Results:封装多个结果集
@Select (value = "select * from user" ) @Results (id = "userMap" , value = { @Result (id = true , column = "id" , property = "userId" ), @Result (column = "username" , property = "userName" ), @Result (column = "address" , property = "userAddress" ), @Result (column = "sex" , property = "userSex" ), @Result (column = "birthday" , property = "userBirthday" ), } ) List<User> findAll () ;
其他方法也需要引用,则直接获取@Results的id
@Select (value = "select * from user where id = #{id}" )@ResultMap (value = {"userMap" })User findById (Integer userId) ;
3.多表查询操作 (1)使用注解实现一对一复杂关系映射 实现复杂关系映射之前我们可以在映射文件中通过配置< resultMap >来实现。
在使用注解开发时我们需要借助@Results 注解,@Result 注解,@One 注解,@Many 注解。
(1)说明: @Results 注解代替的是标签< resultMap > 该注解中可以使用单个@Result 注解,也可以使@Result 集合@Results({@Result(),@Result()})或@Results(@Result())
@Resutl 注解 代替了 < id> 标签和< result> 标签 @Result 中 属性介绍:id 是否是主键字段 column 数据库的列名 property 需要装配的属性名
one 需要使用的@One 注解(@Result(one=@One)())) many 需要使用的@Many 注解(@Result(many=@many)()))
@One 注解(一对一) 代替了< assocation> 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。 @One 注解属性介绍:select 指定用的 来多表查询的 sqlmapper fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。。 使用格式:@Result(column=” “,property=””,one=@One(select=””))
@Many 注解(多对一) 代替了< Collection> 标签, 是是多表查询的关键,在注解中用来指定子查询返回对象集合。
注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList) 但是注解中可以不定义;
使用格式:@Result(property=””,column=””,many=@Many(select=””))
(2)实体类
public class Account implements Serializable { private Integer id; private Integer uid; private Double money; private User user; ...}
(3)账户的持久层接口并使用注解配置
public interface IAccountDao {@Select ("select * from account" )@Results (id="accountMap" , value= { @Result (id=true ,column="id" ,property="id" ), @Result (column="uid" ,property="uid" ), @Result (column="money" ,property="money" ), @Result (column="uid" , property="user" , one=@One (select="com.itheima.dao.IUserDao.findById" , fetchType=FetchType.LAZY) ) }) List<Account> findAll () ;}
(2)使用注解实现一对多复杂关系映射 例如:查询用户信息时,也要查询他的账户列表。使用注解方式实现。一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。
实体类:
public class User implements Serializable { private Integer userId; private String userName; private Date userBirthday; private String userSex; private String userAddress; private List<Account> accounts;
持久层接口并使用注解配置
public interface IUserDao {@Select ("select * from user" )@Results (id="userMap" ,value= { @Result (id=true ,column="id" ,property="userId" ), @Result (column="username" ,property="userName" ), @Result (column="sex" ,property="userSex" ), @Result (column="address" ,property="userAddress" ), @Result (column="birthday" ,property="userBirthday" ), @Result (column="id" ,property="accounts" , many=@Many ( select="com.itheima.dao.IAccountDao.findByUid" , fetchType=FetchType.LAZY ) ) }) List<User> findAll () ;} @Many :相当于<collection>的配置 select 属性:代表将要执行的 sql 语句(对多的语句) fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值
4. 缓存的配置 前面提到了二级缓存在非注解开发中的配置步骤,现在学习在注解开发中的配置。
(1)首先在SqlMapConfig中开启二级缓存支持
<settings > <setting name ="cacheEnabled" value ="true" /> </settings >
(2)在持久层接口(IUserDao)中使用注解配置二级缓存
@CacheNamespace (blocking=true )public interface IUserDao { ... }