功能说明

  • 代码生成、自动CRUD、可无缝对接mybaits增强框架Mapper
  • 基于properties配置多数据源支持,无需修改XML
  • 读写分离,事务内操作强制读主库
  • 基于注解自动缓存管理(所有查询方法结果自动缓存、自动更新,事务回滚缓存同步回滚机制)
  • 自动缓存实现基于jeesuite-cachespring-data-redis
  • 分页组件
  • 敏感操作保护
  • 无缝集成mybatis增强框架 Mapper

使用说明

添加依赖

<dependency>
    <groupId>com.jeesuite</groupId>
    <artifactId>jeesuite-mybatis</artifactId>
    <version>[最新版本]</version>
</dependency>

其他功能说明
  • 读写分离
    1. 根据执行sql类型自动路由master/slave库
    2. 支持基于properties文件动态扩展slave节点
    3. 支持@Transactional强制使用master库查询
  • 自动缓存
    1. 支持所有mapper自定义查询方法自动缓存管理(自动缓存、自动更新)
    2. 支持通过@CacheEvictCascade注解,级联缓存更新
    3. 支持通过EntityCacheHelper手动写入/更新缓存并自动纳入自动管理体系
    4. 防止缓存穿透,可配置缓存null值并自动更新
  • 分页
    1. 支持通过标注或者定义page方法实现物理分页

配置

基础配置

# CRUD增强(默认:default,可选:mapper3)
jeesuite.mybatis.crudDriver=default
jeesuite.mybatis.dbType=Mysql
jeesuite.mybatis.cacheEnabled=true
jeesuite.mybatis.nullValueCache=false
jeesuite.mybatis.cacheExpireSeconds=300
jeesuite.mybatis.dynamicExpire=false
jeesuite.mybatis.rwRouteEnabled=false
jeesuite.mybatis.paginationEnabled=true

数据源配置

#mysql global config
db.driverClass=com.mysql.jdbc.Driver
db.initialSize=2
db.minIdle=1
db.maxActive=10
db.maxWait=60000
db.timeBetweenEvictionRunsMillis=60000
db.minEvictableIdleTimeMillis=300000
db.testOnBorrow=false
db.testOnReturn=false

#master
master.db.url=jdbc:mysql://localhost:3306/demo_db?seUnicode=true&amp;characterEncoding=UTF-8
master.db.username=root
master.db.password=123456
master.db.initialSize=2
master.db.minIdle=2
master.db.maxActive=20

#slave ....
slave1.db.url=jdbc:mysql://localhost:3306/demo_db2?seUnicode=true&amp;characterEncoding=UTF-8
slave1.db.username=root
slave1.db.password=123456

#slave2 ....
slave2.db.url=jdbc:mysql://localhost:3306/demo_db3?seUnicode=true&amp;characterEncoding=UTF-8
slave2.db.username=root
slave2.db.password=123456

说明

  • slave序列从1开始
  • 每个数据源的配置会覆盖全局配置
mybais模块AOP

如果使用了自动cache和读写分离插件,需要通过AOP做缓存回滚,和强制使用master生效。

package ${project.base.package}.interceptor;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.jeesuite.mybatis.spring.MybatisPluginBaseSpringInterceptor;


@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class MybatisPluginSpringInterceptor extends MybatisPluginBaseSpringInterceptor {

    @Override
    @Pointcut("execution(* ${project.base.package}.service.*.*(..))")
    public void pointcut() {}

}
spring配置
    <!--除了datasource配置外,其他都与原生配置一样-->
    <bean id="routeDataSource" class="com.jeesuite.mybatis.datasource.MutiRouteDataSource">
       <!-- 
       如果你的数据库配置文件内容自定义了加解密规则,请自行重写ConfigReader
       <property name="configReader">
           <bean class="com.jeesuite.mybatis.datasource.MyConfigReader" />
       </property>
        -->
    </bean>


    <bean id="routeSqlSessionFactory" class="com.jeesuite.mybatis.spring.SqlSessionFactoryBean">
      <!-- 如果包含多个数据源则需要指定groupName,默认为:default -->
      <property name="groupName" value="default" />
    <property name="configLocation" value="classpath:mybatis-configuration.xml" />
    <property name="mapperLocations" value="classpath:mapper/*Mapper.xml" />
    <property name="typeAliasesPackage" value="com.jeesuite.mybatis.test.entity" />
    <property name="dataSource" ref="routeDataSource" />
  </bean>

  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="sqlSessionFactoryBeanName" value="routeSqlSessionFactory" />
    <property name="basePackage" value="com.jeesuite.mybatis.test.mapper" />
  </bean>

