Merge branch 'develop' of https://github.com/dibo-software/diboot-v2 into develop

This commit is contained in:
wuy 2020-06-26 19:41:59 +08:00
commit 3c31879fc8
22 changed files with 259 additions and 46 deletions

View File

@ -18,6 +18,7 @@ package com.diboot.core.starter;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.diboot.core.config.Cons; import com.diboot.core.config.Cons;
import com.diboot.core.util.DateConverter; import com.diboot.core.util.DateConverter;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
@ -72,7 +73,7 @@ public class CoreAutoConfiguration implements WebMvcConfigurer {
} }
@Bean @Bean
@ConditionalOnMissingBean(HttpMessageConverters.class) @ConditionalOnMissingBean
public HttpMessageConverters fastJsonHttpMessageConverters() { public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
converter.setDefaultCharset(Charset.forName(Cons.CHARSET_UTF8)); converter.setDefaultCharset(Charset.forName(Cons.CHARSET_UTF8));
@ -90,6 +91,16 @@ public class CoreAutoConfiguration implements WebMvcConfigurer {
return new HttpMessageConverters(httpMsgConverter); return new HttpMessageConverters(httpMsgConverter);
} }
/**
* Mybatis-plus分页插件
*/
@Bean
@ConditionalOnMissingBean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
/** /**
* 默认支持String-Date类型转换 * 默认支持String-Date类型转换
* @param registry * @param registry
@ -98,4 +109,5 @@ public class CoreAutoConfiguration implements WebMvcConfigurer {
public void addFormatters(FormatterRegistry registry) { public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DateConverter()); registry.addConverter(new DateConverter());
} }
} }

View File

@ -16,12 +16,15 @@
package com.diboot.core.binding.parser; package com.diboot.core.binding.parser;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.diboot.core.binding.query.BindQuery; import com.diboot.core.binding.query.BindQuery;
import com.diboot.core.binding.query.dynamic.AnnoJoiner; import com.diboot.core.binding.query.dynamic.AnnoJoiner;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.BeanUtils; import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.ContextHelper; import com.diboot.core.util.ContextHelper;
import com.diboot.core.util.S; import com.diboot.core.util.S;
import com.diboot.core.util.V; import com.diboot.core.util.V;
import com.diboot.core.vo.Status;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
@ -54,9 +57,9 @@ public class ParserCache {
*/ */
private static Map<String, String> entityClassTableCacheMap = new ConcurrentHashMap<>(); private static Map<String, String> entityClassTableCacheMap = new ConcurrentHashMap<>();
/** /**
* entity类小驼峰-entity类 * entity类小驼峰实例名-entity类
*/ */
private static Map<String, Class<?>> entityLowerCaseCamelEntityClassCacheMap = new ConcurrentHashMap<>(); private static Map<String, Class<?>> entityName2EntityClassCacheMap = new ConcurrentHashMap<>();
/** /**
* dto类-BindQuery注解的缓存 * dto类-BindQuery注解的缓存
*/ */
@ -121,7 +124,7 @@ public class ParserCache {
Class<?> entityClass = Class.forName(entityClassName); Class<?> entityClass = Class.forName(entityClassName);
TableLinkage linkage = new TableLinkage(entityClass, m); TableLinkage linkage = new TableLinkage(entityClass, m);
tableToLinkageCacheMap.put(linkage.getTable(), linkage); tableToLinkageCacheMap.put(linkage.getTable(), linkage);
entityLowerCaseCamelEntityClassCacheMap.put(entityClass.getSimpleName(), entityClass); entityName2EntityClassCacheMap.put(entityClass.getSimpleName(), entityClass);
} }
} }
} }
@ -174,14 +177,27 @@ public class ParserCache {
return tableName; return tableName;
} }
/** /**
* 根据类的entity小驼峰获取EntityClass * 根据entity类获取mapper实例
* @return * @return
*/ */
public static Class<?> getEntityClassByEntityLowerCaseCamel(String table){ public static BaseMapper getMapperInstance(Class<?> entityClass){
String tableName = getEntityTableName(entityClass);
TableLinkage linkage = getTableLinkage(tableName);
if(linkage == null){
throw new BusinessException(Status.FAIL_INVALID_PARAM, "未找到 "+entityClass.getName()+" 的Mapper定义");
}
BaseMapper mapper = (BaseMapper) ContextHelper.getBean(linkage.getMapperClass());
return mapper;
}
/**
* 根据类的entity类名获取EntityClass
* @return
*/
public static Class<?> getEntityClassByClassName(String className){
initTableToLinkageCacheMap(); initTableToLinkageCacheMap();
return entityLowerCaseCamelEntityClassCacheMap.get(table); return entityName2EntityClassCacheMap.get(className);
} }
/** /**

View File

@ -16,7 +16,9 @@
package com.diboot.core.binding.parser; package com.diboot.core.binding.parser;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.diboot.core.config.Cons; import com.diboot.core.config.Cons;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.util.BeanUtils; import com.diboot.core.util.BeanUtils;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@ -20,7 +20,7 @@ import java.lang.annotation.*;
/** /**
* 绑定管理器 * 绑定管理器
* @author Xieshuang * @author mazc@dibo.ltd
* @version v2.0 * @version v2.0
* @date 2019/7/18 * @date 2019/7/18
*/ */

