功能说明
- 代码生成、自动CRUD、可无缝对接mybaits增强框架
Mapper
- 基于properties配置多数据源支持,无需修改XML
- 读写分离,事务内操作强制读主库
- 基于注解自动缓存管理(所有查询方法结果自动缓存、自动更新,事务回滚缓存同步回滚机制)
- 自动缓存实现基于
jeesuite-cache
和spring-data-redis
- 分页组件
- 敏感操作保护
- 无缝集成mybatis增强框架 Mapper
使用说明
添加依赖
<dependency>
<groupId>com.jeesuite</groupId>
<artifactId>jeesuite-mybatis</artifactId>
<version>[最新版本]</version>
</dependency>
其他功能说明
- 读写分离
- 根据执行sql类型自动路由master/slave库
- 支持基于properties文件动态扩展slave节点
- 支持@Transactional强制使用master库查询
- 自动缓存
- 支持所有mapper自定义查询方法自动缓存管理(自动缓存、自动更新)
- 支持通过@CacheEvictCascade注解,级联缓存更新
- 支持通过EntityCacheHelper手动写入/更新缓存并自动纳入自动管理体系
- 防止缓存穿透,可配置缓存null值并自动更新
- 分页
- 支持通过标注或者定义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&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&characterEncoding=UTF-8
slave1.db.username=root
slave1.db.password=123456
#slave2 ....
slave2.db.url=jdbc:mysql://localhost:3306/demo_db3?seUnicode=true&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);
}
});