1、Mybatis中的延迟加载
问题:在一对多中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询账户的,而不是只要一查用户就把账户查出来。延迟加载
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询用户出来。立即加载
什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。 要查的是一个集合,空间大!
多对一,一对一:通常情况下我们都是采用立即加载。
创建项目模块day04_eesy_01lazy 把day03中的day03_eesy_03one2many项目代码拷贝到当前新创建的这个项目中
一对一时使用 assocation 实现延迟加载
(这个标签之前是一对一时在result里写user的,下面是之前的代码
<!-- 定义封装account和user的resultMap --> <resultMap id="accountUserMap" type="account"> <!--主键 属性与列名--> <id property="id" column="aid"></id> <!--非主键--> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容--> <association property="user" column="uid" javaType="user"> <id property="id" column="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </association> </resultMap>
修改IAccountDao映射文件,需要实现延迟加载
看现在的assocation标签代码!!!
<!-- 定义封装account和user的resultMap --> <resultMap id="accountUserMap" type="account"> <!--主键 属性与列名--> <id property="id" column="id"></id> <!--非主键--> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容 延迟加载加上select属性指定的内容:查询用户唯一标识(在还能传入方法了。。是IUserDao.xml里面的方法sql语句 column属性必写上:用户根据id查询时,所需要的id是谁有它指出--> <association property="user" column="uid" javaType="user" select="com.xxw.dao.IUserDao.findById"> </association> </resultMap>
<!-- 查询所有账户同时包含用户名和地址信息 -->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
在主配置文件SqlMapConfig.xml中添加标签,开启延迟加载
<!--开启Mybatis支持延迟加载-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
一对多时使用 Collection 实现延迟加载
同上改集合标签,增加seclet属性,关联第二层方法
<collection property="accounts" ofType="account" select="com.xxw.dao.IAccountDao.findAccountByUid" column="id"> </collection>
再要在接口中增加这个方法。具体见pdf 跳跳跳,别看了,后面用注解更好!!!!!
2、Mybatis中的缓存
什么是缓存
存在于内存中的临时数据。
为什么使用缓存
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
Mybatis中的一级缓存和二级缓存
一级缓存:
它指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时 sqlsession.close或.cleancache,mybatis的一级缓存也就消失了。数据库有commit也会消失。
!!!!!!!!!有用的!!!!!到时候剪切遍历数据库,有些是重复的。但是我是以集合或数组的方式取数据,也可以吗?
<select id="findById" resultType="UsEr" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings>
<!-- 开启二级缓存的支持 --> <setting name="cacheEnabled" value="true"/> </settings>
因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为 false 代表不开启二级缓存
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 开启二级缓存的支持 -->
<cache></cache>
</mapper>
第三步:让当前的操作支持二级缓存(在select标签中配置)
同一级的标签
编写测试类:见pdf吧
经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。
3、Mybatis中的注解开发
Mybatis 也可以使用注解开发方式,这样我们就可以减少编写 Mapper 映射 文件.xml了。
环境搭建
创建项目day04_eesy_03annotation_mybatis,导入坐标
* 在mybatis中针对,CRUD一共有四个注解
* @Select @Insert @Update @Delete
当有同路径xml文件时,就算没有配置,也会干扰注解形式开发,所以不要写xml的映射文件了
单表CRUD操作(代理Dao方式)
public interface IUserDao { /** * 查询所有用户 * * 在mybatis中针对,CRUD一共有四个注解 * * @Select @Insert @Update @Delete */ @Select("select * from user") List<User> findAll(); /** * 插入用户保存 * * @param user */ @Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})") void saveUser(User user); /** * 更新用户 * * @param user */ @Update("update user set username = #{username},sex = #{sex},birthday = #{birthday},address = #{address} where id = #{id}") void updateUser(User user); /** * 删除用户 * * @param id */ @Delete("delete from user where id = #{id}") void deleteUser(Integer id); /** * 根据id查找 * @param id * @return */ @Select("select * from user where id = #{id}") User findById(Integer id); /** * 模糊查找 * @param username * @return */ @Select("select * from user where username like #{username}") List<User> findByName(String username); /** * 聚合函数查总数 * @return */ @Select("select count(id) from user") int findTotal(); }
public class AnnotationCRUDTest { private InputStream in; private SqlSessionFactory factory; private SqlSession session; private IUserDao userDao; @Before public void init()throws Exception{ in = Resources.getResourceAsStream("SqlMapConfig.xml"); factory = new SqlSessionFactoryBuilder().build(in); session = factory.openSession(); userDao = session.getMapper(IUserDao.class); } @After public void destroy()throws Exception{ session.commit(); session.close(); in.close(); } /** * 测试查询所有 */ @Test public void testFindAll(){ //5.执行查询所有方法 List<User> users = userDao.findAll(); for(User user : users){ System.out.println(user); } } @Test public void testSave(){ User user = new User(); user.setUsername("mybatis annotation"); user.setAddress("北京市昌平区"); user.setBirthday(new Date()); userDao.saveUser(user); } @Test public void testUpdate(){ User user = new User(); user.setId(50); user.setUsername("annotation update"); user.setAddress("北京市海淀区"); user.setSex("男"); user.setBirthday(new Date()); userDao.updateUser(user); } @Test public void testDelete(){ userDao.deleteUser(53); } @Test public void testFindById(){ User user = userDao.findById(50); System.out.println(user); } @Test public void testFindByName(){ List<User> users = userDao.findByName("%mybatis%");//模糊查询 for(User user : users){ System.out.println(user); } } @Test public void testFindTotal(){ int total = userDao.findTotal(); System.out.println(total); } }
mybatis注解建立实体类属性和数据库表中列的对应关系
创建项目day04_eesy_04annoOne2Many 导入上一个工程的代码
修改如下:dao接口中只留下关于查询的方法即可(这里方便多了,改接口类即可,不用再像之前还得改对应xml文件)
修改实体类,让表中的字段不能对应实体类中的属性!!!下图
(之前xml是用别名解决,resultmap里面的标签
这里是用results注解了!!!!(那之后一对一的asscousion和多对多的collection应该也是在这个result注解里面?不是,换了。。。
public interface IUserDao { /** * 查询所有用户 * * 在mybatis中针对,CRUD一共有四个注解 * * @Select @Insert @Update @Delete */ @Select("select * from user") @Results(id = "userMap",value = { // 这个results后面的id=usermap是提供该注解的唯一标识,让别的地方可以调用 // 开始起别名是属性与列名对应 主键是id所以true? @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(); /** * 根据id查找 * @param id * @return */ @Select("select * from user where id = #{id}") // 用@resultmap注解引用唯一标识名称就能用 @ResultMap("userMap") User findById(Integer id); /** * 模糊查找 * @param username * @return */ @Select("select * from user where username like #{username}") @ResultMap("userMap") List<User> findByName(String username); }
多表查询操作
一对一
之前学的就是在一的实体类建立多的实体类属性,实现它的setget方法,然后<association>标签里写它的映射
这里不用association了,用@One这个注解实现一对一还顺便选择饿加载模式,用到select+全限定类名对应方法名(后面查用户的方法)和fetchtype选择加载模式
/** * 查询所有账户,并获取每个帐户所对应的用户信息(一对一多表查询) * @return */ @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(property = "user",column = "uid",one=@One(select="com.xxw.dao.IUserDao.findById",fetchType= FetchType.EAGER)) }) List<Account> findAll();
彩色部分方法名想好久才想明白,它是后1/2的方法查一的one!!查用户信息的,通过uid查,方法是IUserDao接口的findById方法,实现这个方法是饿加载!!!!!!!难怪我之前第一章没太懂。。。
一对多
之前学的就是在一的实体类建立多的集合实体类属性,实现该集合setget方法,然后<collection>标签里写集合的映射
用户找账户,账户的方法是后1/2所以要写找账户方法的全名!!!!!和他的加载模式(延迟加载)!!!!它还是多,所以是在@many里面写
/**IAccountDao.java里面的方法 * 根据用户id查询账户信息 * @param userId * @return */ @Select("select * from account where uid = #{userId}") List<Account> findAccountByUid(Integer userId);
IUserDao接口加上
@Select("select * from user")
@Results(id = "userMap",value = {
// 这个results后面的id=usermap是提供该注解的唯一标识,让别的地方可以调用
// 开始起别名是属性与列名对应 主键是id所以true?
@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"),
@Result(property = "accounts",column = "id", many = @Many(
select = "com.xxw.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY))
}
)
List<User> findAll();
缓存的配置
不管xml还是注解,一级缓存都是自动开启的。
在 SqlMapConfig 中开启二级缓存支持
<!-- 配置二级缓存 -->
<settings>
<!-- 开启二级缓存的支持 --> 这其实也是默认的不写也行
<setting name="cacheEnabled" value="true"/>
</settings>
在持久层接口中使用注解配置二级缓存
开二级缓存 这个要写!!!!!!
@CacheNamespace(blocking=true) //mybatis 基于注解方式实现配置二级缓存
public interface IUserDao { }
运行日志显示user1并未从数据库获取而是拿的user的 同一工厂的缓存!!!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mybatis4天学习完毕。pdf资料上讲得挺详细的,感觉mybatis的复习只需要复习day4的第三章注解开发就行。mybatis底层就是day1自定义框架讲得挺详细的。