spring boot整合mybatis+开启spring boot缓存

spring boot的缓存说白了就是底层依赖map结构,每个key可以由自己生成,也可以由spring boot中spring cache abstract框架包中的simple key general生成。不过这些都是小问题,主要的是理解一下spring boot中的缓存设计结构是怎样的。
在spring boot中,其缓存结构如下:
在这里插入图片描述
一个spring boot下面管着很多的cachemanager,而每个cachemanager管理着多个cache,每个cache存放着需要的数据。在spring boot中,cache的底层结构就是map结构,每个要缓存的数据需要一个key值作为标记,缓存的数据作为object对象存入在map中。

环境准备

spring boot项目环境准备

创建一个新的spring boot项目,其中必须引入spring cache abstract包就可以了,如果是spring boot2.x以前的版本,没记错的话应该引入的是spring cache这个包。
第一步:
在这里插入图片描述
右侧红框里面就是引入的所有包,其中mybatis framword是我想用mybatis作为dao层技术,MySQL驱动这个就不说了,不同的数据库不同的驱动就OK了,spring cache abstraction是我们要用的框架包,spring boot所有关于缓存的东西都在这个包中。
一路next,直到项目创建完毕,其pom.xml文件的坐标如下所示:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

OK一个项目已经创建完毕了。

MySQL数据库环境准备

创建一个数据库为cache,再创建一个department表,其SQL脚本如下:

create database cache;
use cache;
create table department(
	id int auto_increment primary key,
	departmentName varchar(255) null
)charset = utf-8;
INSERT INTO cache.department (id, departmentName) VALUES (1, 'test1');
INSERT INTO cache.department (id, departmentName) VALUES (2, 'test2');

完善项目目录结构

搭建如下目录结构:
在这里插入图片描述
employ*类的文件不要创建了,上面就只有department类的文件用的到。配置application.yml文件内容如下所示:

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://192.168.43.37:3306/cache?serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration:
    # 开启mybatis的驼峰命名支持
    map-underscore-to-camel-case: true
# 设置日志级别
logging:
  level:
    com.lm.cache.dao: debug
debug: true

根据自己的信息进行配置,给出department.java实体类,如下:

package com.lm.cache.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department  implements Serializable {

	private static final long serialVersionUID =  2657932609750655357L;

	private Long id;

	private String departmentName;

}

给出departmentDao.java持久层操作类

package com.lm.cache.dao;

import com.lm.cache.domain.Department;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface DepartmentDao {

    @Select("select * from department")
    List<Department> findAll();

    @Select("select * from department where id = #{id}")
    List<Department> findById(Integer id);

    @Insert("insert into department(departmentName) values(#{departmentName})")
    int saveDepartment(Department department);

    @Update("update set d.departmentName = #{departmentName} from department d where d.id = #{id}")
    Department updateDepartment(Department department);

    @Delete("delete from department d where d.id = #{id}")
    int deleteDepartment(Department department);

    @Delete("delete from department d where d.id = #{id}")
    int deleteDepartmentById(Long id);
}

DepartmentService.java

package com.lm.cache.service;

import com.lm.cache.dao.DepartmentDao;
import com.lm.cache.domain.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@CacheConfig(cacheNames = "department")
@Service
public class DepartmentService {

    @Autowired
    private DepartmentDao departmentDao;

    @Cacheable(key = "#root.args[0]")
    public Department findOneDepartment(Integer id) {
        Department department = departmentDao.findById(id).get(0);
        System.out.println("service:running service to department");
        return department;
    }

    @CachePut(key = "#department.id")
    public Department updateDepartment(Department department) {
        return departmentDao.updateDepartment(department);
    }

    @CacheEvict(key = "#department.id")
    public int deleteDepartment(Department department) {
        return departmentDao.deleteDepartment(department);
    }
}

departmentController.java

package com.lm.cache.controller;

import com.lm.cache.domain.Department;
import com.lm.cache.service.DepartmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/dept")
public class DepartmentController {

    @Autowired
    private DepartmentService departmentService;

    @GetMapping("/find/{id}")
    public Department findOneDepartment(@PathVariable("id") Integer id){
        return departmentService.findOneDepartment(1);
    }
}

出程序application.java

package com.lm.cache;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching
@MapperScan("com.lm.cache.dao")
@SpringBootApplication
public class SpringbootCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }

}

