当前位置:网站首页>对象比较
对象比较
2022-08-05 05:16:00 【staHuri】
对象比较
命题
- 对数据库对象在更新的时候进行数据比较,记录差异.
设计
确定比较对象
- 在这里使用 Spring 中
ComponentScan的思想.
在 Spring 中通过@Component注解来说明这是一个组件,在通过ComponentScan扫描到带有@Component的类进行注册.
确定比较的字段
一个数据库对象存在很多字段,可能全部需要比较,也可能只是部分比较.对此需要通过一定的方法找到需要比较的字段.同样使用注解进行控制.
在思考一个问题,通常我们使用关系型数据库,会存储的是一个外键(例如:1,2,3),可能不便于阅读. 这里需要将外键转换成可理解的属性.
总结:
- 单纯值比较
- 外键的值比较,可读性
实现
- 设计注解
@HavingDiff这个注解的目的是给实体对象使用,用来表示这是一个需要比较的对象
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE})
public @interface HavingDiff {
}
- 设计注解
@DiffAnnotation这个注解的目的是给字段(属性)使用
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.FIELD})
public @interface DiffAnnotation {
/** * 字段中文名称 */
String name() default "";
/** * 消息,在这里使用[old]和[new]进行替换 */
String msg() default "";
/** * mapper class */
Class<?> mapper() default Object.class;
/** * 链接对象 */
Class<?> outJoin() default Object.class;
/** * 外联对象需要显示的字符串属性,用来展示的连接字段 */
String outField() default "";
}
- 注解对应的实体
public class DiffAnnotationEntity {
String name;
String msg;
Class<?> mapper;
Class<?> outJoin;
String outField;
}
注解和注解的实体对象已经设计完成,接下来就需要进行包扫描路径的获取了.
使用 Spring 的 enable 类型的开发方式, 写出如下注解.
- 注解解释
@Import会执行EnableDiffSelect中的方法.scanPackages扫描路径.byIdMethodmapper 的根据id查询方法名.
- 注解解释
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(value = {
EnableDiffSelect.class})
public @interface EnableDiff {
String[] scanPackages() default {
};
String byIdMethod() default "selectById";
}
EnableDiffSelect实现ImportSelector接口,在这里的目的是获取注解EnableDiff的两个属性值,放入线程变量,在后续提供扫描入口
public class EnableDiffSelect implements ImportSelector {
@Override
public String[] selectImports(
AnnotationMetadata annotationMetadata) {
Map<String, Object> annotationAttributes = annotationMetadata
.getAnnotationAttributes(EnableDiff.class.getName());
String[] scanPackages = (String[]) annotationAttributes.get("scanPackages");
String byIdSQL = (String) annotationAttributes.get("byIdMethod");
DiffThreadLocalHelper.setScan(scanPackages);
DiffThreadLocalHelper.setByIdMethod(byIdSQL);
return new String[0];
}
}
- 需要扫描的包路径已经成功获取,接下来就是扫描具有
@HavingDiff的类- 下面代码的思想
- 判断这个类是否存有
@HavingDiff注解 - 存在后继续判断字段是否由
@DiffAnnotation注解 - 组装对象放入map对象
key: 具有@HavingDiff注解的类.class
value:
key: 具有@DiffAnnotation的字段,类的属性字段
value:@DiffAnnotation的实体对象
- 判断这个类是否存有
- 下面代码的思想
@Component
public class DiffRunner implements CommandLineRunner, Ordered {
/** * key: 类的字节码 value: Map -> key: 字段,value: 字段上面的注解对象 */
static Map<Class<?>, Map<String, DiffAnnotationEntity>> cache = new HashMap<>();
public static Map<String, DiffAnnotationEntity> get(Class<?> clazz) {
return cache.get(clazz);
}
@Override
public void run(String... args) throws Exception {
List<String> scan = DiffThreadLocalHelper.getScan();
for (String packageStr : scan) {
if (!StringUtils.isEmpty(packageStr)) {
Set<Class<?>> classes = ScanUtils.getClasses(packageStr);
for (Class<?> aClass : classes) {
Map<String, DiffAnnotationEntity> diffEntityMap = clazzWork(aClass);
if (!CollectionUtils.isEmpty(diffEntityMap)) {
cache.put(aClass, diffEntityMap);
}
}
}
}
}
private Map<String, DiffAnnotationEntity> clazzWork(Class<?> clazz) {
HavingDiff havingDiff = clazz.getAnnotation(HavingDiff.class);
// 是否存在这个注解, 如果存在则进行
Map<String, DiffAnnotationEntity> map = new HashMap<>();
if (havingDiff != null) {
for (Field declaredField : clazz.getDeclaredFields()) {
declaredField.setAccessible(true);
// 字段名称
String fieldName = declaredField.getName();
// 获取注解
DiffAnnotation diffAnnotation = declaredField.getAnnotation(DiffAnnotation.class);
if (diffAnnotation != null) {
DiffAnnotationEntity diffAnnotationEntity = annToEntity(diffAnnotation);
map.put(fieldName, diffAnnotationEntity);
}
}
}
return map;
}
/** * 注解转换成为实体对象 */
private DiffAnnotationEntity annToEntity(DiffAnnotation diffAnnotation) {
DiffAnnotationEntity diffAnnotationEntity = new DiffAnnotationEntity();
diffAnnotationEntity.setName(diffAnnotation.name());
diffAnnotationEntity.setMsg(diffAnnotation.msg());
diffAnnotationEntity.setMapper(diffAnnotation.mapper());
diffAnnotationEntity.setOutJoin(diffAnnotation.outJoin());
diffAnnotationEntity.setOutField(diffAnnotation.outField());
return diffAnnotationEntity;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
- 比较
- 比较对象是相同的类
这里直接通过一个泛型T解决 - 获取新老字段属性值,比较字段
反射遍历字段,获取新老字段属性值 - 通过字段名称从上一步的得到的map中获取注解信息
key: 具有`@HavingDiff`注解的类.class
value:
key: 具有`@DiffAnnotation`的字段,类的属性字段
value: `@DiffAnnotation`的实体对象
比较的时候存在前面说到的问题: 外键的可读性.
注解@DiffAnnotation中属性outField就是为了解决这个问题而设计.- 在可读性之前还需要做一个事情: 查询数据库(根据id查询)得到外联的实体对象
- 得到实体对象后就可以通过反射来获取属性值了.
- 在可读性之前还需要做一个事情: 查询数据库(根据id查询)得到外联的实体对象
差异化信息包装.
public class DiffInfoEntity {
private String field;
private String msg;
private String txId;
private String ov;
private String nv;
}
@Service
public class IDiffInterfaceImpl<T> implements IDiffInterface<T> {
private static final String OLD_PLACEHOLDER = "old";
private static final String NEW_PLACEHOLDER = "new";
Gson gson = new Gson();
@Autowired
private ApplicationContext context;
@Autowired
private SqlSession sqlSession;
/** * @param source 原始对象 * @param target 修改后的对象 */
@Override
public List<DiffInfoEntity> diff(T source, T target, String logTxId) {
Class<?> sourceClass = source.getClass();
List<DiffInfoEntity> res = new ArrayList<>();
for (Field declaredField : sourceClass.getDeclaredFields()) {
declaredField.setAccessible(true);
// 字段名称
String fieldName = declaredField.getName();
String oldValue = getTargetValue(source, fieldName);
String newValue = getTargetValue(target, fieldName);
// 注解对象
DiffAnnotationEntity fromFiled = getFromFiled(source, fieldName);
if (fromFiled != null) {
// 字段中文
String nameCn = fromFiled.getName();
// 外联对象的取值字段
String outField = fromFiled.getOutField();
// 外联对象的字节码
Class<?> outJoin = fromFiled.getOutJoin();
// 外联对象的mapper
Class<?> mapper = fromFiled.getMapper();
// 三个值都是默认值则不做外联查询
if (StringUtils.isEmpty(outField) &&
outJoin.equals(Object.class) &&
mapper.equals(Object.class)
) {
if (oldValue.equals(newValue)) {
String changeLog = changeData(oldValue, newValue, fromFiled.getMsg());
DiffInfoEntity diffInfoEntity = genDiffInfoEntity(logTxId, nameCn, oldValue, newValue,
changeLog);
res.add(diffInfoEntity);
}
} else {
String ov = mapper(mapper, oldValue, outField);
String nv = mapper(mapper, newValue, outField);
if (ov.equals(nv)) {
String changeLog = changeData(ov, nv, fromFiled.getMsg());
DiffInfoEntity diffInfoEntity = genDiffInfoEntity(logTxId, nameCn, ov, nv, changeLog);
res.add(diffInfoEntity);
}
}
}
}
return res;
}
private DiffInfoEntity genDiffInfoEntity(String logTxId, String nameCn, String ov, String nv,
String changeLog) {
DiffInfoEntity diffInfoEntity = new DiffInfoEntity();
diffInfoEntity.setField(nameCn);
diffInfoEntity.setMsg(changeLog);
diffInfoEntity.setNv(nv);
diffInfoEntity.setOv(ov);
diffInfoEntity.setTxId(logTxId);
return diffInfoEntity;
}
private String mapper(Class<?> mapper, Serializable serializable, String filed) {
try {
Class<?> aClass = Class.forName(mapper.getName());
Object mapperObj = Proxy.newProxyInstance(aClass.getClassLoader(),
new Class[]{
mapper},
new Target(sqlSession.getMapper(mapper))
);
Method selectById = mapperObj.getClass()
.getMethod(DiffThreadLocalHelper.getIdMethod(), Serializable.class);
Object invoke = selectById.invoke(mapperObj, serializable);
return getValue(invoke, filed, "");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/** * 获取变更的文字内容 */
private String changeData(String oldValue, String newValue, String msg) {
return msg.replace(OLD_PLACEHOLDER, oldValue).replace(NEW_PLACEHOLDER, newValue);
}
private String getTargetValue(T t, String field) {
String result = "";
result = getValue(t, field, result);
return result;
}
private String getValue(Object t, String field, String result) {
Class<?> aClass = t.getClass();
for (Field declaredField : aClass.getDeclaredFields()) {
declaredField.setAccessible(true);
String fieldName = declaredField.getName();
if (field.equals(fieldName)) {
try {
Object o = declaredField.get(t);
result = String.valueOf(o);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return result;
}
/** * 根据类型获取注解的实体对象 * <p> * key:字段,value:对象 * * @see DiffAnnotationEntity */
private Map<String, DiffAnnotationEntity> getFromClazz(T t) {
return DiffRunner.get(t.getClass());
}
private DiffAnnotationEntity getFromFiled(T t, String field) {
return getFromClazz(t).get(field);
}
private static class Target implements InvocationHandler {
private final Object target;
public Target(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
}
- 至此全部结束
项目地址: https://github.com/huifer/crud
分支: dev
边栏推荐
猜你喜欢

CVPR 2022 |节省70%的显存,训练速度提高2倍
![[Pytorch study notes] 9. How to evaluate the classification results of the classifier - using confusion matrix, F1-score, ROC curve, PR curve, etc. (taking Softmax binary classification as an example)](/img/ac/884d8aba8b9d363e3b9ae6de33d5a4.png)
[Pytorch study notes] 9. How to evaluate the classification results of the classifier - using confusion matrix, F1-score, ROC curve, PR curve, etc. (taking Softmax binary classification as an example)

OSPF网络类型

ECCV2022 | RU & Google propose zero-shot object detection with CLIP!

MySQL主从复制—有手就能学会的MySQL集群搭建教程

AWS 常用服务

CVPR2021 - Inception Convolution with Efficient Dilation Search

CVPR2020 - 自校准卷积

SQL(1) - Add, delete, modify and search

Tensorflow steps on the pit notes and records various errors and solutions
随机推荐
OSPF网络类型
Comparison and summary of Tensorflow2 and Pytorch in terms of basic operations of tensor Tensor
[Over 17] Pytorch rewrites keras
Facial Motion Capture 调研
【ts】typescript高阶:联合类型与交叉类型
神经网络也能像人类利用外围视觉一样观察图像
基于Flink CDC实现实时数据采集(四)-Sink接口实现
MySQL
Flink和Spark中文乱码问题
八、响应处理——ReturnValueHandler匹配返回值处理器并处理返回值原理解析
SSL 证书签发详细攻略
【数据库和SQL学习笔记】8.SQL中的视图(view)
Tensorflow踩坑笔记,记录各种报错和解决方法
如何编写一个优雅的Shell脚本(三)
ECCV2022 | RU & Google propose zero-shot object detection with CLIP!
ACL 的一点心得
CAN、CAN FD
Service
基于Flink CDC实现实时数据采集(二)-Source接口实现
发顶会顶刊论文,你应该这样写作