当前位置: 代码迷 >> 综合 >> 【ssm入门#4-MyBatis】延迟加载(一对一、一对多)+缓存机制(一级缓存、二级缓存)+注解开发(CRUD、属性映射、一对一、一对多、接口中注解开启二级缓存)+IDEA:找不到主类错误
  详细解决方案

【ssm入门#4-MyBatis】延迟加载(一对一、一对多)+缓存机制(一级缓存、二级缓存)+注解开发(CRUD、属性映射、一对一、一对多、接口中注解开启二级缓存)+IDEA:找不到主类错误

热度:61   发布时间:2024-01-29 14:52:50.0

延迟加载


一对一

在MyBatis中,一对一的实现是靠<association>标签实现的,设置第二步查询的入口属性column赋予参数,传递给select此属性指向的从表实体类的操作方法,即可实现。还要注意延迟加载<configuration><environments>标签前设置延迟加载开关:

<settings><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/>
</settings>

实质上延迟加载需要到用到主表从表两个Dao配置文件,主表配置文件中只需要写查询自己的SQL语句,并把参数传过去调用从表属性的配置文件实现封装。
在这里插入图片描述
在这里插入图片描述
当测试方法只是单纯调用延迟加载方法时,并未对查询得到的结果操作,这时只是会执行主表配置文件的查询语句,而后续对从表实体类的查询并未进行。当然主表实体类一经操作,自然调用所有查询,即从表实体类也会立即查询得到结果并封装。
第一步查询进行时

缓存机制


一级缓存
myBatis中, dao操作类接口是由 sqlSession代理实现的,所以多个不同的 dao可以由同一个 sqlSession实现,此时不同的 dao拥有同样的一级缓存,进行同样的查询操作不会重新执行,即一级缓存中拥有同一的缓存对象。当 sqlSession关闭或者清除缓存之后,两个 dao将会执行两次同样的查询。

 sqlSession.clearCache();//清除sqlSession.close();//关闭

值得注意,一级缓存是 SqlSession 范围的缓存,当调用 SqlSession修改添加删除commit()close()等方法时,就会清空一级缓存。

二级缓存
myBatis中,二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。 由于SqlSessionFactory中开启的SqlSession,二级缓存是在SqlSessionFactory对象的缓存区上。当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化 方式来保存对象。

在一级缓存关闭的情况下,即使二级缓存正确开启使用,所取出的缓存对象也是不一致的,主要是二级缓存是通过存储序列化信息方式保存的对象内容,反序列化就重新实例化了新对象。但是SQL语句确实只是执行了一遍。

开启步骤:
第一步:在 主配置文件SqlMapConfig.xml 开启二级缓存

<settings> <!-- 开启二级缓存的支持 -->  <setting name="cacheEnabled" value="true"/> </settings> 
因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为 false 代表不开启二级缓存。 

第二步:配置相关的 Mapper 映射文件

<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。 
<?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="com.itheima.dao.IUserDao">  
<!-- 开启二级缓存的支持 -->  
<cache></cache> 
</mapper> 

第三步:配置 statement 上面的 useCache 属性

<!-- 根据 id 查询 --> 
<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,禁用二级缓存。

注解开发


基本CRUD

//注解方式,只需要传递String[]类型单个参数value,可以省略value=//注解方式,只需要传递String[]类型单个数组元素,大括号也可省略//查询所有操作@Select("select * from user")List<User> findAll();//添加User操作@Insert("insert into user(username,birthday,sex,address) " +"values(#{username},#{birthday},#{sex},#{address})")int saveUser(User user);//更新User@Update("update user set username=#{username}," +"birthday=#{birthday},sex=#{sex},address=#{address} " +"where id=#{id}" )void updateUser(User user);//删除User//因为传入参数没有涉及到User类,只是简单参数类型,#{}里面可以乱填@Delete("delete from user where id=#{i} ")void deleteUser(Integer userId);//查询具体ID用户@Select("select * from user where id=#{i} ")User findById(Integer userId);//模糊查询//传入参数String也是基本数据类型对象,随意指定#{}里面内容//@Select("select * from user where username like #{name}")@Select("select * from user where username like '%${value}%' ")List<User> findUserByNameFuzzy(String username);//聚合函数的应用@Select("select count(*) from user ")int findTotal();

