作者:Eddy  历史版本:1  最后编辑:龚清  更新时间:2024-11-20 15:41

适用版本:v3.3.6+;

dependency

<dependency>
    <groupId>net.oschina.j2cache</groupId>
    <artifactId>j2cache-core</artifactId>
    <version>${j2cache-core.version}</version>
</dependency>

<dependency>
    <groupId>net.oschina.j2cache</groupId>
    <artifactId>j2cache-mybatis</artifactId>
    <version>${j2cache.version}</version>
</dependency>

配置说明

j2cache.properties配置说明:

  • 配置文件位于platform/business服务下的config/j2cache.properties文件

    # 缓存广播方法,支持 jgroups/redis/lettuce/rabbitmq/rocketmq
    j2cache.broadcast = lettuce
    
    # 第一二级缓存实现方式,一级支持 ehcache/caffeine, 二级支持 redis/lettuce/memcached
    j2cache.L1.provider_class = caffeine
    j2cache.L2.provider_class = lettuce
    
    # 在redis缓存数据中启用/禁用ttl(如果禁用,redis中的对象将永远不会过期,默认值:true)
    j2cache.sync_ttl_to_redis = true
    
    # 默认情况下是否缓存空对象(默认为false)
    j2cache.default_cache_null_object = true
    
    # 缓存序列化方式,支持 fst/kryo/fastjson/java/json/fse
    j2cache.serialization = kryo
    
    # caffeine配置文件
    caffeine.properties = /config/caffeine.properties
    
    # 缓存名称空间可选,默认为[空]
    # redis存储模式 支持 generic/hash
    # 发布/订阅频道名称
    lettuce.namespace =
    lettuce.storage = generic
    lettuce.channel = j2cache.platform

caffeine.properties配置说明:

  • 配置文件位于platform/business服务下的config/caffeine.properties文件
  #########################################
  # Caffeine configuration
  # [name] = size, xxxx[s|m|h|d]
  #########################################
  // 默认缓存,大小是 1000 个对象,TTL 是 30 分钟
  // default 是当我们调用如下方法时:
  //public void set(String region, String key, Object value)
  //如果我们传入的 region 参数(假设为:region1)没有在 caffeine.properties 中定义的话,那 J2Cache 会自动创建一个名为 region1 的缓存 Region,其配置和 default 的配置一致。
  default = 1000, 30m

  ibps.context = 10000, 10m

  ibps.sys = 10000, 1h
  ibps.res = 10000, 1h
  ibps.client = 3000, 1h
  ibps.category = 10000, 2h
  ibps.desktop = 1000, 8h

  ibps.group = 1000, 1h
  ibps.attr = 10000, 1h
  ibps.level = 100, 1h
  ibps.party = 10000, 1h

API说明

缓存的相关操作是在Domain和Repository层实现的,具体代码可以在 IBase,IRepository,AbstracDomain,AbstracRepository 中查看。

  • IBase

    // 对象缓存是否启用
    default boolean isCacheOpenning()
    
    // 对象缓存是否启用空对象缓存,v3.2.4配置
    default boolean isCacheNullObject()
    
    // 返回缓存名称
    default String getCacheName()
    
    // 返回内部缓存名称
    default String getInternalCacheName()
    
    // 根据pk清除对应缓存
    default void evict(PK pk)
    
    // 根据pk和data存入缓存
    default void caching(PK pk, P data)
    
    // 返回内部类型
    default String getInternalType()
    
    // 根据pk值返回存入缓存中的key值
    default String getPKString(PK pk)
    
  • IRepository

    // 列表缓存是否启用
    default boolean isCacheListOpenning()
    
    // 是否跳过内部方法
    default Boolean isSkipInternal()
    
    // 是否跳过缓存
    default Boolean isSkipCache()
  • AbstracRepository

    // 根据id值从缓存中获取到对应对象完整信息
    public P get(PK id)
    
    // originSqlKey:查询所有字段的SqlKey,cacheSqlKey:只查询id字段的SqlKey,queryFilter:查询条件
    public List<P> queryByKey(String originSqlKey, String cacheSqlKey, QueryFilter queryFilter)

使用示例

