简单的提交保存,前台数据只需映射到一个对象字段,SpringMVC的对象绑定可轻松处理。这篇文章介绍了SpringMVC中的几种数据绑定:https://blog.csdn.net/z_dendy/article/details/12648641

在一些复杂的场景中前台包含多种类型,每种类型的对象有多个且支持增删改,通用的解决方式是将这些对象list封装在一个大对象里,前端name使用objects1[0].field1的形式做数据绑定,这样做可以实现,但存在的问题是每次增删需要维护下标,多种类型就得维护多个下标,而且前台动态增加记录时得拼接html,方式繁琐不够灵活

可使用map映射,map的key表示不同的字段,value表示该字段下不同的值,value的长度即是该类型下对象的个数,通过转换工具,后台可直接拿到这个对象list而前端不需要维护index和拼接html

前端

<form method="post" action="/component/submit" enctype="multipart/form-data">
    <div>
        <input type="text" name="users[id]" value="">
        <input type="text" name="users[realName]" value="realName1">
        <input type="text" name="users[birthday]" value="2018-06-01 11:01:01">
    </div>
    <div>
        <input type="text" name="users[id]" value="2">
        <input type="text" name="users[realName]" value="realName2">
        <input type="text" name="users[birthday]" value="2018-06-02 12:02:02">
    </div>
    <div>
        <input type="text" name="addresses[id]" value="1">
        <input type="text" name="addresses[street]" value="street1">
    </div>
    <div>
        <input type="text" name="addresses[id]" value="2">
        <input type="text" name="addresses[street]" value="street2">
    </div>
    <div>
        <input type="text" name="addresses[id]" value="">
        <input type="text" name="addresses[street]" value="street3">
    </div>
    <input type="submit">
</form>

后端接收

    @RequestMapping("/submit")
    @ResponseBody
    public String submit(TransferDomain transferDomain) throws Exception{
        List<User> userList = CollectionUtils.transferListObject(transferDomain.getUsers(), User.class, null);
        List<Address> addressList = CollectionUtils.transferListObject(transferDomain.getAddresses(), Address.class, null);
        System.out.println(JSON.toJSONString(userList, SerializerFeature.WriteDateUseDateFormat));
        System.out.println(JSON.toJSONString(addressList, SerializerFeature.WriteDateUseDateFormat));
        return  "success";
    }

转换工具,类型转换用到了com.alibaba.fastjson.util.TypeUtils

    /**
     * 1. sourceMap属性中对应某列值为空字符串 --- skip
     * 2. propValue2Null指定 --- null
     * @param sourceMap
     * @param clz
     * @param propValue2Null
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> List<T> transferListObject(Map<String, Object> sourceMap, Class<T> clz, Map<String, Object> propValue2Null) throws Exception {
        List<T> result = new ArrayList<T>();
        int length = -1;
        PropertyDescriptor[] des = Introspector.getBeanInfo(clz).getPropertyDescriptors();
        for (PropertyDescriptor d : des) {
            String fieldName = d.getName();
            Object object = sourceMap.get(d.getName());
            if (null != object && (object instanceof String || object instanceof String[]) && !"class".equalsIgnoreCase(fieldName)) {
                String[] cols = null;
                if (object instanceof String) {
                    cols = new String[]{(String) object};
                } else {
                    cols = (String[]) object;
                }
                if (null != cols && cols.length > 0) {
                    if (length == -1) {
                        length = cols.length;
                        for (int i = 0; i < length; i++) {
                            result.add(clz.newInstance());
                        }
                    } else {
                        if (length != cols.length) {
                            throw new RuntimeException("列长度不相同, " + fieldName + ", " + JSON.toJSONString(cols));
                        }
                    }
                    for (int i = 0; i < length; i++) {
                        if(StringUtils.isBlank(cols[i])){
                            continue; // skip blank value
                        }
                        String type = d.getPropertyType().getCanonicalName();
                        Method writeMethod = d.getWriteMethod();
                        Object invokeObj = result.get(i);
                        // now we first process field-value that implies null
                        if (null != propValue2Null && !propValue2Null.isEmpty() && propValue2Null.containsKey(fieldName)) {
                            String v = String.valueOf(propValue2Null.get(fieldName)); // if propValue2Null get(fieldName) return null then "null"
                            if (v.equals(cols[i])) {
                                writeMethod.invoke(invokeObj, new Object[]{null});
                                continue;
                            }
                        }
                        if ("java.lang.String".equals(type)) { // if cols[i] is null, no error occur
                            writeMethod.invoke(invokeObj, TypeUtils.castToString(cols[i]));
                        } else if ("java.math.BigDecimal".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToBigDecimal(cols[i]));
                        } else if ("java.util.Date".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToDate(cols[i]));
                        } else if ("java.lang.Long".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToLong(cols[i]));
                        } else if ("java.lang.Integer".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToInt(cols[i]));
                        } else if ("java.lang.Float".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToFloat(cols[i]));
                        } else if ("java.lang.Double".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToDouble(cols[i]));
                        } else if ("java.lang.Character".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToChar(cols[i]));
                        } else if ("java.lang.Boolean".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToBoolean(cols[i]));
                        } else if ("java.lang.Short".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToShort(cols[i]));
                        } else if ("java.lang.Byte".equals(type)) {
                            writeMethod.invoke(invokeObj, TypeUtils.castToByte(cols[i]));
                        } else {
                            // silent skip
                        }
                    }
                }

            }
        }
        return result;
    }

外层包装对象

public class TransferDomain implements Serializable{

    private static final long serialVersionUID = 1L;

    private Map<String, Object> users;


    private Map<String, Object> addresses;


    public Map<String, Object> getUsers() {
        return users;
    }

    public void setUsers(Map<String, Object> users) {
        this.users = users;
    }

    public Map<String, Object> getAddresses() {
        return addresses;
    }

    public void setAddresses(Map<String, Object> addresses) {
        this.addresses = addresses;
    }
}

输出

[{"birthday":"2018-06-01 11:01:01","realName":"realName1"},{"birthday":"2018-06-02 12:02:02","id":2,"realName":"realName2"}]
[{"id":1,"street":"street1"},{"id":2,"street":"street2"},{"street":"street3"}]

最后根据id是否为null做相应业务增删操作,转换效率会有一些问题,但是对于后台足够了