前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >fsdq系统笔记

fsdq系统笔记

作者头像
Remember_Ray
发布2020-09-15 10:07:11
1.3K0
发布2020-09-15 10:07:11
举报
文章被收录于专栏:Ray学习笔记Ray学习笔记

开发环境

IDEA + SSM + Oracle

涉及技术

暂无

需求

组合查询

分析

这里与我们平时的查询不太一样,可以由用户控制查询操作符。这样相当于把操作权交给用户,我们想到的就是SQL注入,那Mybatis怎么实现注入呢?

我们平时使用的基本都是 #{field},这样的好处是防止SQL注入,但这里我们想使用注入SQL,可以使用 ${field},有什么区别呢?

比较两条SQL语句:drop table ${tableName};drop table #{tableName};

当那么name=sys_user时,使用 ${tableName} 对应下来就是drop table sys_user; ,使用 #{tableName} 的就是drop table "sys_user";

只有drop table sys_user才是符合我们的预期的想要得到的结果。

这时候应该使用${tableName}

页面设计

代码

代码语言:javascript
复制
<% layout("/layouts/microHplus.html",{title:"登记表管理"}){ %>
<link rel="stylesheet" href="${ctxStatic }/fsdq/css/fsdqDjb.css" media="all"/>

<style>
    .error {
        z-index: 999999;
    }
</style>

<div class="gray-bg">

    <form id="inputForm" action="${ctx}/fsdq/djb/composeQueryList" method="get" class="form-horizontal">
        <div class="row">
            <div class="col-sm-12">
                <div class="ibox float-e-margins">
                    <div class="ibox-title">
                        <h5>组合查询</h5>
                        <div class="ibox-tools">
                            <a class="collapse-link">
                                <i class="fa fa-chevron-up"></i>
                            </a>
                        </div>
                    </div>
                    <div class="ibox-content">
                        <input type="button" class="btn btn-w-m btn-primary" onclick="addTrCx('cxTable', -1)" value="添加">
                        <input type="button" class="btn btn-w-m btn-danger" onclick="delTr('cxId')" value="删除">
                        <input type="submit" class="btn btn-w-m btn-info" value="重置" >
                        <input type="submit" class="btn btn-w-m btn-success" value="查询" >

                        <table class="table table-bordered" id="cxTable">
                            <thead>
                                <th><input id="allCx" type="checkbox" disabled/></th>
                                <th>搜索字段</th>
                                <th>搜索条件</th>
                                <th>搜索内容</th>
                            </thead>
                            <tbody>
                            <tr>
                                <td><input type='checkbox' name='cxId' disabled/></td>
                                <td>
                                    <!-- 必须和数据库字段一致 -->
                                    <select class="form-control" name="fsdqFamilySelectList[0].queryField">
                                        <option value="">请选择</option>
                                        <option value="name">母亲名字</option>
                                        <option value="age">年龄</option>
                                        <option value="education">文化程度</option>
                                        <option value="political">政治面貌</option>
                                        <option value="reportdate">申报时间</option>
                                    </select>
                                </td>

                                <td>
                                    <select class="form-control" name="fsdqFamilySelectList[0].queryCondition">
                                        <option value="">请选择</option>
                                        <option value="" disabled>-------- 字符型 --------</option>
                                        <option value="andLikeStr">包含   (搜索的内容中间包含所输入的查询条件值)</option>
                                        <option value="andStr">等于   (搜索的内容等于所输入的查询条件值)</option>
                                        <option value="andNotLikeStr">不等于   (搜索的内容不等于所输入的查询条件值)</option>
                                        <option value="" disabled>-------- 日期型 --------</option>
                                        <option value="andEqualDate">等于   (搜索的日期等于所输入的查询条件值)</option>
                                        <option value="andGtDate">晚于   (搜索的日期晚于所输入的查询条件值)</option>
                                        <option value="andGtEqualDate">晚于或等于   (搜索的日期晚于或等于所输入的查询条件值)</option>
                                        <option value="andLtDate">早于   (搜索的日期早于所输入的查询条件值)</option>
                                        <option value="andLtEqualDate">早于或等于   (搜索的日期早于或等于所输入的查询条件值)</option>
                                        <option value="" disabled>-------- 数字型 --------</option>
                                        <option value="andInteger">等于   (搜索的字段等于所输入的查询条件值)</option>
                                        <option value="andGtInteger">大于   (搜索的字段大于所输入的查询条件值)</option>
                                        <option value="andGtEqualInteger">大于或等于   (搜索的字段大于或等于所输入的查询条件值)</option>
                                        <option value="andLtInteger">小于   (搜索的字段小于所输入的查询条件值)</option>
                                        <option value="andLtEqualInteger">小于或等于   (搜索的字段小于或等于所输入的查询条件值)</option>
                                    </select>
                                </td>

                                <td>
                                    <input type="text" class="form-control" name="fsdqFamilySelectList[0].queryValue" autocomplete="off" placeholder="请填写">
                                </td>
                            </tr>
                            </tbody>
                        </table>
                        <table class="layui-hide" id="test" lay-filter="test"></table>
                    </div>
                </div>
            </div>
        </div>
    </form>
</div>

<script src="${ctxStatic }/fsdq/js/fsdqDjb.js?v=1.0.1"></script>
<script>
    var cxIndex = 1;
    function addTrCx(tab, row) {
        var trHtml = "<tr>\n" +
            "                                <td><input type='checkbox' name='cxId'/></td>\n" +
            "                                <td>\n" +
            "                                    <select class=\"form-control\" name=\"fsdqFamilySelectList[" + cxIndex +"].queryField\">\n" +
            "                                        <option value=''>请选择</option>\n" +
            "                                        <option value=\"name\">母亲名字</option>\n" +
            "                                        <option value=\"age\">年龄</option>\n" +
            "                                        <option value=\"education\">文化程度</option>\n" +
            "                                        <option value=\"political\">政治面貌</option>\n" +
            "                                        <option value=\"reportdate\">申报时间</option>\n" +
            "                                    </select>\n" +
            "                                </td>\n" +
            "\n" +
            "                                <td>\n" +
            "                                    <select class=\"form-control\" name=\"fsdqFamilySelectList[" + cxIndex +"].queryCondition\">\n" +
            "                                        <option value=''>请选择</option>\n" +
            "                                        <option value=\"\" disabled>-------- 字符型 --------</option>\n" +
            "                                        <option value=\"andLikeStr\">包含   (搜索的内容中间包含所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andStr\">等于   (搜索的内容等于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andNotLikeStr\">不等于   (搜索的内容不等于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"\" disabled>-------- 日期型 --------</option>\n" +
            "                                        <option value=\"andEqualDate\">等于   (搜索的日期等于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andGtDate\">晚于   (搜索的日期晚于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andGtEqualDate\">晚于或等于   (搜索的日期晚于或等于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andLtDate\">早于   (搜索的日期早于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andLtEqualDate\">早于或等于   (搜索的日期早于或等于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"\" disabled>-------- 数字型 --------</option>\n" +
            "                                        <option value=\"andInteger\">等于   (搜索的字段等于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andGtInteger\">大于   (搜索的字段大于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andGtEqualInteger\">大于或等于   (搜索的字段大于或等于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andLtInteger\">小于   (搜索的字段小于所输入的查询条件值)</option>\n" +
            "                                        <option value=\"andLtEqualInteger\">小于或等于   (搜索的字段小于或等于所输入的查询条件值)</option>\n" +
            "                                    </select>\n" +
            "                                </td>\n" +
            "\n" +
            "                                <td>\n" +
            "                                    <input type=\"text\" class=\"form-control\" name=\"fsdqFamilySelectList[" + cxIndex +"].queryValue\" autocomplete=\"off\" placeholder=\"请填写\">\n" +
            "                                </td>\n" +
            "                            </tr>";

        addTr(tab, row, trHtml);
        cxIndex++;
    }
</script>
<script>
    layui.use(['form','layer','laydate','table','laytpl'],function(){
        var form = layui.form,
            layer = parent.layer === undefined ? layui.layer : top.layer,
            $ = layui.jquery,
            laydate = layui.laydate,
            laytpl = layui.laytpl,
            table = layui.table;
        table.render({
            elem: '#test'
            // , url: '${ctx}/fsdq/djb/composeQueryListAjax'
            , data: ${composeQueryListJson}
            , toolbar: '#toolbarDemo'
            , title: '母亲家庭报表'
            , cols: [[
                {type: 'numbers',title:'序号'},
                {field:'name', title: '姓名'},
                {field: 'age', title: '年龄'},
                {field:'birthday', title: '出生年月'},
                {field:'political', title: '政治面貌'},
                {field:'employment', title: '就业情况'},
                {field:'education', title: '文化程度'},
                {field:'income', title: '家庭月收入(元)'},
                {field:'insured', title: '低保情况'},

            ]]
        });
    });
</script>
<%}%>

