2021-09-22

昨天随便说一下pojo和vo和dto的关系给大家2个需求 昨天的博客的地址

https://mp.weixin.qq.com/s?__biz=MzA4MTAwMzA1Mw==&mid=2247484844&idx=1&sn=b950db1607efa078ccc41af61d885dc4&chksm=9f9ad2eea8ed5bf800d2a6d56eddebb33487f1db0741b6a71c0ea262db79480f7fbcb8119ebb&token=722122288&lang=zh_CN#rd

需求

假设是从A复制到B: 
需求1:如果B中某字段有值(不为null),则该字段不复制;
也就是B中该字段没值时,才进行复制,适合于对B进行补充值的情况。 
需求2:如果A中某字段没值(为null),则该字段不复制
也就是不要把null复制到B当中

先解决第一个需求

求1:如果B中某字段有值(不为null),则该字段不复制; 也就是B中该字段没值时,才进行复制,适合于对B进行补充值的情况。

解决方案

这里我使用的是自定义注解当自定义注解放在那个字段上面就不进行复制 重写这个复制的源码

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})//
//  1.CONSTRUCTOR:用于描述构造器
//2.FIELD:用于描述域
//3.LOCAL_VARIABLE:用于描述局部变量
//4.METHOD:用于描述方法//
// 6.PARAMETER:用于描述参数//
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
//ElementType.FIELD 只能用在字段上
@Documented
@Component
public @interface judge {
}

B

@Data

public class Department implements Serializable {
    private Integer departmentid;
    @judge
     String departmentname;
    private Integer managerid;
    private String hh;
}

A

@Data

@NoArgsConstructor
public class DepartmentidDto {
    private Integer departmentid;
    private String departmentname;
    private String hh;

}

main

public class YY {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        DepartmentidDto department=new DepartmentidDto();
        department.setDepartmentid(1);
        department.setHh("4444444");
        department.setDepartmentname("2222222222222");
        Department departmentid=new Department();
        //
        departmentid.setDepartmentname("3333333333333333333");
        departmentid.setHh("12314");

//  前面是dto a 后面是pojo  b
        BeanUtilsVo.copyProperties(department,departmentid);
        System.out.println(departmentid.toString());


    }
}

可以看到a向b复制 字段departmentname的值在b是3333333333333333333 a的值是2222222在b上加上自定义注解

重写方法

public abstract class BeanUtilsVo {
    /**使用的方法为copyProperties(Object source, Object target)**/
    public static void copyProperties(Object source, Object target) throws BeansException {
        //复制的  被复制的
        copyProperties(source, target, (Class)null, (String[])null);
    }
    private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException {
        Assert.notNull(source"Source must not be null");
        /**
         //对象为空则抛出异常IllegalArgumentException
         public static void notNull(@Nullable Object object, String message) {
         if (object == null) {
         throw new IllegalArgumentException(message);
         }
         }
         **/
        Assert.notNull(target, "Target must not be null");

        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
            }

            actualEditable = editable;
        }
        //获取PropertyDescriptor(属性描述器)数组,getPropertyDescriptors具体内容看下方
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            //getWriteMethod中注释说明
            //May return null if the property can't be written.
            //也就是说对应的类中必须有set(写入)方法,否则返回空
            Method writeMethod = targetPd.getWriteMethod();

            System.out.println("=======================");
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                //取出对应的属性并读取值
                System.out.println( targetPd.getName());
//               source.getClass().getDeclaredFields();
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
//                System.out.println(sourcePd.getClass().getDeclaredFields().toString());
//                boolean f=false;
//                 if (!Objects.isNull(sourcePd)){
//                     Field[] declaredFields = sourcePd.getClass().getDeclaredFields();
//                     System.out.println("ppppppppppppppppp"+declaredFields.length);
//                     if (declaredFields.length>0){
//                         for (Field declaredField : declaredFields) {
//                             judge fieldAnnotation =declaredField.getDeclaredAnnotation(judge.class);
//                             if (fieldAnnotation!=null){
//                                 f=true;
//                                 System.out.println("1111111111111");
//                             }
//                         }
//                     }
//                 }
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }

                            Object value = readMethod.invoke(source);
//                            Field[] declaredFields = source.getClass().getDeclaredFields();

                            System.out.println(value);
                            if (!Objects.isNull(value)){
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }
                                Field[] declaredFields = target.getClass().getDeclaredFields();
                                int  i=0;
                                for (Field declaredField : declaredFields) {
                                    judge fieldAnnotation =declaredField.getDeclaredAnnotation(judge.class);
                                    if (fieldAnnotation!=null){
                                        if (targetPd.getName().equals(declaredField.getName())){
                                            i++;
                                        }
//                                    System.out.println(declaredField.getName());
                                    }

                                }
                                if (i==0){
                                    //写入对应的值到目标类  关键
                                    writeMethod.invoke(target, value);
                                }
                            }
