ORM框架回顾-myabtis3

mybatis的缓存机制

mybatis的懒加载机制

mybatis的注解开发

mybatis的注解开发-crud操作

mybatis的注解开发-一对一关联查询

mybatis的注解开发-一对多关联查询

mybatis的缓存主要有基于SqlSession的一级缓存和基于SqlSessionFactory的二级缓存,首先我们先来看一下mybatis的一级缓存。

mybatis的一级缓存

<select id="findUserById" parameterType="int" resultType="user">
        select * from user where id=#{id}
</select>
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}    

User findUserById(Integer id);
//测试代码
public class Test2 {
    private SqlSession sqlSession;

    private UserDao userDao;

    private AccountDao accountDao;

    private RoleDao roleDao;

    private InputStream input;

    @BeforeEach
    public void init() {
        try {
            input = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory build = new SqlSessionFactoryBuilder().
                    build(input);
            sqlSession = build.openSession(true);
            userDao = sqlSession.getMapper(UserDao.class);
            accountDao = sqlSession.getMapper(AccountDao.class);
            roleDao = sqlSession.getMapper(RoleDao.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @AfterEach
    public void destroy() {
        try {
            sqlSession.close();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Test
    public void findByUserId() {
        //调用两次查询语句,如果不存在缓存的话,那么一定是执行
        //两次sql语句的查询操作,下面我们来看看结果来简单分析一下
        System.out.println(userDao.findUserById(45));
        System.out.println(userDao.findUserById(45));
    }
}

在这里插入图片描述
我们可以看到这里调用了两次查询操作,但是只发送了一次查询的sql语句操作,说明在SqlSession级别的确是存在缓存的,接下来我们修改一下查询操作,先调用SqlSession的clearCache方法再去查询。

@Test
    public void findByUserId() {
        System.out.println(userDao.findUserById(45));
        sqlSession.clearCache();
        System.out.println(userDao.findUserById(45));
    }

在这里插入图片描述
在我们调用了clear操作后,同样的查询语句这次发送了两条sql语句,说明session级别的缓存失效了。

一级缓存失效的几点原因

  • 1.SqlSession调用了close方法,然后从SqlSessionFactory获取了一个新的SqlSession,那么原本的缓存自然就失效了
  • 2.SqlSession调用了clearCache方法,清空了session中的缓存
  • 3.缓存中的数据与数据库中的数据不一致了,那么什么情况下会导致缓存中的数据与数据库中的数据会不一致呢,当我们调用了数据的增、删、改操作以后。当我们使用了insert、delete、update方法后SqlSession会主动清空它所缓存的数据。

mybatis的二级缓存

mybatis的二级缓存是基于SqlSessionFactory级别的缓存,简单来说就是由同一个SqlSessionFactory创建的SqlSession可以获取到SqlSessionFactory中的缓存的数据,在SqlSessionFactory中缓存的数据不是pojo实体类对象,而是key-value的map数据。
要开启SqlSessionFactory的缓存需要一些配置,默认是不开启的,这个也是很自然,因为如果每次查询都要缓存,当然就很消耗内存了。
开启二级缓存的步骤:
1.在mybatis的配置文件mybatis-config.xml中配置支持二级缓存;

<settings>
        <!--开启二级缓存的使用-->
        <setting name="cacheEnabled" value="true"/>
</settings>
2.让当前的映射文件支持二级缓存(在映射文件中配置)
<cache/>
3.让当前的操作支持二级缓存(指定操作开启二级缓存)
<select id="findUserById" parameterType="int" resultType="user" useCache="true">
        select * from user where id=#{id}
</select>

查询结果:
在这里插入图片描述

但是实际上mybatis的缓存其实是比较鸡肋的,因为我们通常做开发都是基于spring-framework的ioc实现下进行开发,这个时候我们通常不会去操作SqlSession,而且在mybatis-spring的插件包中也会在每次执行完后就commit(详细的等看完了再来补充)

mybatis的懒加载机制

当我们在查询一条订单记录的时候,自然会同时查出该订单记录关联的用户表中的那条记录,但是反过来,当我们在查一个用户记录的时候,通常不会对该用户名下所有的订单记录都进行查询,这无疑会大大增加服务器的开销,并且也不符常理,懒加载机制就是为了解决这个问题。
同样的,这里还是从用户表与账户表的模型出发,需求是查询账单表的每条记录并且查询出所对应的用户信息。

<settings>
        <!--设置延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--加载所有属性改为按需加载相应的字段-->
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>

开启延迟加载,并且不会加载所有的字段,只查询所使用到的字段

<select id="findAll" resultMap="accountUser">
        select * from account
</select>

这里就不能再将user表和account表进行连接查询查两张表了,因为这里要使用懒加载机制,如果还是和先前那样通过inner join查询那么一定会查询两张表,也就不存在懒加载这么一说了。

<resultMap id="accountUser" type="account">
        <!--映射account的数据库字段和实体类的映射-->
        <id property="id" column="id"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>

        <!--一对一的关系映射:配置封装user的内容-->
        <!--select属性指定的内容:因为使用的是懒加载,所以只有在使用到用户信息的时候才会去加载用户信息,这个select标签的属性就是用了定义,需要用到用户信息的时候通过哪个方法去查询用户-->
        <!--column属性指定的内容:根据id去查询用户信息时,我们需要用到哪个参数的值作为条件去查询-->
        <association fetchType="lazy" property="user" javaType="user" column="uid" select="mybatis.dao.UserDao.findUserById">
        </association>
</resultMap>
//通过使用account中的user信息和不使用user信息来观察查询结果
	@Test
    public void findAccount() {
        List<Account> accounts = accountDao.findAll();
        //accounts.forEach(a -> System.out.println(a + " \n " + a.getUser()));
    }

在这里插入图片描述
我们可以清除的看到此时并没有发送执行查询user表的sql语句,现在把注释打开,通过调用getUser方法再来观察查询结果
在这里插入图片描述
这个时候就可以明显发现执行了查询user表的sql语句,同时也打印出了user的相关信息,以上就是在一对一的情况下的懒加载。下面来简单说一下一对多情况下的懒加载,其实和一对一没有什么区别,除了在配置resultMap时的标签不同,这里从user的角度出发,查询一个用户的同时如果在用到用户下的账户信息的时候就去查询account表,否则就不查询。
user的相关配置:

List<User> findUserByLazy();
 <resultMap id="lazyUser" type="user">
        <id property="userId" column="id"/>
        <result property="userName" column="username"/>
        <result property="userBirthday" column="birthday"/>
        <result property="userSex" column="sex"/>
        <result property="userAddress" column="address"/>
		<!--注意:collection中的column属性填的是当要查询account的时候要通过哪个字段的属性值去查询,不是java pojo中的属性名-->
        <collection fetchType="lazy" property="accounts" ofType="account" column="id" select="mybatis.dao.AccountDao.findAccountByUid"/>
 </resultMap>

<select id="findUserByLazy" resultMap="lazyUser">
        select * from user
</select>

同样的,在AccountDao与其对应的映射文件中需要提供findAccountByUid方法的签名以及查询的sql语句

List<Account> findAccountByUid();
<select id="findAccountByUid" resultType="account">
        select * from account where uid = #{id}
</select>

mybatis的注解开发

当使用mybatis的注解开发的时候就不再需要映射文件了,但是要注意的是如果项目中在同目录下任然存在映射文件,那么不管是否引入了映射文件,mybatis都会报错,所以这里我们就换了一个目录建包来测试。


<mappers>
		<!--这里需要引入的包的路径就是相应的mapper接口所在的路径-->
        <package name="mybatis.mapper"/>
</mappers>

数据库的字段名与之前一样不做改动,此时我们将User实体类中的成员变量名改的和数据库不一致

public class User implements Serializable {
    //注解方式如何处理实体类与数据库字段名的不一致问题,在UserMapper中配置Results注解
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
}

注解开发的查询操作

package mybatis.mapper;

import mybatis.entity.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {
    //Results中的id属性可以给其他方法通过@ResultMap注解使用
    @Select("select * from user")
    //因为这个时候数据库的字段名与java pojo的成员变量名不再对应,所以通过Results注解(其实就相当于xml中的resultMap标签)
    @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")
    })
    List<User> findAll();

}

原本的xml配置方式开发,从映射文件到接口再到实体类,我们可以看到对应关系都由注解的方式指明了。

<select id = "findById" parameterType = "int" resultType="user">
	select * from user by id = #{id}
</select> 

这是普通xml的方式,可以看到实体类通过别名的方式对这个entity包进行了配置,所以只要在返回类型使用别名user就可以指定,参数值通过parameterType进行指定,再下来就是对应方法名,在映射文件的mapper标签中有一个namespace属性就配置了当前映射文件所对应的dao比如UserDao.xml的mapper标签的namespace属性为"",这样就建立了映射文件与UserDao接口的关联,最后只要根据select标签中id属性值对应方法名即可,那么在注解开发中很容易就实现这些联系。

注解开发的更新操作

@Update("update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id=#{userId}")
    void update(User user);
//这里需要注意的是mybatis中的实体类与数据解析使用的是OGNL表达式,简而言之就是通过pojo.属性名来获取数据
//底层应该是通过get方法来获取,所以sql语句中的username=#{userName} #{}里面的值必须和pojo类的字段名保持一致

注解开发的保存操作

	@Insert("insert into user(username,address,sex,birthday) values (#{userName},#{userAddress},#{userSex},#{userBirthday})")
    //@Options(useGeneratedKeys=true, keyProperty="userId", keyColumn="id")
    @SelectKey(statement = "select last_insert_id()",keyColumn = "id",keyProperty = "userId",before = false,resultType = Integer.class)
    Integer saveUser(User user);
//使用option注解和SelectKey注解中的其中一个都可以返回主键id的自增值

注解开发的删除操作

@Delete("delete from user where id = #{uid}")
Integer deleteUserById(int id);
//因为此时的参数值只有一个id,所以#{}中的值只是个符号,可以随便填,mybatis都能正确为它赋值,这点与xml中也是一致的

注解开发的多表查询-一对一

这里仍然采用user与account的模型,从account出发,某一条account记录与user表是一对一的关系。

	@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"),
        //这里的colum值必须要填数据库中的字段名,不能填pojo的属性名
        @Result(property = "user",column = "uid",one =
        @One(select = "mybatis.mapper.UserMapper.findById",fetchType = FetchType.EAGER))
    })
    List<Account> findAll();
//userMapper
 @Select("select * from user where id = #{id}")
 @ResultMap("userMap")
 User findById(Integer id);

注解开发的多表查询-一对多

	@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(property = "accounts",column = "id",many =
                @Many(fetchType = FetchType.LAZY,select = "mybatis.mapper.AccountMapper.findAccountsByUid")
        )
    })
    List<User> findAll();
	@Select("select * from account where uid = #{id}")
    @ResultMap("accountMap")
    List<Account> findAccountsByUid(Integer id);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值