對(duì)比兩個(gè)對(duì)象不同的工具類(lèi)

場(chǎng)景

記錄變更日志骇两,例如:

場(chǎng)景1:管理員李四态贤,新增了用戶(hù):昵稱(chēng)【lll】气笙;真實(shí)姓名【李四】次企;是否被凍結(jié)【0】

場(chǎng)景2:管理員張三,修改了用戶(hù)數(shù)據(jù):地址【北京市】->【鄭州市】

場(chǎng)景1實(shí)現(xiàn)

教師類(lèi):

import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @Author nitric oxide
 * @Description
 * @Date 6:25 下午 2021/11/8
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Teacher {

    /**
     * id
     */
    @ApiModelProperty("id")
    private Integer id;

    /**
     * 昵稱(chēng)
     */
    @ApiModelProperty("昵稱(chēng)")
    private String nickName;

    /**
     * 密碼
     */
    @ApiModelProperty("密碼")
    private String password;

    /**
     * 真實(shí)姓名
     */
    @ApiModelProperty("真實(shí)姓名")
    private String trueName;

    /**
     * 手機(jī)號(hào)
     */
    @ApiModelProperty("手機(jī)號(hào)")
    private String phone;

    /**
     * 地址
     */
    @ApiModelProperty("地址")
    private String address;

    /**
     * 學(xué)生集合
     */
    @ApiModelProperty("學(xué)生集合")
    private List<Student> students;

    /**
     * 創(chuàng)建時(shí)間
     */
    @ApiModelProperty("創(chuàng)建時(shí)間")
    private LocalDateTime dbctime;

    /**
     * 創(chuàng)建時(shí)間
     */
    @ApiModelProperty("修改時(shí)間")
    private LocalDateTime dbutime;

}

PS:我們項(xiàng)目中生成實(shí)體類(lèi)的方式是用MyBatisX生成潜圃,會(huì)自帶很多不需要打印的字段缸棵,一些特殊的VO可能還有學(xué)生集合需要打印,還有一些繼承關(guān)系的屬性需要打犹菲凇(這只是我能想到的場(chǎng)景堵第,可能還有一些其他的業(yè)務(wù)場(chǎng)景)吧凉。

打印工具類(lèi)如下:

import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author nitric oxide
 * @Description 對(duì)象比較器
 * @Date 4:19 下午 2021/10/22
 */
@Slf4j
public class ObjectCompareUtils {

    /**
     * 排除不需要對(duì)比的屬性
     */
    private static final Map<String, Integer> EXCLUDE = new HashMap<String, Integer>(){{
        put("id", 0);
        put("dbctime", 0);
        put("dbutime", 0);
        put("serialVersionUID", 0);
    }};

    /**
     * 集合模板
     */
    private static final String TEMPLATE_COLLECTION = "%s: {%s}";

    /**
     * 單個(gè)對(duì)象輸出中文toString
     */
    private static final String TEMPLATE_OBJECT = "%s: 【%s】;";


    /**
     * 輸出對(duì)象toString中文版本
     * @param o1 傳遞的對(duì)象
     * @return toString中文
     * @throws IllegalAccessException
     */
    public static String getObjectContent(Object o1){
        if (o1 == null) {
            throw new RuntimeException("對(duì)象toString中文轉(zhuǎn)換異常踏志,對(duì)象不能為空");
        }
        StringBuilder sb = new StringBuilder();
        Class c1 = o1.getClass();
        while (c1 != null) {
            Field[] fields = c1.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if (EXCLUDE.get(field.getName()) != null) {
                    continue;
                }
                Object value = null;
                try {
                    value = field.get(o1);
                } catch (IllegalAccessException e) {
                    log.warn("通過(guò)反射獲取對(duì)象屬性失敗");
                }
                ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                String property = apiModelProperty == null || apiModelProperty.value().isEmpty() ? field.getName() : apiModelProperty.value();
                if (value instanceof Collection) {
                    //如果是對(duì)象中包含集合阀捅,則通過(guò)遞歸可以打印出所有的屬性
                    sb.append(String.format(TEMPLATE_COLLECTION, property, recursionObjectContent((Collection)value)));
                } else if (value != null) {
                    sb.append(String.format(TEMPLATE_OBJECT, property, value));
                }
            }
            //兼容打印繼承的屬性
            c1 = c1.getSuperclass();
        }
        return sb.toString();
    }

    private static String recursionObjectContent(Collection o1) {
        if (o1 == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        o1.forEach(c -> {
            sb.append("(");
            sb.append(getObjectContent(c));
            sb.append(")");
        });
        return sb.toString();
    }
}

測(cè)試類(lèi):

public static void main(String[] args) throws IllegalAccessException {

        List<Student> students = new ArrayList<Student>(){{
            add(new Student().setId(233).setTrueName("小紅").setPhone("12222222222"));
            add(new Student().setId(244).setTrueName("小張").setPhone("13333333333"));
            add(new Student().setId(255).setTrueName("小李").setPhone("14444444444"));
        }};

        Teacher object = new Teacher().setNickName("趙老師").setTrueName("趙四").setAddress("北京")
                .setStudents(students)
                .setDbctime(LocalDateTime.now())
                .setDbutime(LocalDateTime.now())
                .setId(12);

        System.out.println(ObjectCompareUtils.getObjectContent(object));

}

結(jié)果:

昵稱(chēng): 【趙老師】;真實(shí)姓名: 【趙四】针余;地址: 【北京】饲鄙;學(xué)生集合: {(學(xué)生姓名: 【小紅】;手機(jī)號(hào): 【12222222222】圆雁;)(學(xué)生姓名: 【小張】忍级;手機(jī)號(hào): 【13333333333】;)(學(xué)生姓名: 【小李】伪朽;手機(jī)號(hào): 【14444444444】轴咱;)}

