SSM框架之Mybatis学习笔记3

Mybatis


分页


Limit 分页

  • 基于 sql 实现
接口
//分页
List<User> getUserListByLimit(Map<String,Integer> map);
实现
<select id="getUserListByLimit" parameterType="map" resultMap="UserMap">
    select * from mybatis.user limit #{startindex},#{pagesize}
</select>
测试
@Test
public void test1(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Integer> map = new HashMap<>();
    map.put("startindex",0);
    map.put("pagesize",2);
    List<User> userListByLimit = mapper.getUserListByLimit(map);
    for (User user : userListByLimit) {
        System.out.println(user);
    }
    sqlSession.close();
}

RowBounds 分页

  • 基于 Java 代码实现
接口
//rowbounds分页
List<User> getUserByRowBounds();
实现
<select id="getUserByRowBounds" resultMap="UserMap">
    select *
    from mybatis.user;
</select>
测试
@Test
public void test2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    RowBounds rowBounds = new RowBounds(0, 2);
    List<User> userList = sqlSession.selectList("com.yqly.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

分页插件(PageHelper)

使用注解开发


面向接口编程

  • 解耦,可拓展,提高复用,规范性更好

  • 在分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易

  • 接口是定义(规范,约束)与实现(名实分离的原则)的分离

  • 接口本身反映了系统设计人员对系统的抽象理解

  • 接口有两类

    第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class)

    第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface)

注解开发

//获取全部用户列表
@Select("select * from mybatis.user")
List<User> getUserList();

省略了 Mapper.xml 配置文件,但需要在 Mybatis 核心配置文件中绑定接口

<mappers>
    <mapper class="com.yqly.mapper.UserMapper"/>
</mappers>
@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserList();
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}
查询
//获取指定id的用户列表
//方法存在多个参数时,所有的参数前面都必须加上@Param("id")注解
@Select("select * from mybatis.user where id=#{id}")
User getUserById(@Param("id") int id);
@Test
public void test1(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
    sqlSession.close();
}
增加
//添加用户
@Insert("insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{password})")
int addUser(User user);
@Test
public void test2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int i = mapper.addUser(new User(6,"溜溜","123456"));
    System.out.println(i);
    sqlSession.close();
}
修改
//修改用户
@Update("update mybatis.user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Test
public void test3(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int i = mapper.updateUser(new User(1, "张三", "123456"));
    System.out.println(i);
    sqlSession.close();
}
删除
//删除用户
@Delete("delete from mybatis.user where id=#{id}")
int deleteUser(@Param("id") int id);
@Test
public void test4(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int i = mapper.deleteUser(6);
    System.out.println(i);
    sqlSession.close();
}

增删改都可以在工具类中开启事务自动提交

public static SqlSession getSqlSession(){
    return sqlSessionFactory.openSession(true);       //开启事务自动提交
}
#{} 和 ${} 的区别
  • 前者可以防止 sql 注入而后者不行
@Param() 注解
  • 基本类型或 String 类型的参数需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型,可以忽略,但建议加上
  • 在 sql 中引用的参数就是在 @Param() 中设置的属性名

Lombok


  • Java 开发插件,可以通过定义一些注解来简化代码

@Data

  • 自动生成无参构造,get,set,tostring,hashcode,equals 方法

@AllArgsConstructor

  • 自动生成有参构造

@NoArgsConstructor

  • 自动生成为无参构造

多对一处理


数据库编写

create table `teacher`(
    `id` int not null ,
    `name` varchar(20) default null,
    primary key (`id`)
);
insert into `teacher`(id, name) VALUES (1,'李老师');
create table `student`(
    `id` int not null ,
    `name` varchar(20) default null,
    `tid` int not null,
    primary key (`id`),
    key `fktid` (`tid`),
    constraint `fktid` foreign key (`tid`) references `teacher` (`id`)
);
insert into student (id, name, tid)
values
       (1,'小赵',1),
       (2,'小钱',1),
       (3,'小孙',1),
       (4,'小李',1),
       (5,'小周',1),
       (6,'小吴',1),
       (7,'小郑',1);

环境搭建

  1. 导入 lombok

  2. 新建实体类 Teacher,Student

    package com.yqly.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublic class Student {    private int id;    private String name;    private Teacher teacher;}
    
    package com.yqly.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Teacher {
        private int id;
        private String name;
    }
    
  3. 建立 Mapper 接口

  4. 建立 Mapper.xml 配置文件

  5. 在 Mybatis 核心配置文件中绑定接口

  6. 测试环境是否搭建成功

多表查询

对于复杂的属性,需要单独处理,指定属性类型为对象用 association,集合用 collection

javaType 用来定义指定类型

集合中的泛型信息,使用 ofType 定义指定类型

查询嵌套处理
<resultMap id="studentte" type="student">
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getStudent" resultMap="studentte">
    select * from student;
</select>
<select id="getTeacher" resultType="teacher">
    select * from teacher where id=#{tid};
</select>
结果嵌套处理
<resultMap id="studentte" type="student">
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <association property="teacher" javaType="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
    </association>
</resultMap>
<select id="getStudent" resultMap="studentte">
    select s.id,s.name,s.tid tid,t.name tname from student s,teacher t where s.tid=t.id;
</select>

一对多处理


数据库与环境和上面的多对一基本一样,实体类需要修改如下

package com.yqly.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    private int tid;
}
package com.yqly.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}

动态 SQL


  • 根据不同的条件生成不同的 SQL 语句

表结构