//                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
//                                writeMethod.setAccessible(true);
//                            }
//                            Field[] declaredFields = target.getClass().getDeclaredFields();
//                            int  i=0;
//                            for (Field declaredField : declaredFields) {
//                                judge fieldAnnotation =declaredField.getDeclaredAnnotation(judge.class);
//                                if (fieldAnnotation!=null){
//                                    if (targetPd.getName().equals(declaredField.getName())){
//                                        i++;
//                                    }
////                                    System.out.println(declaredField.getName());
//                                }
//
//                            }
//                            if (i==0){
//                                //写入对应的值到目标类  关键
//                                writeMethod.invoke(target, value);
//                            }
                            //写入对应的值到目标类  关键
//                            writeMethod.invoke(target, value);
                        } catch (Throwable var15) {
                            throw new FatalBeanException("Could not copy property '
" + targetPd.getName() + "' from source to target", var15);
                        }
                    }
                }
            }
        }

    }

}

核心是

   Field[] declaredFields = target.getClass().getDeclaredFields();
                                int  i=0;
                                for (Field declaredField : declaredFields) {
                                    judge fieldAnnotation =declaredField.getDeclaredAnnotation(judge.class);
                                    if (fieldAnnotation!=null){
                                        if (targetPd.getName().equals(declaredField.getName())){
                                            i++;
                                        }
//                                    System.out.println(declaredField.getName());
                                    }

                                }
                                if (i==0){
                                    //写入对应的值到目标类  关键
                                    writeMethod.invoke(target, value);
                                }

执行看效果

看到里面的值还是没有改变 解决

需求2:如果A中某字段没值(为null),则该字段不复制 也就是不要把null复制到B当中

上面还是可以用

核心

  Object value = readMethod.invoke(source);
//                            Field[] declaredFields = source.getClass().getDeclaredFields();

                            System.out.println(value);

看结果

看到

public class YY {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        DepartmentidDto department=new DepartmentidDto();
        department.setDepartmentid(1);
        
        department.setDepartmentname("2222222222222");
        Department departmentid=new Department();
        //
        departmentid.setDepartmentname("3333333333333333333");
        departmentid.setHh("12314");

//  前面是dto a 后面是pojo  b
        BeanUtilsVo.copyProperties(department,departmentid);
        System.out.println(departmentid.toString());


    }
}

日志

=======================
=======================
departmentid
1
=======================
departmentname
2222222222222
=======================
hh
null
=======================
managerid
Department(departmentid=1, departmentname=3333333333333333333, managerid=null, hh=12314)

希望可以给大家带来看源码的能力,和学习这个方法的源码是干什么的,怎么进行根据需求去改造这个源码,谢谢你的支持,这个问题,百度上也有,但是写的,看不懂,这个写的比较简单,希望给你带来学习的意义,去理解复制的原理

对这个需求有问题关注我微信公共号Java代码学习营地