場(chǎng)景2實(shí)現(xiàn)

在實(shí)現(xiàn)對(duì)比類(lèi)的時(shí)候只實(shí)現(xiàn)了PO(持久化對(duì)象)的對(duì)比。因?yàn)榭瓷厦娴慕Y(jié)果可以得出烈涮,如果對(duì)比學(xué)生集合的不同輸出結(jié)果太過(guò)于復(fù)雜朴肺;而且對(duì)于業(yè)務(wù)邏輯來(lái)講,所有的邏輯都是基于某張表的crud坚洽,此處的對(duì)比打印只涉及到update語(yǔ)句戈稿。

記錄日志前可以先通過(guò)select語(yǔ)句查詢(xún)出原始數(shù)據(jù),調(diào)用工具類(lèi)對(duì)比即可酪术。

教師類(lèi):

同上面的教師類(lèi)器瘪,自行把學(xué)生列表去掉

對(duì)比工具:

import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author ldy
 * @Description 對(duì)象比較器
 * @Date 4:19 下午 2021/10/22
 */
@Slf4j
public class ObjectCompareUtils {

    /**
     * 排除不需要對(duì)比的屬性
     */
    private static final Map<String, Integer> EXCLUDE = new HashMap<String, Integer>(){{
        put("id", 0);
        put("dbctime", 0);
        put("dbutime", 0);
        put("serialVersionUID", 0);
    }};

    /**
     * 屬性對(duì)比后生成的模板
     */
    private static final String TEMPLATE_DIFFERENT = "%s: 【%s】 -> 【%s】;";

    /**
     * 對(duì)比兩個(gè)對(duì)象的不同
     * @param o1 原來(lái)的對(duì)象
     * @param o2 新對(duì)象
     * @return
     * @throws IllegalAccessException
     */
    public static String getDifferentContent(Object o1, Object o2){
        if (o2 == null) {
            throw new RuntimeException("對(duì)比異常绘雁,新對(duì)象不能為空");
        }
        Class c = o2.getClass();
        if (o1 == null) {
            try {
                o1 = c.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (o1.getClass() != o2.getClass()) {
            throw new RuntimeException("對(duì)比異常,兩個(gè)參數(shù)非同一個(gè)類(lèi)");
        }

        Field[] fields = c.getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        for (Field field : fields) {
            field.setAccessible(true);
            if (EXCLUDE.get(field.getName()) != null) {
                continue;
            }
            Object oldValue = null;
            Object newValue = null;
            try {
                oldValue = field.get(o1);
                newValue = field.get(o2);
            } catch (IllegalAccessException e) {
                log.warn("通過(guò)反射獲取對(duì)象屬性失敗");
            }
            if (!nullableEquals(oldValue, newValue)) {
                ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                String property = apiModelProperty == null || apiModelProperty.value().isEmpty() ? field.getName() : apiModelProperty.value();
                sb.append(String.format(TEMPLATE_DIFFERENT, property, oldValue, newValue));
            }
        }
        return sb.toString();
    }

    private static boolean nullableEquals(Object a, Object b) {
        return (a == null && b == null) || (a != null && a.equals(b));
    }
}

跟上面的工具類(lèi)兩個(gè)可以寫(xiě)在一起援所,博客里面分開(kāi)是方便大家理解

測(cè)試類(lèi):

public static void main(String[] args) throws IllegalAccessException {

        Teacher oldObject = new Teacher().setNickName("趙老師").setTrueName("趙四").setAddress("北京")
                .setPhone("12222222222")
                .setDbctime(LocalDateTime.now())
                .setDbutime(LocalDateTime.now())
                .setId(12);

        Teacher newObject = new Teacher().setNickName("趙老師").setTrueName("趙四").setAddress("鄭州")
                .setPhone("13333333333")
                .setDbctime(LocalDateTime.now())
                .setDbutime(LocalDateTime.now())
                .setId(12);

        System.out.println(ObjectCompareUtils.getDifferentContent(oldObject, newObject));
}

結(jié)果:

手機(jī)號(hào): 【12222222222】 -> 【13333333333】庐舟;地址: 【北京】 -> 【鄭州】;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末住拭,一起剝皮案震驚了整個(gè)濱河市挪略,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滔岳,老刑警劉巖杠娱,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異谱煤,居然都是意外死亡摊求,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)刘离,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)室叉,“玉大人睹栖,你說(shuō)我怎么就攤上這事〖牒郏” “怎么了野来?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)踪旷。 經(jīng)常有香客問(wèn)我曼氛,道長(zhǎng),這世上最難降的妖魔是什么令野? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任搪锣,我火速辦了婚禮,結(jié)果婚禮上彩掐,老公的妹妹穿的比我還像新娘构舟。我一直安慰自己,他們只是感情好堵幽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布狗超。 她就那樣靜靜地躺著,像睡著了一般朴下。 火紅的嫁衣襯著肌膚如雪努咐。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天殴胧,我揣著相機(jī)與錄音渗稍,去河邊找鬼。 笑死团滥,一個(gè)胖子當(dāng)著我的面吹牛竿屹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灸姊,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拱燃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了力惯?” 一聲冷哼從身側(cè)響起碗誉,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎父晶,沒(méi)想到半個(gè)月后哮缺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甲喝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年尝苇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茎匠,死狀恐怖格仲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诵冒,我是刑警寧澤凯肋,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站汽馋,受9級(jí)特大地震影響侮东,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜豹芯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一悄雅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铁蹈,春花似錦宽闲、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沿腰,卻和暖如春览徒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颂龙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工习蓬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人措嵌。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓躲叼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親铅匹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子押赊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容