实体类与数据库表属性映射问题

 @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")  })
//下面方式复用映射@Select("select * from user where id = #{uid} ")  @ResultMap("userMap") 

一对一
主表Dao操作类中的方法注释配置:
在这里插入图片描述
一对多
主表Dao操作类中的方法注释配置:
在这里插入图片描述
一对一、一对多差别在于内置对象是单个或者多个组成的集合,和基于XML的配置方式思想如出一辙,具体根据情况选择合适的参数。
开启二级缓存
在这里插入图片描述
可以发现,SqlSession代理实现同一个接口时,放在缓存区的同一对象不会再次查询,这是一级缓存,是基于SqlSession对象的缓存区。无论基于XML还是注解开发都是默认开启的。

关于二级缓存,接口注解要设置(没有默认开启):

@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存 
public interface IUserDao {} 

同时主配置文件SqlMapConfig.xml中也要开启(虽然是默认开启的):

<!-- 配置二级缓存 --> 
<settings> <!-- 开启二级缓存的支持 -->  <setting name="cacheEnabled" value="true"/> </settings> 

关于开启二级缓存的实验:

@Testpublic void testLevel2Cache(){/*** 不同的Session,不同的缓存区,前一个没有关闭的话,二级缓存没写入,后一个找不到同语句对应对象,会再次发起查询* 不同的Session,不同的缓存区,关闭前一个的话,后一个会在二级缓存读取数据,只会查询一次*/SqlSession sqlSession0=sqlSessionFactory.openSession();IUserDao iUserDao1=sqlSession0.getMapper(IUserDao.class);User user=iUserDao1.findById(46);System.out.println(user);sqlSession0.close();SqlSession sqlSession1=sqlSessionFactory.openSession();System.out.println("SqlSession是否同一个");System.out.println(sqlSession0==sqlSession1);IUserDao iUserDao2=sqlSession1.getMapper(IUserDao.class);User user1=iUserDao2.findById(46);System.out.println(user1);System.out.println(user==user1);sqlSession1.close();/*** 同一个Session,一级缓存清空,会查询两次,不清空只会查询一次*//* IUserDao iUserDao1=sqlSession.getMapper(IUserDao.class);User user=iUserDao1.findById(46);System.out.println(user);sqlSession.clearCache();IUserDao iUserDao2=sqlSession.getMapper(IUserDao.class);User user1=iUserDao2.findById(46);System.out.println(user1);System.out.println(user==user1);*/}

在这里插入图片描述
对于只执行了一遍查询语句,动用不同SqlSession也能输出两次同样的数据必定存在非SqlSession的缓存区(一级缓存),正是二级缓存起作用。至于为什么对象信息一样,却不是同一个,已经做过记录。

有个现象是,在前一个SqlSession未关闭时,后一个SqlSession发起查询操作,也会调用查询语句,并非直接在二级缓存中取数据。不排除二级缓存的保存操作是在上一个SqlSession关闭后,二级缓存才写入数据。后一个一开始在二级缓存区检索并无对应对象,才会直接再次发起查询。

IDEA:找不到主类错误

动手改着项目结构增加几个小模块时,突然出现了这么一个错误,借鉴前辈的解决方法:
IDEA错误: 找不到或无法加载主类解决方法
这个时候啥子编译错误的信息提示都没有,就很慌,丈二和尚摸不着头脑。基本和这位大佬一样的情况,原因大概是:乱改项目结构会导致整个项目标记资源文件夹和子模块的混乱交叉,从而编译的时候出的错找不出原因。同样最后打开项目结构把大项目的文件夹的资源标记全部清除干净,完事。很底层的错误,虽然解决不费力气但还是希望不要遇到狗咬刺猬的下场。

  相关解决方案