fsdqDjb.js

代码语言:javascript
复制
////////添加一行、删除一行封装方法///////
/**
 * 为table指定行添加一行
 *
 * tab 表id
 * row 行数,如:0->第一行 1->第二行 -2->倒数第二行 -1->最后一行
 * trHtml 添加行的html代码
 *
 */
function addTr(tab, row, trHtml) {
    //获取table最后一行 $("#tab tr:last")
    //获取table第一行 $("#tab tr").eq(0)
    //获取table倒数第二行 $("#tab tr").eq(-2)
    var $tr = $("#" + tab + " tr").eq(row);
    $tr.after(trHtml);
}

/**
 * 删除单个
 * @param ckb
 */
function delTr(ckb) {
    //获取选中的复选框,然后循环遍历删除
    var ckbs = $("input[name=" + ckb + "]:checked");
    ckbs.each(function() {
        $(this).parent().parent().remove();
    });
}

/**
 * 全选
 * allCkb 全选复选框的id
 * items 复选框的name
 */
function allCheck(allCkb, items) {
    $("#" + allCkb).click(function() {
        $('[name=' + items + ']:checkbox').attr("checked", this.checked);
    });
}

/** 删除 tr */
// function delTr(name) {
//     delTr(name);
// }

实体类

使用组合对象集合接收数据

