标签:applicationContext 翻译 return String 注解 null public 字典
前言
在日常开发中查询单表的情况非常多。这时总会出现表里存的是编码(如部门编号),但却要返回对应的描述(如部门名称)。
通常一般思路是在 Service 进行关联查询或依赖组件完成。比如 Mybatis 中用 join 语句将 sql 写死,比如 JPA 中在实体类属性字段加上@ManyToOne注解,直接将对象组合起来。
private String orgId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "orgId", insertable = false, updatable = false)
private BaseOrg baseOrg;
上面的方式固然简单直接,但是我觉得还不够快,而且过度依赖组件在以后的修改中也会比较麻烦,接下来就由我来提供一种新的思路。
思路
无论是列表还是单个查询,本质上是先找到编码,再去找对应描述,首要条件就是:顺序不能颠倒,我们不能进行预判。所以我们的任务就像一条流水线一样,得到数据进行查询,再返回填充。如果是列表,那就遍历一遍,时间复杂度O(n)。
而这样一个过程其实是非常模范的,容易提炼出来。我起先的思路是结合Spring的切面来做,可深度考虑后发现切面只能针对方法的调用,而方法的返回值有很多种,单个对象、List以及IPage分页等。放在 set() 方法上也没有办法得到该 set() 对应的实体再填充。后面转换思路写为工具类在所需要的地方进行调用,一切都简单了不少。比如加入到 MP 的分页转换过程中( IPage <PO>
to IPage <VO>
)。
工具类的思路确定了。我们剩下还需要的。1是查询对应的编码所需要的单表查询Service,2是填充的属性名称(如果是Json动态添加一个JsonElement就不需要在VO再加一个属性,但考虑到我们的业务层或许也需要该字段,就添个属性用来存放)。接下来就开工。
实现
注解
- @Dict 包含填充目标属性,和调用的service
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface Dict {
String target();
String service();
}
- 视图对象VO
@Data
@NoArgsConstructor
@ApiModel(value = "员工VO", description = "EmployeeVO")
public class EmployeeVO implements Serializable {
/** 名称 */
@ApiModelProperty("名称")
private String name;
/** 编号 */
@ApiModelProperty("编号")
private String number;
/** 所属机构代码 */
@ApiModelProperty("所属机构代码")
@Dict(target = "orgName", service = "baseOrgService")
private String orgCode;
/** 所属机构 */
@ApiModelProperty("所属机构")
private String orgName;
public EmployeeVO(EmployeePO po) {
this.name = po.getName();
this.number = po.getNumber();
this.orgCode = po.getOrgCode();
}
}
- 进行翻译的工具类
@Slf4j
public class BeanHelpUtils {
/// 主要代码 ⬇️
public static <T> void translation(T t)
throws IntrospectionException, InvocationTargetException, IllegalAccessException {
Field[] fields = t.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Dict.class)) {
String target = field.getAnnotation(Dict.class).target();
String service = field.getAnnotation(Dict.class).service();
DictService dictService = SpringContextUtil.getBean(service, DictService.class);
if (dictService != null) {
PropertyDescriptor source = new PropertyDescriptor(field.getName(), t.getClass());
Object invoke = source.getReadMethod().invoke(t);
if (invoke instanceof String) {
Object result = dictService.getValue((String) invoke);
PropertyDescriptor targetResult = new PropertyDescriptor(target, t.getClass());
targetResult.getWriteMethod().invoke(t, result);
}
}
}
}
}
/// 主要代码 ⬆️
public static <T> void translation(List<T> collect) {
for (T t : collect) {
try {
translation(t);
} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
if (log.isInfoEnabled()) log.info(e.getMessage());
e.printStackTrace();
}
}
}
/** 分页复制 */
public static <T, E> IPage<T> pageTransform(IPage<E> page, Function<E, T> sup) {
if (page == null || page.getRecords() == null) return null;
List<T> collect = page.getRecords().stream().map(sup).collect(Collectors.toList());
translation(collect);
return new Page<T>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(collect);
}
}
- 为了能进行统一调用,写了一个 DictService 字典接口
public interface DictService {
Object getValue(String key);
}
- 实现了字典接口的 OrgService
@Service
public class BaseOrgService extends ServiceImpl<BaseOrgMapper, BaseOrgPO> implements DictService {
@Override
public Object getValue(String orgCode) {
BaseOrgPO po =
baseMapper.selectOne(
new QueryWrapper<BaseOrgPO>()
.lambda()
.eq(BaseOrgPO::getOrgCode, orgCode)
.last("LIMIT 1"));
if (po == null) return null;
return po.getOrgName();
}
}
- 获取 Bean 的工具类,使用了 Spring 的 ApplicationContextAware
@Configuration
public class SpringContextUtil implements ApplicationContextAware {
public static ApplicationContext applicationContext;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext)
throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static String getProperty(String path) {
return applicationContext.getEnvironment().getProperty(path);
}
public static Object getBean(String name) throws BeansException {
if (applicationContext == null) return null;
return applicationContext.getBean(name);
}
public static <T> T getBean(String name, Class<T> requiredType) throws BeansException {
if (applicationContext == null) return null;
return applicationContext.getBean(name, requiredType);
}
}
- 在所需要的地方应用
// 调用分页转换,自动翻译
...
IPage<EmployeePO> poPage = employeeMapper.selectPage(page, new QueryWrapper<EmployeePO>().lambda()
.eq(...
return BeanHelpUtils.pageTransform(poPage, EmployeeVO::new);
// 或直接调用翻译
List<EmployeeVO> records = poPage.getRecords();
BeanHelpUtils.translation(records);
return records;
数据字典缓存
由于我一开始提到的是数据字典,其实数据字典通常是一张或者两张表,用来存放编码和对应值,如:
A表存放:
key | value |
---|---|
gender | 性别 |
B表存放:
key | value | display |
---|---|---|
gender | 1 | 男 |
gender | 2 | 女 |
(只留一张 B 表也行,可根据数据复杂度而定)
最后通过单表的 value 值和名称 gender 来进行数据字典表查找。
有时候数据字典会叫别的名字,如标准码、标准代码,多见于专业领域。
由于数据字典表的特性,在写入之后,很少会去修改,非常适合结合Redis来进行缓存,提高查询数据。
我们可以在对应数据字典的 Service 层接入 Spring Cache + Redis 来缓存。
(完)
标签:applicationContext,翻译,return,String,注解,null,public,字典 来源: https://www.cnblogs.com/arminzheng/p/16200528.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。