实测有效,读者自测。。。
实测有效,读者自测。。。
实测有效,读者自测。。。


上面用到了四个关于spring cache abstraction的注解,其实这些注解呢,最终都会反映到所用的cache框架底层,每个cache框架底层用的配置也是不一样的。spring做的好的一点,就是把所有的缓存框架整合了起来,每个人都只用一种cache注解语法,就能实现底层缓存之间的切换。

关于cache的几个常用的注解:

  1. @EnableCaching: 这个注解标注在spring boot入口类上,表示自动缓存,即开启spring boot缓存支持。
  2. @Cacheable:这个注解表示给指定的方法或者类开启缓存,执行时机为,先在缓存中查询需要的数据,如果查到了,返回结果,且不执行注解的方法;如果没有发现,则执行注解的方法,并将注解的方法的返回结果缓存到缓存组件中。其底层代码如下所示(我会将每个属性的作用在属性上方用注释解释):
package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
// target是一个元注解,变送下面的注解可以用在类和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
// retention表示此注解的有效时间,retentionPolicy.runtime表示此注解贯穿整个程序运行
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
	// aliasfor表示别名,下面这个注解的意思就是value和chchename的作用一样,只不过名称不一致而已。
	// value和cachename的作用就是指定一个或者多个cache的名称,最终缓存数据都会放到每个对应cachename中去。
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};

	// cache前面说过,底层是concurrenthashmap,这里的key就是用来标记此次缓存数据;
	/* key如果没有指定,默认是方法的第一个参数,如果第一个参数没有,则由spring boot中的simplekeygeneral类中的getkey生成一个key;如果注解中用属性指定了,则用指定的key属性作为标识;*/
    String key() default "";
	// 指定key的生成器,此生成器可以编写一个,只需要实现相应的方法并把其注册为bean就可以了
    String keyGenerator() default "";
	// 指定cachemanager,如果没有指定则使用SimpleCacheConfiguration来进行管理;当然如果引入别的缓存框架,或者编写了自己的cachemanager,可以在这里指定,不过建议不要写这个属性,因为我相信没有几个写的cachemanager能比spring boot或者其他框架自配的好
    String cacheManager() default "";

    // 和cachemanager的作用一样,cachemanager中会调用cacheresolver来解析cache的,也是建议不要自己写
    String cacheResolver() default "";

    // condition条件语句,表示只有符合condition条件的方法才会被缓存,支持SPEL,即spring表达式语言。
    // 例如:condition="#root.args[0] < 1 and #root.args[1]>2",表示方法中的参数第一个参数只有小于1,第二个参数只有大于2才会启动缓存来缓存此数据
    String condition() default "";

	// unless和condition的作用正好相反,把unless立即为除非就可以了。
	// 例如:unless = "#root.args[0]<1",表示除非第一个参数小于1,否则全部缓存 
    String unless() default "";

	// 是否开启同步缓存
    boolean sync() default false;
}

  1. @cacheput:此源码与cacheable几乎一样,无非就是少了一个Boolean sync() default false;属性而已。不过此注解的意义为,不管缓存中有没有数据,我都希望执行此注解注解的方法,且将方法结果缓存。与cacheable的不同在于,cacheable注解的方法在执行之前会检查缓存,如果缓存中有数据,就将缓存中的数据返回,而不执行方法,此注解多用于update方法。
  2. @cacheevict:此注解的作用是用来清除指定缓存数据的,多用于delete方法。
  3. @caching:此注解是一个复合注解,其内部可用多个@cacheevict,@cacheable,@cacheput,其内部注解的属性和用法与外部无意,不过读者应该注意语法冲突。
  4. @cacheconfig:此注解的作用在于将上述2到4注解中通用的属性提取出来在类上配置,在缓存加载时先读取cacheconfig类中配置的属性,然后再访问2-4中的配置属性,如果有相同的属性配置,2-4注解上配置的属性会覆盖cacheconfig上配置的属性。

给出一个key的生成器,此配置类需要配置在主入口类的同级目录中的config目录中。

package com.atguigu.cache.config;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.Arrays;

@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
    	// 实现这个匿名类就可以实现key的生成。
        return new KeyGenerator(){

            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString()+"]";
            }
        };
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值