FsdqFamily.java

代码语言:javascript
复制
private List<FsdqFamilySelect> fsdqFamilySelectList;

FsdqFamilySelect.java

代码语言:javascript
复制
public class FsdqFamilySelect extends DataEntity<FsdqFamilySelect> {

    private String queryField;     // 查询的字段
    private String queryCondition; // 查询的条件
    private String queryValue;     // 查询的值

    public String getQueryField() {
        return queryField;
    }

    public void setQueryField(String queryField) {
        this.queryField = queryField;
    }

    public String getQueryCondition() {
        return queryCondition;
    }

    public void setQueryCondition(String queryCondition) {
        this.queryCondition = queryCondition;
    }

    public String getQueryValue() {
        return queryValue;
    }

    public void setQueryValue(String queryValue) {
        this.queryValue = queryValue;
    }

    @Override
    public String toString() {
        return "FsdqFamilySelect{" +
                "queryField='" + queryField + '\'' +
                ", queryCondition='" + queryCondition + '\'' +
                ", queryValue='" + queryValue + '\'' +
                '}';
    }
}

服务层

接收到前端传递的参数

开始处理

代码语言:javascript
复制
/**
	 * 注入查询
	 * @author Ray
	 */
	public List<FsdqFamily> composeQuery(FsdqFamily fsdqFamily) {

		List<FsdqFamily> fsdqFamilyList = new ArrayList<>();

		try {
			if (ObjectUtil.isNotNull(fsdqFamily)
					&& CollectionUtil.isNotEmpty(fsdqFamily.getFsdqFamilySelectList())
					&& !fsdqFamily.getFsdqFamilySelectList().isEmpty()) {

				StringBuilder sqlString = new StringBuilder();
				InfusionUtils utils = new InfusionUtils();

				for (FsdqFamilySelect select :
						fsdqFamily.getFsdqFamilySelectList()) {
					if (StringUtils.isNotBlank(select.getQueryCondition())
							&& StringUtils.isNotBlank(select.getQueryCondition())
							&& StringUtils.isNotBlank(select.getQueryValue())) {
						Method method = utils.getClass().getMethod(select.getQueryCondition(), String.class, String.class, String.class);
						String sql = (String) method.invoke(utils, InfusionUtils.TABLE_ALIAS, select.getQueryField(), select.getQueryValue());
						sqlString.append(sql);
					}
				}

				if (StringUtils.isNotBlank(sqlString.toString())) {
					fsdqFamilyList = fsdqFamilyDao.composeQuery(sqlString.toString());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return fsdqFamilyList;
	}

注意

代码语言:javascript
复制
Method method = utils.getClass().getMethod(select.getQueryCondition(), String.class, String.class, String.class);

这里我们使用了反射的知识,调用方法要指定参数(不然会找不到对应的方法,注意这个坑),这里我们的参数都是String.class的,只是方法名不一样,而我们的方法名是由select.getQueryCondition()控制的,跟参数耦合在一起了。

然后调用该方法,就可以得到需要注入的SQL语句

代码语言:javascript
复制
String sql = (String) method.invoke(utils, InfusionUtils.TABLE_ALIAS, select.getQueryField(), select.getQueryValue());

得到注入的SQL语句

DAO层

代码语言:javascript
复制
@MyBatisDao
public interface FsdqFamilyDao extends CrudDao<FsdqFamily> {
    List<FsdqFamily> composeQuery(@Param(value = "sqlString") String sqlString);
}

Mapper

代码语言:javascript
复制
   <select id="composeQuery" resultType="com.dfht.modules.fsdq.entity.FsdqFamily">
		SELECT
		<include refid="fsdqFamilyColumns"/>
		FROM
		fsdq_family a
		<where>
			a.del_flag = 0
			<if test="sqlString != null and sqlString != ''">
				${sqlString}
			</if>
		</where>
</select>

得到的SQL语句

代码语言:javascript
复制
SELECT * WHERE a.del_flag = 0 AND a.draft_flag = '1' AND a.name LIKE '%小红%' AND a.age > 22

工具类

自己封装的工具类,简单的生成注入的SQL语句

代码语言:javascript
复制
package com.dfht.modules.fsdq.util;

import org.apache.commons.lang3.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @Description: sql注入,打死不改版
 * @Author Ray
 * @Date 2020/09/10 9:07
 * @Version 1.0
 */
public class InfusionUtils {

    public static final String AND = " AND ";
    public static final String LIKE = " LIKE ";
    public static final String NOT = " NOT ";

    public static final String EQUAL = " = ";
    public static final String SINGLE_QUOTES = "'";
    public static final String PERCENT_SIGN = "%";

    public static final String LT = " < ";
    public static final String LT_EQUAL = " <= ";
    public static final String GT = " > ";
    public static final String GT_EQUAL = " >= ";

    public static final String TO_DATE_START = "to_date('";
    public static final String TO_DATE_END = "','yyyy-mm-dd hh24:mi:ss')";

    public static final String PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss";

    public static final String TABLE_ALIAS = "a";


    /**
     * 字符串型 -- 包含 -- 搜索的内容中间包含所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 字符串
     * @return
     */
    public static String andLikeStr(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(LIKE);
        sqlString.append(SINGLE_QUOTES).append(PERCENT_SIGN)
                .append(value)
                .append(PERCENT_SIGN).append(SINGLE_QUOTES);

        return sqlString.toString();
    }

    /**
     * 字符串型 -- 等于 -- 搜索的内容等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 字符串
     * @return
     */
    public static String andStr(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(EQUAL);
        sqlString.append(SINGLE_QUOTES).append(value).append(SINGLE_QUOTES);

        return sqlString.toString();
    }

    /**
     * 字符串型 -- 不等于 -- 搜索的内容不等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 字符串
     * @return
     */
    public static String andNotLikeStr(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(NOT);
        sqlString.append(LIKE);
        sqlString.append(SINGLE_QUOTES).append(PERCENT_SIGN)
                .append(value)
                .append(PERCENT_SIGN).append(SINGLE_QUOTES);

        return sqlString.toString();
    }


    /**
     * 日期型 -- 等于 -- 搜索的日期等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 日期   yyyy-mm-dd hh24:mi:ss  格式
     * @return
     */
    public static String andEqualDate(String alias, String sqlField, String value) {

        if (!isDateVail(value)) {
            return "";
        }

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(EQUAL);
        sqlString.append(TO_DATE_START).append(value).append(TO_DATE_END);

        return sqlString.toString();
    }

    /**
     * 日期型 -- 晚于 -- 搜索的日期晚于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 日期   yyyy-mm-dd hh24:mi:ss  格式
     * @return
     */
    public static String andGtDate(String alias, String sqlField, String value) {

        if (!isDateVail(value)) {
            return "";
        }

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(GT);
        sqlString.append(TO_DATE_START).append(value).append(TO_DATE_END);

        return sqlString.toString();
    }

    /**
     * 日期型 -- 晚于或等于 -- 搜索的日期晚于或等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 日期   yyyy-mm-dd hh24:mi:ss  格式
     * @return
     */
    public static String andGtEqualDate(String alias, String sqlField, String value) {

        if (!isDateVail(value)) {
            return "";
        }

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(GT_EQUAL);
        sqlString.append(TO_DATE_START).append(value).append(TO_DATE_END);

        return sqlString.toString();
    }

    /**
     * 日期型 -- 早于 -- 搜索的日期早于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 日期   yyyy-mm-dd hh24:mi:ss  格式
     * @return
     */
    public static String andLtDate(String alias, String sqlField, String value) {

        if (!isDateVail(value)) {
            return "";
        }

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(LT);
        sqlString.append(TO_DATE_START).append(value).append(TO_DATE_END);

        return sqlString.toString();
    }

    /**
     * 日期型 -- 早于或等于 -- 搜索的日期早于或等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值 日期   yyyy-mm-dd hh24:mi:ss  格式
     * @return
     */
    public static String andLtEqualDate(String alias, String sqlField, String value) {

        if (!isDateVail(value)) {
            return "";
        }

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(LT_EQUAL);
        sqlString.append(TO_DATE_START).append(value).append(TO_DATE_END);

        return sqlString.toString();
    }


    /**
     * 数字型 -- 等于 -- 搜索的字段等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值  数值
     * @return
     */
    public static String andInteger(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(EQUAL);
        sqlString.append(value);

        return sqlString.toString();
    }

    /**
     * 数字型 -- 大于 -- 搜索的字段大于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值  数值
     * @return
     */
    public static String andGtInteger(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(GT);
        sqlString.append(value);

        return sqlString.toString();
    }

    /**
     * 数字型 -- 大于或等于 -- 搜索的字段大于或等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值  数值
     * @return
     */
    public static String andGtEqualInteger(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(GT_EQUAL);
        sqlString.append(value);

        return sqlString.toString();
    }

    /**
     * 数字型 -- 小于 -- 搜索的字段小于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值  数值
     * @return
     */
    public static String andLtInteger(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(LT);
        sqlString.append(value);

        return sqlString.toString();
    }

    /**
     * 数字型 -- 小于或等于 -- 搜索的字段小于或等于所输入的查询条件值。
     * @param alias 数据库别名
     * @param sqlField 数据库字段名
     * @param value 查询的值  数值
     * @return
     */
    public static String andLtEqualInteger(String alias, String sqlField, String value) {

        StringBuilder sqlString = new StringBuilder();
        sqlString.append(AND);
        sqlString.append(StringUtils.isNotBlank(alias) ? alias + "." : "");
        sqlString.append(sqlField);
        sqlString.append(LT_EQUAL);
        sqlString.append(value);

        return sqlString.toString();
    }



    /**
     * 判断输入的字符串是否满足时间格式 : yyyy-MM-dd HH:mm:ss
     * @param patternString 需要验证的字符串
     * @return 合法返回 true ; 不合法返回false
     */
    private static boolean isDateVail(String patternString) {
        //用于指定 日期/时间 模式
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(PATTERN_DEFAULT);
        boolean flag = true;
        try {
            //Java 8 新添API 用于解析日期和时间
            LocalDateTime.parse(patternString, dtf);
        } catch (Exception e) {
            flag = false;
        }
        return flag;
    }

    public static void main(String[] args) {
        String strAnd = andStr("a", "name", "123");
        System.out.println("strAnd = " + strAnd);

        String andLikeStr = andLikeStr("a", "name", "123");
        System.out.println("andLikeStr = " + andLikeStr);

        String andNotLikeStr = andNotLikeStr("a", "name", "123");
        System.out.println("andNotLikeStr = " + andNotLikeStr);

        String dateAndEqual = andEqualDate("a", "REPORTDATE", "2020-09-02 00:00:00");
        System.out.println("dateAndEqual = " + dateAndEqual);

        String dateAndGt = andGtDate("a", "REPORTDATE", "2020-09-02 00:00:00");
        System.out.println("dateAndGt = " + dateAndGt);

        String dateAndGtEqual = andGtEqualDate("a", "REPORTDATE", "2020-09-02 00:00:00");
        System.out.println("dateAndGtEqual = " + dateAndGtEqual);

        String dateAndLt = andLtDate("a", "REPORTDATE", "2020-09-02 00:00:00");
        System.out.println("dateAndLt = " + dateAndLt);

        String dateAndLtEqual = andLtEqualDate("a", "REPORTDATE", "2020-09-02 00:00:00");
        System.out.println("dateAndLtEqual = " + dateAndLtEqual);

        String integerAnd = andInteger("a", "age", "31");
        System.out.println("integerAnd = " + integerAnd);

        String integerGtAnd = andGtInteger("a", "age", "31");
        System.out.println("integerGtAnd = " + integerGtAnd);

        String integerGtEqualAnd = andGtEqualInteger("a", "age", "31");
        System.out.println("integerGtEqualAnd = " + integerGtEqualAnd);

        String integerLtAnd = andLtInteger("a", "age", "31");
        System.out.println("integerLtAnd = " + integerLtAnd);

        String integerLtEqualAnd = andLtEqualInteger("a", "age", "31");
        System.out.println("integerLtEqualAnd = " + integerLtEqualAnd);

        //System.out.println(isDateVail("2020-09-02 00:00:00"));
        //System.out.println(isDateVail("2020-09-02"));
    }
}

两个实体类比较差异

传入两个实体类,可以比较指定字段或全部字段,如果值一致跳过,如果值不一致,则记录在Map里。

代码语言:javascript
复制
package com.dfht.modules.fsdq.util;

import cn.hutool.core.util.ObjectUtil;
import io.swagger.annotations.ApiModelProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.IntFunction;

/**
 * @Description: 数据差异比较
 * @Author Ray
 * @Date 2020/09/11 15:16
 * @Version 1.0
 */
public class CompareFields {

    private static Logger log = LoggerFactory.getLogger(CompareFields.class);

    /**
     * 比较两个实体属性值,返回一个map以有差异的属性名为key,value为一个list分别存beforeObj,afterObj此属性名的值
     * @param beforeObj 进行属性比较的对象 before
     * @param afterObj 进行属性比较的对象 after
     * @param compareArr 选择要比较的属性数组  1.指定属性 {xxx}  2.全部属性 {} 或 null
     * @param nonCompareArr 不比较的属性数字
     * @return 属性差异比较结果 Map<String, Map>
     */
    public static Map<String, Map> compareFields(Object beforeObj, Object afterObj, String[] compareArr, String[] nonCompareArr) {
        try {
            //装返回值得
            Map<String, Map> map = new LinkedHashMap<>();
            //需要对比的字段list
            List<String> compareList = null;
            //不需要对比的字段list
            List<String> nonCompareList = null;
            if (compareArr != null && compareArr.length > 0) {
                // array转化为list
                compareList = Arrays.asList(compareArr);
            }
            if (nonCompareArr != null && nonCompareArr.length > 0) {
                // array转化为list
                nonCompareList = Arrays.asList(nonCompareArr);
            }
            // 只有两个对象都是同一类型的才有可比性
            if (beforeObj.getClass() == afterObj.getClass()) {
                Class clazz = beforeObj.getClass();
                // 获取object的属性描述
                PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz,
                        Object.class).getPropertyDescriptors();
                // 这里就是所有的属性了
                for (PropertyDescriptor pd : pds) {
                    // 属性名
                    String name = pd.getName();
                    // 跳过
                    if (nonCompareList.contains(name)) {
                        continue;
                    }
                    // 指定属性判断
                    boolean condition1 = compareList != null && compareList.contains(name);
                    // 全部属性判断
                    boolean condition2 = compareList == null;
                    if (condition1 || condition2) {
                        map.putAll(handleData(clazz, name, pd, beforeObj, afterObj, map));
                    }
                }
            }else {
                log.error("对象类型不一致,不能完成对比");
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 封装数据处理
     */
    private static Map<String, Map> handleData(Class clazz, String name, PropertyDescriptor pd, Object beforeObj, Object afterObj, Map<String, Map> map) throws Exception {
        // 注解内容
        Field fieldName = clazz.getDeclaredField(name);
        ApiModelProperty annotation = fieldName.getAnnotation(ApiModelProperty.class);
        String annotationDescribe = ObjectUtil.isNotNull(annotation) ? annotation.value() : "";
        // get方法
        Method readMethod = pd.getReadMethod();
        // 在beforeObj上调用get方法等同于获得beforeObj的属性值
        Object objBefore = readMethod.invoke(beforeObj);
        // 在afterObj上调用get方法等同于获得afterObj的属性值
        Object objAfter = readMethod.invoke(afterObj);
        if (objBefore == null || objAfter == null) {
            return map;
        }
        if (objBefore instanceof Timestamp) {
            objBefore = new Date(((Timestamp) objBefore).getTime());
        }
        if (objAfter instanceof Timestamp) {
            objAfter = new Date(((Timestamp) objAfter).getTime());
        }
        if (objBefore == null && objAfter != null) {
            Map m = new LinkedHashMap();
            m.put("describe",annotationDescribe);
            m.put("objBefore",objBefore);
            m.put("objAfter",objAfter);
            map.put(name, m);
        }
        // 比较这两个值是否相等,不等则放入map
        if (!objBefore.equals(objAfter)) {
            Map m = new LinkedHashMap();
            m.put("describe",annotationDescribe);
            m.put("objBefore",objBefore);
            m.put("objAfter",objAfter);
            map.put(name, m);
        }

        return map;
    }


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

        UserTest user1 = new UserTest("1", "Ray", 14, new Date());

        Thread.sleep(1000);

        UserTest user2 = new UserTest("2", "RayK", 14, new Date());

        String[] column = new String[] {"id", "name", "age"};  // 获取指定
        //String[] column = new String[] {};   // 获取全部
        String[] nonColumn = new String[] {};   // 获取全部

        Map<String, Map> resultMap = compareFields(user1, user2, column, nonColumn);
        //Map<String, Map> resultMap = compareFields(user1, user2, null);  // 获取全部

        System.out.println(resultMap);
    }
}


class UserTest {

    @ApiModelProperty(value = "主键")
    private String id;
    @ApiModelProperty(value = "姓名")
    private String name;
    @ApiModelProperty(value = "年龄")
    private int age;
    @ApiModelProperty(value = "生日")
    private Date birthday;

    public UserTest() {
    }

    public UserTest(String id, String name, int age, Date birthday) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

运行Main方法

这里我们比较的是 "id", "name", "age" 三个字段,在两个UserTest对象中,除了age一样,其他都是不同的。

得到结果

控制层

代码语言:javascript
复制
@RequestMapping(value = "editNotesForm")
public String editNotesForm(String selectedIds, Model model) {

	//System.out.println("selectedIds = " + selectedIds);

	Map<String, Map> resultMap = new LinkedHashMap<>();
	List<String> ids = Arrays.asList(selectedIds.split(","));

	if (!ids.isEmpty()) {
		List<FsdqFamilyHistoryVO> familyHistories = fsdqFamilyHistoryService.findListByOldId(ids);
		if (!familyHistories.isEmpty() && familyHistories.size() == ids.size()) {
			String[] column = new String[] {};
			String[] nonColumn = new String[] {"id", "updateDate", "userName", "version"};
			resultMap = CompareFields.compareFields(familyHistories.get(0), familyHistories.get(1), column, nonColumn);

			model.addAttribute("beforeObj", familyHistories.get(0));
			model.addAttribute("afterObj", familyHistories.get(1));
		}
	}

	// 测试数据
	/*Map m = new LinkedHashMap();
	m.put("describe","主键");
	m.put("objBefore","1");
	m.put("objAfter","2");
	resultMap.put("id", m);

	Map m2 = new LinkedHashMap();
	m2.put("describe","姓名");
	m2.put("objBefore","Ray");
	m2.put("objAfter","Ray2");
	resultMap.put("name", m2);

	Map m3 = new LinkedHashMap();
	m3.put("describe","年龄");
	m3.put("objBefore","12");
	m3.put("objAfter","14");
	resultMap.put("age", m3);*/

	model.addAttribute("resultMap", resultMap);

	return "modules/fsdq/djb/fsdqEditNotesForm";
}

页面展示

这里使用模板引擎 beetl

代码语言:javascript
复制
<% layout("/layouts/microHplus.html",{title:"家庭成员管理"}){ %>
<style>
    .form-group {
        margin-bottom: 50px;
        height: 5px;
    }
    .myLabel {
        text-align: right;
        line-height: 30px;
    }
</style>
<div class="layui-tab layui-tab-brief">
    <div class="layui-tab layui-tab-brief">

        <div class="row">
            <div class="col-sm-6 b-r">
                <div class="ibox float-e-margins">
                    <div class="ibox-title">
                        <blockquote>
                            <p>版本号:<strong>${beforeObj.version!}</strong></p>
                            <small>修改人:${beforeObj.userName!}</small>
                            <small>修改时间:${!(beforeObj.updateDate,dateFormat='yyyy-MM-dd HH:mm:ss')}</small>
                        </blockquote>
                    </div>
                    <div class="ibox-content">
                        <% if(resultMap != null) {%>
                            <% for(itemMap in resultMap) {%>
                                <div class="form-group">
                                    <% for(item in itemMap.value) {%>
                                        <% if (item.key == 'describe') {%>
                                            <label class="col-sm-2 control-label myLabel">${item.value}</label>
                                        <% } %>
                                        <% if (item.key == 'objBefore') {%>
                                            <div class="col-sm-10">
                                                <input type="text" class="form-control" value="${item.value}">
                                            </div>
                                        <% } %>
                                    <% } %>
                                </div>
                            <% } %>
                        <% } %>
                    </div>
                </div>
            </div>

            <!-- 分割线 -->

            <div class="col-sm-6">
                <div class="ibox float-e-margins">
                    <div class="ibox-title">
                        <blockquote>
                            <p>版本号:<strong>${afterObj.version!}</strong></p>
                            <small>修改人:${afterObj.userName!}</small>
                            <small>修改时间:${!(afterObj.updateDate,dateFormat='yyyy-MM-dd HH:mm:ss')}</small>
                        </blockquote>
                    </div>
                    <div class="ibox-content">
                        <% if(resultMap != null) {%>
                            <% for(itemMap in resultMap) {%>
                                <div class="form-group">
                                    <% for(item in itemMap.value) {%>
                                        <% if (item.key == 'describe') {%>
                                            <label class="col-sm-2 control-label myLabel">${item.value}</label>
                                        <% } %>
                                        <% if (item.key == 'objAfter') {%>
                                            <div class="col-sm-10">
                                                <input type="text" class="form-control" value="${item.value}">
                                            </div>
                                        <% } %>
                                    <% } %>
                                </div>
                            <% } %>
                        <% } %>
                    </div>
                </div>
            </div>
        </div>

        <% if(resultMap == null) {%>
        <div class="panel panel-danger">
            <div class="panel-heading">
                数据错误
            </div>
            <div class="panel-body">
                <p>数据存在问题,请检查!</p>
            </div>
            <div class="panel-footer">
                Error
            </div>
        </div>
        <% } %>

    </div>
</div>
<script>
    $(function () {

    })
</script>
<%}%>

效果图

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-12|,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开发环境
    • 涉及技术
    • 需求
      • 组合查询
        • 分析
        • 页面设计
        • 实体类
        • 服务层
        • DAO层
        • Mapper
        • 工具类
      • 两个实体类比较差异
        • 运行Main方法
        • 控制层
        • 页面展示
        • 效果图
    相关产品与服务
    腾讯云 BI
    腾讯云 BI(Business Intelligence,BI)提供从数据源接入、数据建模到数据可视化分析全流程的BI能力,帮助经营者快速获取决策数据依据。系统采用敏捷自助式设计,使用者仅需通过简单拖拽即可完成原本复杂的报表开发过程,并支持报表的分享、推送等企业协作场景。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档