我们来逐步分析你提出的问题,先从 SQL 语句的逻辑入手,再解释为什么会出现 `null` 值,以及为什么在“9-15”这一行中窗口函数返回的是 `2021-06-21`(比当前行的结束时间还大),最后给出正确计算每个品牌**去重后总打折天数**的方法。 --- ### ✅ 你的SQL语句是: ```sql SELECT brand, stt, edt, MAX(edt) OVER ( PARTITION BY brand ORDER BY stt ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) AS maxEdt FROM test4; ``` 作用:对每个品牌的促销活动按开始日期排序,然后为每一行计算 **之前所有行中最大的结束日期(edt)**。 --- ## 🔍 问题1:为什么第一行 `maxEdt` 是 `null`? 看 redmi 的第一条记录: ``` redmi 2021-06-05 2021-06-21 null ``` 因为这是一组(brand=redmi)中的**第一行**。 窗口定义是: > `ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING` 意思是:“从该分组的第一行到当前行的前一行”。 但当前行是第一行,它前面没有数据 → 所以前面的行集合为空集 → `MAX(edt)` 在空集上运算结果为 `NULL`。 ✅ **结论**:第一个活动没有“之前的活动”,所以 `maxEdt = NULL` 是正常的。 --- ## 🔍 问题2:为什么第二行(2021-06-092021-06-15)的 `maxEdt = 2021-06-21`? 看第二条 redmi 记录: ``` redmi 2021-06-09 2021-06-15 2021-06-21 ``` 注意!虽然当前行的 `edt=2021-06-15`,但它的 `maxEdt` 来自于前面所有行的最大 `edt`,也就是第一行的 `edt=2021-06-21`。 ⚠️ 关键点: 窗口函数 `MAX(edt)` 是取历史行中 `edt` 字段的值,并不是和当前行有关。 即使当前行的 `edt` 较小(如 6月15日),只要前面某行的 `edt` 更晚(如 6月21日),就会被保留下来。 所以这里 `maxEdt = 2021-06-21` 是完全正确的。 💡 这个值的意义是:**在当前这次活动开始之前,该品牌最后一次可能还在打折的时间点(即最大结束日)**。 这个技巧常用于判断是否有时间重叠! --- ## ✅ 如何用这个字段判断是否连续或重叠? 我们可以利用 `maxEdt` 和当前行的 `stt` 比较: - 如果 `stt <= maxEdt`:说明当前活动与前面某个活动有重叠或紧接 → 可以合并 - 否则:中间有空档 → 不连续,需要新开一个区间 这是典型的“合并区间”问题,在数据库中可通过以下方式解决: --- ## ✅ 正确解法:计算每个品牌去重后的总打折天数(处理交叉日期) ### Step 1: 排序并找出是否属于同一连续区间(使用 `start_gap_flag`) ```sql WITH ordered_data AS ( SELECT brand, stt::date, edt::date FROM test4 ), marked_gaps AS ( SELECT brand, stt, edt, -- 如果当前开始时间 > 之前所有活动的最大结束时间,则说明断开了 CASE WHEN stt > MAX(edt) OVER ( PARTITION BY brand ORDER BY stt ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) THEN 1 ELSE 0 END AS is_new_period FROM ordered_data ), -- Step 2: 使用累计标志划分不同的连续区间 grouped_periods AS ( SELECT brand, stt, edt, SUM(is_new_period) OVER ( PARTITION BY brand ORDER BY stt ROWS UNBOUNDED PRECEDING ) AS period_id FROM marked_gaps ), -- Step 3: 对每个 period 取最小 stt 和最大 edt(即合并后的区间) merged_periods AS ( SELECT brand, MIN(stt) AS period_stt, MAX(edt) AS period_edt FROM grouped_periods GROUP BY brand, period_id ) -- Step 4: 计算每个品牌总的不重复天数 SELECT brand, SUM(period_edt - period_stt + 1) AS total_discount_days FROM merged_periods GROUP BY brand ORDER BY brand; ``` --- ### 📌 代码解释: 1. **`ordered_data`**:确保日期类型为 `date`,避免字符串比较出错。 2. **`marked_gaps`**: - 使用窗口函数获取此前所有区间的最大结束日。 - 若当前开始日 > 此前最大结束日 → 新周期开始(`is_new_period = 1`)。 3. **`grouped_periods`**: - 利用 `SUM(...) OVER (...)` 构造唯一 `period_id`,将连续或重叠的区间归为一组。 4. **`merged_periods`**: - 按 `brand + period_id` 分组,取每段的起止时间。 5. **最终统计**: - 每个合并区间的天数 = `end - start + 1`(包含首尾)。 - 累加得到各品牌总天数。 --- ### 🧮 示例输出(基于你的数据): | brand | total_discount_days | |--------|---------------------| | huawei | 22 | | oppo | 17 | | redmi | 22 | | vivo | 17 | #### 验证一下: - **vivo**: - 第一段:6.5 ~ 6.15 (11天) - 第二段:6.9 ~ 6.21(重叠了6.9~6.15) - 合并后:6.5 ~ 6.21 → 共 17 天 ✅ - **redmi**: - 6.5~6.21,6.9~6.15(完全包含),6.17~6.26 - 前两段已覆盖到6.21,第三段6.17 < 6.21 → 重叠 - 合并为 6.5 ~ 6.26 → 共 22 天 ✅ - **huawei**: - 6.5~6.26,6.9~6.15(包含),6.17~6.21(也在范围内) - 整体就是 6.5~6.26 → 22 天 ✅ --- ## ✅ 回答总结: - `maxEdt` 出现 `NULL` 是因为第一行前面没有数据,符合窗口定义。 - `maxEdt` 显示比当前 `edt` 大,是因为它反映的是**之前活动中最晚结束时间**,而非当前行。 - 要准确统计去重天数,必须先**合并重叠/连续的时间区间**,不能简单相加。 - 上述 SQL 完整实现了“按品牌合并打折时间段并计算总天数”的需求。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值