create table `blog`(
    `id` varchar(50) not null comment '博客id',
    `title` varchar(100) not null comment '博客标题',
    `author` varchar(20) not null comment '博客作者',
    `create_time` datetime not null comment '创建时间',
    `views` int comment '阅读量'
)

实体类

package com.yqly.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

if

  • 一次判断条件,达成则添加
<select id="getBlogListIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <if test="title!=null">
            title=#{title}
        </if>
        <if test="author!=null">
            and author=#{author}
        </if>
    </where>
</select>
@Test
public void test1(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("title","我的2021年终总结");
    map.put("author","小肖");
    List<Blog> blogList = mapper.getBlogListIf(map);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}

choose

  • 由上到下一次判断,当上面条件满足时,不再执行下面的语句,必须传一个条件都不满足时的执行语句的参数
<select id="getBlogListChoose" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <choose>
            <when test="title!=null">
                title=#{title}
            </when>
            <when test="author!=null">
                and author=#{author}
            </when>
            <otherwise>
                and views=#{views}
            </otherwise>
        </choose>
    </where>
</select>
@Test
public void test2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("title","我的2020年终总结");
    map.put("author","小肖");
    map.put("views",11111);
    List<Blog> blogList = mapper.getBlogListChoose(map);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}

trim(where,set)

  • 定制元素功能
where
  • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
set
  • set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title!=null">
            title=#{title},
        </if>
        <if test="author!=null">
            author=#{author}
        </if>
    </set>
    where id=#{id}
</update>
@Test
public void test3(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("title","2022年年度计划表");
    map.put("author","区委②");
    map.put("id","1");
    int i = mapper.updateBlog(map);
    System.out.println(i);
    sqlSession.close();
}

foreach

  • 允许指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符
  • 这个元素也不会错误地添加多余的分隔符
<select id="getBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" separator="or">
            id=#{id}
        </foreach>
    </where>
</select>
@Test
public void test4(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    ArrayList ids = new ArrayList();
    ids.add(1);
    ids.add(2);
    map.put("ids",ids);
    List<Blog> blogList = mapper.getBlogForeach(map);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}

SQL 片段

  • 对于经常使用的 sql 语句,可以使用 SQL 标签包装起来,便于复用
<sql id="if-title-author">
    <if test="title!=null">
        title=#{title}
    </if>
    <if test="author!=null">
        and author=#{author}
    </if>
</sql>
<select id="getBlogListIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <include refid="if-title-author"/>
    </where>
</select>

缓存


  • 存在内存的临时数据
  • 将用户经常查询的数据放在缓存中,用户去查询时不用从磁盘上查询而是在缓存中查询
  • 减少和数据库的交互次数,减少系统开销,提高系统效率,解决了高并发系统的性能问题

Mybatis 缓存

  • 默认定义了两级缓存:一级缓存和二级缓存
  • 默认情况下只有一级缓存开启
  • 二级缓存需要手动开启和配置
  • 为了提高扩展性,Mybatis 定义了缓存接口 Cache。我们可以通过实现 Cache 接口来自定义二级缓存
  • 所有 select 语句的结果将会被缓存
  • 所有 insert、update 和 delete 语句会刷新缓存(增删改可能会改变原来的数据)
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改
Mybatis 缓存的清除策略
  • LRU – 最近最少使用:移除最长时间不被使用的对象
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象

默认的清除策略是 LRU

一级缓存
  • SqlSession 级别的缓存,也称为本地缓存
@Test
public void test5(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
    User user1 = mapper.getUserById(1);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession.close();
}

在这里插入图片描述

输出了两次查询结果,却只进行了一次 SQL 语句查询,第二次是从缓存中查询

二级缓存
  • namespace 级别的缓存,也称全局缓存

  • 开启二级缓存时要先在 Mybatis 核心配置文件中设置 cacheEnabled 为 true

  • 二级缓存是事务性的

    当 SqlSession 完成并提交或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新

  • 工作机制

    一个会话(SqlSession)查询一条数据,这个数据也就被放在当前会话的一级缓存中

    当会话(SqlSession)关闭后,一级缓存消失,缓存数据被存放到二级缓存中

    新的会话(SqlSession)查询信息,就可以从二级缓存中获取数据

cache 标签

在要使用二级缓存的 Mapper.xml 加中开启二级缓存,可以自定义一些参数

  • enviction(清除策略),默认的清除策略是 LRU

    可用的清除策略有:

    • LRU – 最近最少使用:移除最长时间不被使用的对象
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们
    • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象
    • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象
  • flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

  • size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

  • readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

数据查看顺序:二级缓存,一级缓存,数据库
@Test
public void test5(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    SqlSession sqlSession1 = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    //测试二级缓存
    User user = mapper.getUserById(1);
    System.out.println(user);
    sqlSession.close();
    System.out.println("====================");
    User user1 = mapper1.getUserById(1);    
    System.out.println(user1);
    //测试一级缓存
    User user2 = mapper1.getUserById(2);      
    System.out.println(user2);   
    System.out.println("====================");
    User user3 = mapper1.getUserById(2);  
    System.out.println(user3);
    sqlSession1.close();
}

在这里插入图片描述

四次查询只进入了两次数据库,第二次查询在二级缓存中查询,第四次查询在一级缓存中查询

自定义缓存(ehcache)

  • 一种广泛使用的开源 Java 分布式缓存,主要面向通用缓存
<cache type="org.mybatis.caches.ehcache.EhBlockingCache"/>

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值