Mybatis框架(4)

延迟加载策略

1、背景

问题:在一对多的关系中,当我们有一个用户,它有100个账户。

          在查询用户的时候,要不要把关联的100个账户查出来?

          在查询账户的时候,要不要把关联的用户信息查出来?

思路:在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询

          在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来

2、概念

延迟加载:在真正使用数据时才发起查询,不用的时候不查询,即按需加载(懒加载) lazy

立即加载:不管用不用,只要一调用方法,马上发起查询 eager

3、多表关系

在对应的四种表关系中:一对多、多对一、一对一、多对多

一对多、多对多:通常采用延迟加载

多对一、一对一:通常采用立即加载

4、使用方法

1、修改SqlMapConfig.xml配置文件中的settings设置

    1)lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态,默认false

    2)aggressiveLazyLoading:开启时,任一方法的调用都会加载该对象的所有延迟加载属性, 否则,每个延迟加载属性会按需加载, 3.4.1 版本后默认关闭(false)

2、修改映射文件

    1)一对一(多对一):可以实现延迟加载,但是我们一般用立即加载

account配置文件:

<resultMap id="accountMap" type="account">

    <id property="id" column="id"></id>

    <result property="uid" column="uid"></result>

    <result property="money" column="money"></result>

    <association property="user" javaType="user" column="uid" select="q.perter.dao.IUserDao.findOne" fetchType="eager">

    </association>

</resultMap>

<select id="findAll" resultMap="accountMap">

    select * from account

</select>

user配置文件:

<select id="findOne" resultType="user" parameterType="Integer">

        select * from user where id = #{id}

</select>

association标签属性:

property:实体类中的属性

javaType:查询结果封装到的具体类(全限定类名)

column:将要执行查询的语句对应的参数

select:将要执行查询的语句(全限定类名+方法名)

fetchType:加载方式:eager(立即加载)、lazy(懒加载)

    2)一对多(多对多):

user配置文件:

<resultMap id="userAccountMap" type="user">

    <id property="id" column="id"></id>

    <result property="username" column="username"></result>

    <result property="birthday" column="birthday"></result>

    <result property="sex" column="sex"></result>

    <result property="address" column="address"></result>

    <collection property="accounts" ofType="account" column="id" select="q.perter.dao.IAccountDao.findAccountByUId">

    </collection>

</resultMap>

<select id="findAll" resultMap="userAccountMap">

    select * from user

</select>

account配置文件:

<select id="findAccountByUId" resultType="account" parameterType="Integer">

    select * from account where uid = #{uid}

</select>

3、测试

延迟加载(一对多)测试:

当只调用方法:List<User> users = userDao.findAll();时,mybatis只进行一次查询,即查询所有user对象

当使用users列表中的某一个对象时,则会再次执行查询

延迟加载

立即加载(一对一)测试:

当调用方法:List<Account> accounts = accountDao.findAll();时,mybatis只进行了一次查询,但是执行了多条语句

立即加载

缓存

1、定义

缓存是指存在于内存中的临时数据

使用缓存可以减少和数据库的交互次数,提高执行效率

2、范围

适用缓存的条件

     1)经常查询并且不经常改变的

     2)数据的正确与否对最终结果影响不大的

不适用于缓存的情况

    1)经常改变的数据

    2)数据的正确与否对最终结果影响很大的,如:商品的库存,银行的汇率,股市的牌价

一级缓存

它指的是Mybatis中SqlSession对象的缓存

当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中

该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用

当SqlSession对象消失时,mybatis的一级缓存也就消失

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

二级缓存

它指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存

开启二级缓存

1)让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)

cacheEnabled:全局性地开启或关闭所有映射器配置文件中已配置的任何缓存,默认true

2)让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

<cache></cache>:开启user支持二级缓存

3)让当前的操作支持二级缓存(在select标签中配置)

<select id="findOne" resultType="user" parameterType="Integer" useCache="true">

    select * from user where id = #{id}

</select>

在<select>标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false

注意

1、针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存

2、当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象

测试