默认使用jeesuite-cache模块作为自动缓存支持,如果你需要使用spring-data-redis或者自定义,可以按如下配置

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory" />
    ....
</bean>

<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
  <property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>

<!-- 使用spring-data-redis作为mybatis自动缓存提供者 -->
<bean class="com.jeesuite.mybatis.plugin.cache.provider.SpringRedisProvider">
    <property name="redisTemplate"  ref="redisTemplate"/>
    <property name="stringRedisTemplate" ref="stringRedisTemplate" />
</bean>

springboot集成

添加依赖
<dependency>
    <groupId>com.jeesuite</groupId>
    <artifactId>jeesuite-springboot-starter</artifactId>
    <version>[最新版本]</version>
</dependency>
启动注解
@EnableJeesuiteMybatis
配置说明
jeesuite.mybatis.dbType=MySQL
jeesuite.mybatis.crudDriver=mapper3
jeesuite.mybatis.cacheEnabled=false
jeesuite.mybatis.rwRouteEnabled=false
jeesuite.mybatis.dbShardEnabled=false
jeesuite.mybatis.paginationEnabled=true

另外需要在自己项目加入spring-data-redis依赖

如何使用自动缓存

只需要在mapper接口增加@com.jeesuite.mybatis.plugin.cache.annotation.Cache标注

public interface UserEntityMapper extends BaseMapper<UserEntity> {

    @Cache
    public UserEntity findByMobile(@Param("mobile") String mobile);

    @Cache
    List<String> findByType(@Param("type") int type);
}

配置完成即可,查询会自动缓存、更新会自动更新。

一些其他说明

开启了自动缓存数据库事务失败写入缓存如何处理?
  • 在事务失败后调用CacheHandler.rollbackCache(),通常在业务的拦截器统一处理
    自动缓存的实体如何更新?
  • 首先查询出实体对象,UserEntity entity = mapper.get...(id);
  • 然后通过mapper.updateSelective(entity);

如果不全量更新会导致缓存的内容是有部分字段,因为update后会那最新的entity更新缓存。

如果手动更新或写入实体缓存?

提供EntityCacheHelper方法,在代码中手动写入缓存也将纳入自动缓存管理,无需担心缓存更新问题。

/**
     * 查询并缓存结果
     * @param entityClass 实体类class (用户组装实际的缓存key)
     * @param key 缓存的key(和entityClass一起组成真实的缓存key。<br>如entityClass=UserEntity.class,key=findlist,实际的key为:UserEntity.findlist)
     * @param expireSeconds 过期时间,单位:秒
     * @param dataCaller 缓存不存在数据加载源
     * @return
     */
    public static <T> T queryTryCache(Class<? extends BaseEntity> entityClass,String key,long expireSeconds,Callable<T> dataCaller)
//生成的缓存key为:UserEntity.findByStatus:2
EntityCacheHelper.queryTryCache(UserEntity.class, "findByStatus:2", new Callable<List<UserEntity>>() {
    public List<UserEntity> call() throws Exception {
      //查询语句
      List<UserEntity> entitys = mapper.findByStatus((short)2);
      return entitys;
   }
});

如何使用分页

1.标注方式(@Pageable)
//mapper接口
@Pageable
List<UserEntity> findByStatus(short status);

//调用
Page<UserEntity> pageInfo = mapper.pageQuery(new PageParams(1,5));
2.page方法定义方式
//mapper接口
@Select("SELECT * FROM users where 1=1")
Page<UserEntity> pageQuery(@Param("pageParam") PageParams pageParam);

//调用
Page<UserEntity> pageInfo = PageExecutor.pagination(new PageParams(1,10), new PageDataLoader<UserEntity>() {
    @Override
    public List<UserEntity> load() {
       return mapper.findByStatus((short)1);
    }
});

results matching ""

    No results matching ""