DefaultPartyUser 的仓库和领域类为例子

  • 仓库

    // DefaultPartyUserRepositoryImpl
    
    ...
    @Override
        public String getInternalCacheName() {
            return CacheKeyConstants.Region.REGION_IBPS_PARTY; // 返回对应的region名称
        }
    
        @Override
        public String getInternalType() {
            return "DefaultPartyUser"; // 返回对应的类型名称,一般为类的名称即可
        }
    
        @Override
        public void getInternal(DefaultPartyUserPo p) {
            if(BeanUtils.isEmpty(p)) {
                return;
            }
            partyEmployeeRepository.setSkipInternal();
            PartyEmployeePo employeePo = partyEmployeeRepository.get(p.getId());
            partyEmployeeRepository.removeSkipInternal();
            if(BeanUtils.isEmpty(employeePo)) {
                return;
            }
    
            p.setFullname(employeePo.getName());
            p.setName(employeePo.getName());
            p.setStatus(employeePo.getStatus());
            p.setGender(employeePo.getGender());
            p.setMobile(employeePo.getMobile());
            p.setEmail(employeePo.getEmail());
            p.setAddress(employeePo.getAddress());
            p.setQq(employeePo.getQq());
            p.setPhoto(employeePo.getPhoto());
            p.setWcAccount(employeePo.getWcAccount());
        }
    
        @Override
        public List<DefaultPartyUserPo> findByParams(Map<String, Object> params) {
            return findByKey("findByParams", "findIdsByParams", params);
        }
    ...
  • 领域

    // DefaultPartyUser
    ...
    @Override
        public String getInternalCacheName() {
            return CacheKeyConstants.Region.REGION_IBPS_PARTY; // 返回对应的region名称
        }
    
        @Override
        public String getInternalType() {
            return "DefaultPartyUser"; // 返回对应的类型名称,一般为类的名称即可
        }
    ...
  • Provider

    // PartyUserProvider
    ...
        @ApiOperation(value = "查询", notes = "根据id查询")
        @Override
        public APIResult<PartyUserPo> get(
                @ApiParam(name = "userId", value = "用户ID", required = true)  
                @RequestParam(name = "userId", required = true)String userId) {
            APIResult<PartyUserPo> result = new APIResult<>();
            try {
                PartyUserPo partyUserPo = defaultPartyUserRepository.get(userId);
                result.setData(partyUserPo);
            } catch (Exception e) {
                setExceptionResult(result, StateEnum.ERROR_EMPLOYEE.getCode(), I18nUtil.getMessage(StateEnum.ERROR_EMPLOYEE.getCode()+""), e);
            }
            return result;
        }
    ...
  • 映射文件

    // DefaultPartyUser.map
    
        <sql id="querySql">
            SELECT * FROM IBPS_PARTY_USER
            left join 
            (
                select ID_ USER_ID_, NAME_, NAME_ FULL_NAME_,STATUS_,GENDER_,EMAIL_,WC_ACCOUNT_,ADDRESS_,MOBILE_,QQ_,PHOTO_ from IBPS_PARTY_EMPLOYEE
            )A on USER_ID_ = ID_
            <where>
                USER_ID_ != '-1' AND A.STATUS_ != 'deleted'
                <if test="@o.Ognl@isNotEmpty(whereSql)">
                    and ${whereSql}
                </if>
            </where>
            <if test="@o.Ognl@isNotEmpty(orderBySql)">
                ORDER BY ${orderBySql}
            </if>
            <if test="@o.Ognl@isEmpty(orderBySql)">
                ORDER BY CREATE_TIME_ DESC
            </if>
        </sql>
    
        <select id="query" parameterType="java.util.Map" resultMap="DefaultPartyUser">
            <include refid="querySql"/>
        </select>
    
        <select id="queryIds" parameterType="java.util.Map" resultMap="DefaultPartyUser">
            SELECT ID_ FROM (<include refid="querySql"/>) T
        </select>
    
        <select id="findDialogUserByParam" parameterType="java.util.Map" resultMap="DefaultPartyUser">
            <include refid="findDialogUserByParamSql"/>
        </select>
    
        <select id="findDialogUserIdsByParamNew" parameterType="java.util.Map" resultMap="DefaultPartyUser">
            SELECT ID_ FROM (<include refid="findDialogUserByParamSql"/>) T
        </select>

缓存中对象是根据id值进行区分,映射文件中,除了常规的query方法,还应该有只查询id字段的queryIds方法,提供给缓存使用。findByKeyqueryByKey方法同理,但非必须,写法参考如上代码。

仓库和领域类需要重写getInternalCacheName方法,参考如上写法,返回对应的缓存分区。setSkipInternalremoveSkipInternal 方法为设置是否跳过内部方法,即是否跳过getInternal方法,具体可查看AbstracReposity类中的get方法。

通过如上配置后,通过仓库的get方法,传入对象的id值,会先去缓存中查找对象,缓存中没有,才会到数据库中查询。