同一个factory,创建第一个的sqlSession,查询完成后,关闭第一个sqlSession,然后创建第二个sqlSession,再次查询进行打印测试

测试结果发现:

1、第一次查询是查询的数据库,第二次查询读取的是二级缓存

2、两次查询所得到的内存地址不一致,说明二级缓存只是序列化存储了对象中的数据,当再次查询时,将数据封装为新的对象返回

二级缓存

注解开发

1、常用注解

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

@Result:实现结果集封装

@Results:可以与@Result 一起使用,封装多个结果集

@ResultMap:实现引用@Results 定义的封装

@One:实现一对一结果集封装

@Many:实现一对多结果集封装

@SelectProvider: 实现动态 SQL 映射

@CacheNamespace:实现注解二级缓存的使用

2、常用注解功能

user表:

--Select、Results(many)

@Select("select * from user")

@Results(id = "userAccountMap", value = {

        @Result(id = true, column = "id", property = "id"),

        @Result(column = "username", property = "username"),

        @Result(column = "sex", property = "sex"),

        @Result(column = "birthday", property = "birthday"),

        @Result(column = "address", property = "address"),

        @Result(property = "accounts", column = "id",

                many = @Many(select = "q.perter.dao.IAccountDao.findByUserId", fetchType = FetchType.LAZY))

})

List<User> findAll();

--Insert、SelectKey

@Insert("insert into user(username, sex, birthday, address) values(#{username}, #{sex}, #{birthday}, #{address} )")

@SelectKey(keyProperty = "id", keyColumn = "id", before = false, statement="select last_insert_id()", resultType = Integer.class)

void saveUser(User user);

--Update

@Update("update user set username = #{username}, sex = #{sex}, birthday = #{birthday}, address = #{address} where id = #{id}")

void updateUser(User user);

--Delete

@Delete("delete from user where id = #{uid} ")

void deleteUser(Integer id);

--Select、ResultMap

@Select("select * from user where id = #{uid}")

@ResultMap("userAccountMap")

User findById(Integer id);

--Select

@Select("select * from user where id = #{uid}")

User findOne(Integer id);

--Select、ResultMap

@Select("select * from user where username like #{username}")

@ResultMap("userAccountMap")

List<User> findUserByName(String userName);

--Select、ResultMap

@Select("select * from user where username like '%${value}'")

@ResultMap("userAccountMap")

List<User> findUserByName2(String userName);

--Select

@Select("select count(id) from user")

Integer findTotal();

账户表:

--Select、Results(one)

@Select("select * from account")

@Results(id="accountUserMap", value = {

        @Result(id=true, property = "id", column = "id"),

        @Result(property = "uid", column = "uid"),

        @Result(property = "money", column = "money"),

        @Result(property = "user", column = "uid",

                one = @One(select = "q.perter.dao.IUserDao.findOne", fetchType = FetchType.EAGER))

})

List<Account> findAll();

3、注解标签解析

注解中的标签都比较容易理解,只有在使用注解实现复杂关系映射时相对复杂,详细解释如下:

1)@Results 注解

代替的是标签<resultMap>

属性:id,用来标识当前注解,其他方法想要使用时,可以直接通过@ResultMap("userAccountMap")使用

该注解中可以使用单个@Result 注解,也可以使用@Result 集合@Results({@Result(),@Result()})或@Results(@Result())

2)@Result 注解

代替了 <id>标签和<result>标签

@Result 中 属性介绍:

    id:是否是主键字段

    column:数据库的列名

    property:需要装配的属性名(类实体中的属性名)

    one:需要使用的@One 注解(@Result(one=@One)()))

    many:需要使用的@Many 注解(@Result(many=@many)()))

3)@One 注解(一对一)

代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象

@One 注解属性介绍:

    select:指定用来多表查询的 sqlmapper,即map的key(接口的全限定类名+方法名)

    fetchType:会覆盖全局的配置参数 lazyLoadingEnabled

使用格式:

@Result(column="",property="",one=@One(select=""))

4)@Many 注解(多对一)

代替了<Collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合

注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义

使用格式:

@Result(property="",column="",many=@Many(select=""))

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341