View File

@ -76,6 +76,17 @@ public interface BaseService<T> {
*/ */
<RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter); <RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
/**
* 创建或更新n-n关联
* 在主对象的service中调用不依赖中间表service实现中间表操作
* @param driverIdGetter 驱动对象getter
* @param driverId 驱动对象ID
* @param followerIdGetter 从动对象getter
* @param followerIdList 从动对象id集合
* @return
*/
<R> boolean createOrUpdateN2NRelations(SFunction<R, ?> driverIdGetter, Object driverId, SFunction<R, ?> followerIdGetter, List<? extends Serializable> followerIdList);
/** /**
* 更新Entity实体 * 更新Entity实体
* @param entity * @param entity

View File

@ -19,10 +19,10 @@ import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils; import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.diboot.core.binding.Binder; import com.diboot.core.binding.Binder;
@ -32,11 +32,13 @@ import com.diboot.core.binding.binder.FieldBinder;
import com.diboot.core.binding.query.dynamic.DynamicJoinQueryWrapper; import com.diboot.core.binding.query.dynamic.DynamicJoinQueryWrapper;
import com.diboot.core.config.BaseConfig; import com.diboot.core.config.BaseConfig;
import com.diboot.core.config.Cons; import com.diboot.core.config.Cons;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.mapper.BaseCrudMapper; import com.diboot.core.mapper.BaseCrudMapper;
import com.diboot.core.service.BaseService; import com.diboot.core.service.BaseService;
import com.diboot.core.util.*; import com.diboot.core.util.*;
import com.diboot.core.vo.KeyValue; import com.diboot.core.vo.KeyValue;
import com.diboot.core.vo.Pagination; import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import org.apache.ibatis.reflection.property.PropertyNamer; import org.apache.ibatis.reflection.property.PropertyNamer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -80,6 +82,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public <RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter) { public <RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter) {
boolean success = createEntity(entity); boolean success = createEntity(entity);
if(!success){ if(!success){
@ -89,13 +92,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
if(V.isEmpty(relatedEntities)){ if(V.isEmpty(relatedEntities)){
return success; return success;
} }
Class relatedEntityClass = BeanUtils.getTargetClass(relatedEntities.get(0)); Class relatedEntityClass = relatedEntities.get(0).getClass();
// 获取关联对象对应的Service
BaseService relatedEntityService = ContextHelper.getBaseServiceByEntity(relatedEntityClass);
if(relatedEntityService == null){
log.error("未能识别到Entity: {} 的Service实现请检查", relatedEntityClass.getName());
return false;
}
// 获取主键 // 获取主键
Object pkValue = getPrimaryKeyValue(entity); Object pkValue = getPrimaryKeyValue(entity);
String attributeName = BeanUtils.convertToFieldName(relatedEntitySetter); String attributeName = BeanUtils.convertToFieldName(relatedEntitySetter);
@ -103,7 +100,20 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
relatedEntities.stream().forEach(relatedEntity->{ relatedEntities.stream().forEach(relatedEntity->{
BeanUtils.setProperty(relatedEntity, attributeName, pkValue); BeanUtils.setProperty(relatedEntity, attributeName, pkValue);
}); });
return relatedEntityService.createEntities(relatedEntities); // 获取关联对象对应的Service
BaseService relatedEntityService = ContextHelper.getBaseServiceByEntity(relatedEntityClass);
if(relatedEntityService != null){
return relatedEntityService.createEntities(relatedEntities);
}
else{
// 查找mapper
BaseMapper mapper = ContextHelper.getBaseMapperByEntity(entity.getClass());
// 新增关联无service只能循环插入
for(RE relation : relatedEntities){
mapper.insert(relation);
}
return true;
}
} }
@Override @Override
@ -143,6 +153,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public boolean updateEntities(Collection<T> entityList) { public boolean updateEntities(Collection<T> entityList) {
if(V.isEmpty(entityList)){ if(V.isEmpty(entityList)){
return false; return false;
@ -168,6 +179,67 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
return super.saveOrUpdateBatch(entityList, BaseConfig.getBatchSize()); return super.saveOrUpdateBatch(entityList, BaseConfig.getBatchSize());
} }
/**
* 更新n-n关联
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public <R> boolean createOrUpdateN2NRelations(SFunction<R, ?> driverIdGetter, Object driverId,
SFunction<R, ?> followerIdGetter, List<? extends Serializable> followerIdList)
{
if(driverId == null){
throw new BusinessException(Status.FAIL_INVALID_PARAM, "主动ID值不能为空");
}
// 从getter中获取class和fieldName
com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda lambda = LambdaUtils.resolve(driverIdGetter);
Class<R> middleTableClass = (Class<R>) lambda.getImplClass();
// 获取主动从动字段名
String driverFieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
String followerFieldName = convertGetterToFieldName(followerIdGetter);
List<R> n2nRelations = null;
if(V.notEmpty(followerIdList)){
n2nRelations = new ArrayList<>(followerIdList.size());
try{
for(Serializable followerId : followerIdList){
R relation = middleTableClass.newInstance();
BeanUtils.setProperty(relation, driverFieldName, driverId);
BeanUtils.setProperty(relation, followerFieldName, followerId);
n2nRelations.add(relation);
}
}
catch (Exception e){
throw new BusinessException(Status.FAIL_EXCEPTION, e);
}
}
// 删除已有关联
LambdaQueryWrapper<R> queryWrapper = new QueryWrapper<R>().lambda()
.eq(driverIdGetter, driverId);
// 查找service
BaseService baseService = ContextHelper.getBaseServiceByEntity(middleTableClass);
if(baseService != null){
// 条件为空不删除
baseService.deleteEntities(queryWrapper);
// 添加
if(V.notEmpty(n2nRelations)){
baseService.createEntities(n2nRelations);
}
}
else{
// 查找mapper
BaseMapper mapper = ContextHelper.getBaseMapperByEntity(middleTableClass);
// 条件为空不删除
mapper.delete(queryWrapper);
// 新增关联无service只能循环插入
if(V.notEmpty(n2nRelations)){
for(R relation : n2nRelations){
mapper.insert(relation);
}
}
}
return true;
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public <RE,R> boolean updateEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE,R> relatedEntitySetter) { public <RE,R> boolean updateEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE,R> relatedEntitySetter) {
@ -269,6 +341,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteEntities(Collection<? extends Serializable> entityIds) { public boolean deleteEntities(Collection<? extends Serializable> entityIds) {
if(V.isEmpty(entityIds)){ if(V.isEmpty(entityIds)){
return false; return false;
@ -323,8 +396,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
*/ */
@Override @Override
public <FT> List<FT> getValuesOfField(Wrapper queryWrapper, SFunction<T, ?> getterFn){ public <FT> List<FT> getValuesOfField(Wrapper queryWrapper, SFunction<T, ?> getterFn){
SerializedLambda lambda = LambdaUtils.resolve(getterFn); String fieldName = convertGetterToFieldName(getterFn);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
String columnName = S.toSnakeCase(fieldName); String columnName = S.toSnakeCase(fieldName);
// 优化SQL只查询当前字段 // 优化SQL只查询当前字段
if(queryWrapper instanceof QueryWrapper){ if(queryWrapper instanceof QueryWrapper){
@ -559,6 +631,18 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
return BeanUtils.getProperty(entity, pk); return BeanUtils.getProperty(entity, pk);
} }
/**
* 转换SFunction为属性名
* @param getterFn
* @param <T>
* @return
*/
private <T> String convertGetterToFieldName(SFunction<T, ?> getterFn) {
com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda lambda = LambdaUtils.resolve(getterFn);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
return fieldName;
}
/*** /***
* 打印警告信息 * 打印警告信息
* @param method * @param method

View File

@ -18,8 +18,10 @@ package com.diboot.core.util;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils; import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.diboot.core.binding.parser.ParserCache;
import com.diboot.core.config.Cons; import com.diboot.core.config.Cons;
import com.diboot.core.service.BaseService; import com.diboot.core.service.BaseService;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
@ -88,6 +90,9 @@ public class ContextHelper implements ApplicationContextAware {
if (APPLICATION_CONTEXT == null){ if (APPLICATION_CONTEXT == null){
APPLICATION_CONTEXT = ContextLoader.getCurrentWebApplicationContext(); APPLICATION_CONTEXT = ContextLoader.getCurrentWebApplicationContext();
} }
if(APPLICATION_CONTEXT == null){
log.warn("无法获取ApplicationContext请在Spring初始化之后调用!");
}
return APPLICATION_CONTEXT; return APPLICATION_CONTEXT;
} }
@ -200,11 +205,20 @@ public class ContextHelper implements ApplicationContextAware {
} }
BaseService baseService = ENTITY_BASE_SERVICE_CACHE.get(entity.getName()); BaseService baseService = ENTITY_BASE_SERVICE_CACHE.get(entity.getName());
if(baseService == null){ if(baseService == null){
log.error("未能识别到Entity: "+entity.getName()+" 的Service实现"); log.info("未能识别到Entity: "+entity.getName()+" 的Service实现");
} }
return baseService; return baseService;
} }
/**
* 根据Entity获取对应的BaseMapper实现
* @param entityClass
* @return
*/
public static BaseMapper getBaseMapperByEntity(Class entityClass){
return ParserCache.getMapperInstance(entityClass);
}
/** /**
* 获取Entity主键 * 获取Entity主键
* @return * @return

View File

@ -141,6 +141,18 @@ public class D extends DateUtils{
return sdf.format(date); return sdf.format(date);
} }
/**
* 获取日期的下一天
* @param date 基准日期
* @return yyyy-MM-dd
*/
public static Date nextDay(Date date){
if(date == null){
return null;
}
return addDays(date, 1);
}
/*** /***
* 获取格式化的日期时间 * 获取格式化的日期时间
* @param date * @param date

View File

@ -27,11 +27,6 @@ import org.springframework.core.env.Environment;
@Slf4j @Slf4j
public class PropertiesUtils { public class PropertiesUtils {
/**
* Spring配置环境变量
*/
private static Environment environment;
/*** /***
* 读取配置项的值 * 读取配置项的值
* @param key * @param key
@ -39,11 +34,9 @@ public class PropertiesUtils {
*/ */
public static String get(String key){ public static String get(String key){
// 获取配置值 // 获取配置值
Environment environment = ContextHelper.getApplicationContext().getEnvironment();
if(environment == null){ if(environment == null){
environment = ContextHelper.getApplicationContext().getEnvironment(); log.warn("无法获取上下文Environment请在Spring初始化之后调用!");
}
if(environment == null){
log.warn("无法获取上下文Environment !");
return null; return null;
} }
String value = environment.getProperty(key); String value = environment.getProperty(key);

View File

@ -54,7 +54,7 @@ public class TestEntityBinder {
// 加载测试数据 // 加载测试数据
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(User::getId, 1001L, 1002L); queryWrapper.in(User::getId, 1001L, 1002L);
List<User> userList = userService.list(queryWrapper); List<User> userList = userService.getEntityList(queryWrapper);
// 自动绑定 // 自动绑定
List<EntityBinderVO> voList = Binder.convertAndBindRelations(userList, EntityBinderVO.class); List<EntityBinderVO> voList = Binder.convertAndBindRelations(userList, EntityBinderVO.class);
// 验证绑定结果 // 验证绑定结果

View File

@ -95,7 +95,7 @@ public class TestEntityListBinder {
// 加载测试数据 // 加载测试数据
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(User::getId, 1001L, 1002L); queryWrapper.in(User::getId, 1001L, 1002L);
List<User> userList = userService.list(queryWrapper); List<User> userList = userService.getEntityList(queryWrapper);
// 自动绑定 // 自动绑定
List<EntityListComplexBinderVO> voList = Binder.convertAndBindRelations(userList, EntityListComplexBinderVO.class); List<EntityListComplexBinderVO> voList = Binder.convertAndBindRelations(userList, EntityListComplexBinderVO.class);
// 验证绑定结果 // 验证绑定结果

View File

@ -57,7 +57,7 @@ public class TestFieldBinder {
// 加载测试数据 // 加载测试数据
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(User::getId, 1001L, 1002L); queryWrapper.in(User::getId, 1001L, 1002L);
List<User> userList = userService.list(queryWrapper); List<User> userList = userService.getEntityList(queryWrapper);
// 自动绑定 // 自动绑定
List<FieldBinderVO> voList = Binder.convertAndBindRelations(userList, FieldBinderVO.class); List<FieldBinderVO> voList = Binder.convertAndBindRelations(userList, FieldBinderVO.class);
// 验证绑定结果 // 验证绑定结果
@ -79,7 +79,7 @@ public class TestFieldBinder {
// 加载测试数据 // 加载测试数据
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(User::getId, 1001L, 1002L); queryWrapper.in(User::getId, 1001L, 1002L);
List<User> userList = userService.list(queryWrapper); List<User> userList = userService.getEntityList(queryWrapper);
// 自动绑定 // 自动绑定
List<UserVO> voList = Binder.convertAndBindRelations(userList, UserVO.class); List<UserVO> voList = Binder.convertAndBindRelations(userList, UserVO.class);
if(V.notEmpty(voList)){ if(V.notEmpty(voList)){

View File

@ -84,7 +84,7 @@ public class TestFieldListBinder {
// 加载测试数据 // 加载测试数据
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(User::getId, 1001L, 1002L); queryWrapper.in(User::getId, 1001L, 1002L);
List<User> userList = userService.list(queryWrapper); List<User> userList = userService.getEntityList(queryWrapper);
// 自动绑定 // 自动绑定
List<EntityListComplexBinderVO> voList = Binder.convertAndBindRelations(userList, EntityListComplexBinderVO.class); List<EntityListComplexBinderVO> voList = Binder.convertAndBindRelations(userList, EntityListComplexBinderVO.class);
// 验证绑定结果 // 验证绑定结果

View File

@ -59,7 +59,7 @@ public class TestJoinQuery {
public void testDateCompaire(){ public void testDateCompaire(){
Department example = departmentService.getSingleEntity(null); Department example = departmentService.getSingleEntity(null);
DepartmentDTO departmentDTO = new DepartmentDTO(); DepartmentDTO departmentDTO = new DepartmentDTO();
departmentDTO.setBegin(example.getCreateTime()); departmentDTO.setCreateTime(example.getCreateTime());
QueryWrapper<Department> queryWrapper = QueryBuilder.toQueryWrapper(departmentDTO); QueryWrapper<Department> queryWrapper = QueryBuilder.toQueryWrapper(departmentDTO);
List<Department> list = departmentService.getEntityList(queryWrapper); List<Department> list = departmentService.getEntityList(queryWrapper);
Assert.assertTrue(list.size() >= 1); Assert.assertTrue(list.size() >= 1);

View File

@ -59,15 +59,15 @@ public class DepartmentDTO extends Department {
@DataAccessCheckpoint(type = CheckpointType.ORG) @DataAccessCheckpoint(type = CheckpointType.ORG)
private Long orgId; private Long orgId;
// 查询单个日期
@BindQuery(comparison = Comparison.GE, field = "createTime") @BindQuery(comparison = Comparison.GE, field = "createTime")
private Date begin; private Date createTime;
@BindQuery(comparison = Comparison.LT, field = "createTime") @BindQuery(comparison = Comparison.LT, field = "createTime")
private Date end; private Date createTimeEnd;
public void setBegin(Date date){ private Date getCreateTimeEnd(){
this.begin = date; return D.nextDay(createTime);
this.end = D.addDays(date, 1);
} }
} }

View File

@ -21,6 +21,8 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.util.Date;
/** /**
* 用户角色 * 用户角色
* @author mazc@dibo.ltd * @author mazc@dibo.ltd
@ -33,6 +35,9 @@ import lombok.experimental.Accessors;
public class UserRole extends BaseEntity { public class UserRole extends BaseEntity {
private static final long serialVersionUID = 3030761344045195972L; private static final long serialVersionUID = 3030761344045195972L;
@TableField(exist = false)
private Long id;
@TableField @TableField
private Long userId; private Long userId;
@ -42,4 +47,7 @@ public class UserRole extends BaseEntity {
@TableField(exist = false) @TableField(exist = false)
private boolean deleted; private boolean deleted;
@TableField(exist = false)
private Date createTime;
} }

View File

@ -16,6 +16,7 @@
package diboot.core.test.binder.service; package diboot.core.test.binder.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.diboot.core.service.BaseService;
import diboot.core.test.binder.entity.User; import diboot.core.test.binder.entity.User;
/** /**
@ -24,6 +25,6 @@ import diboot.core.test.binder.entity.User;
* @version v2.0 * @version v2.0
* @date 2019/1/5 * @date 2019/1/5
*/ */
public interface UserService extends IService<User> { public interface UserService extends BaseService<User> {
} }

View File

@ -16,6 +16,7 @@
package diboot.core.test.binder.service.impl; package diboot.core.test.binder.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.diboot.core.service.impl.BaseServiceImpl;
import diboot.core.test.binder.entity.User; import diboot.core.test.binder.entity.User;
import diboot.core.test.binder.mapper.UserMapper; import diboot.core.test.binder.mapper.UserMapper;
import diboot.core.test.binder.service.UserService; import diboot.core.test.binder.service.UserService;
@ -27,6 +28,6 @@ import org.springframework.stereotype.Service;
* @version 2018/12/23 * @version 2018/12/23
*/ */
@Service @Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
} }

View File

@ -44,7 +44,7 @@ import java.util.List;
*/ */
@TestConfiguration @TestConfiguration
@ComponentScan(basePackages={"com.diboot", "diboot.core"}) @ComponentScan(basePackages={"com.diboot", "diboot.core"})
@MapperScan({"com.diboot.**.mapper", "diboot.**.mapper"}) @MapperScan({"com.diboot.core.mapper", "diboot.core.**.mapper"})
public class SpringMvcConfig implements WebMvcConfigurer{ public class SpringMvcConfig implements WebMvcConfigurer{
private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class); private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class);
@ -63,7 +63,6 @@ public class SpringMvcConfig implements WebMvcConfigurer{
// 设置fastjson的序列化参数禁用循环依赖检测数据兼容浏览器端避免JS端Long精度丢失问题 // 设置fastjson的序列化参数禁用循环依赖检测数据兼容浏览器端避免JS端Long精度丢失问题
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect, fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.BrowserCompatible); SerializerFeature.BrowserCompatible);
fastJsonConfig.setDateFormat(D.FORMAT_DATETIME_Y4MDHM);
converter.setFastJsonConfig(fastJsonConfig); converter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> httpMsgConverter = converter; HttpMessageConverter<?> httpMsgConverter = converter;

View File

@ -30,6 +30,8 @@ import com.diboot.core.util.JSON;
import com.diboot.core.util.V; import com.diboot.core.util.V;
import com.diboot.core.vo.*; import com.diboot.core.vo.*;
import diboot.core.test.StartupApplication; import diboot.core.test.StartupApplication;
import diboot.core.test.binder.entity.UserRole;
import diboot.core.test.binder.service.UserService;
import diboot.core.test.config.SpringMvcConfig; import diboot.core.test.config.SpringMvcConfig;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -56,6 +58,9 @@ public class BaseServiceTest {
@Autowired @Autowired
DictionaryServiceImpl dictionaryService; DictionaryServiceImpl dictionaryService;
@Autowired
UserService userService;
@Test @Test
public void testGet(){ public void testGet(){
// 查询总数 // 查询总数
@ -312,4 +317,33 @@ public class BaseServiceTest {
voList = dictionaryService.getViewObjectList(queryWrapper, pagination, DictionaryVO.class); voList = dictionaryService.getViewObjectList(queryWrapper, pagination, DictionaryVO.class);
Assert.assertTrue(voList.size() == 1); Assert.assertTrue(voList.size() == 1);
} }
/**
* 测试n-n的批量新建/更新
*/
@Test
@Transactional
public void testCreateUpdateN2NRelations(){
Long userId = 10001L;
LambdaQueryWrapper<UserRole> queryWrapper = new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userId);
// 新增
List<Long> roleIdList = Arrays.asList(10L, 11L, 12L);
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
List<UserRole> list = ContextHelper.getBaseMapperByEntity(UserRole.class).selectList(queryWrapper);
Assert.assertTrue(list.size() == roleIdList.size());
// 更新
roleIdList = Arrays.asList(13L);
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
list = ContextHelper.getBaseMapperByEntity(UserRole.class).selectList(queryWrapper);
Assert.assertTrue(list.size() == 1);
// 删除
roleIdList = null;
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
list = ContextHelper.getBaseMapperByEntity(UserRole.class).selectList(queryWrapper);
Assert.assertTrue(list.size() == 0);
}
} }

View File

@ -187,6 +187,26 @@ boolean updateEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISett
boolean deleteEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter); boolean deleteEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
~~~ ~~~
### createOrUpdateN2NRelations 创建或更新n-n关联
* since v2.1.0
```java
/**
* 创建或更新n-n关联
* 在主动对象的service中调用不要求中间表有service
* @param driverIdGetter 驱动对象getter
* @param driverId 驱动对象ID
* @param followerIdGetter 从动对象getter
* @param followerIdList 从动对象id集合
*/
<R> boolean createOrUpdateN2NRelations(SFunction<R, ?> driverIdGetter, Object driverId, SFunction<R, ?> followerIdGetter, List<? extends Serializable> followerIdList);
```
使用示例:
~~~java
List<Long> roleIdList = Arrays.asList(10L, 11L, 12L);
// 新增/修改/删除(集合为空) 中间表关联关系
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
~~~
### deleteEntity ### deleteEntity
```java ```java
boolean deleteEntity(Serializable id); boolean deleteEntity(Serializable id);

View File

@ -16,6 +16,7 @@
package com.diboot.file.excel.cache; package com.diboot.file.excel.cache;
import com.diboot.core.binding.annotation.BindDict; import com.diboot.core.binding.annotation.BindDict;
import com.diboot.core.config.BaseConfig;
import com.diboot.core.service.DictionaryService; import com.diboot.core.service.DictionaryService;
import com.diboot.core.util.BeanUtils; import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.ContextHelper; import com.diboot.core.util.ContextHelper;
@ -215,7 +216,12 @@ public class DictTempCache {
if (cacheTime == null) { if (cacheTime == null) {
return true; return true;
} }
return (System.currentTimeMillis() - cacheTime) > 600000; // 过期分钟数
int expiredMinutes = BaseConfig.getInteger("system.dict.expire", 0);
if(expiredMinutes == 0){
return true;
}
return (System.currentTimeMillis() - cacheTime) > (expiredMinutes * 60000);
} }
} }