MongoDB自动封装更改条件

源码地址:https://gitee.com/Jakewabc/small-study-case/blob/master/nm-demo/mongodb-demo/src/test/java/com/mongodb/TestMongodb.java

在使用MongoDB时都会发现一个搬砖的工程,那就是每次更改封装条件的时候都需要手动一个对象属性一个数据的填,并没有像 mybatis-plus那么封装一个方法,将更改的数据自动转换,这样会避免很多搬砖工程。那么SpringDataJpa没有提供,我们就自己造,方便自己方便他人。

我是使用反射机制实现的,代码我直接粘贴出来,关于为什么这样写,参考其他文章,不在重复描述。

方式一

这种方式在JDK1.8版本可以使用,如果更高版本就不能使用了。因为我使用了 PropertyDescriptor获取get方法,当前对象在高版本已经废除了,不是过期,是废除。如果你还是想用那么可以选择 “直接打开私有权限的”的方式,这种方式使用也没有问题。

 PropertyDescriptor pd = new PropertyDescriptor(field.getName(),clazz);
 /***
     * 将对象中非空的数据转为Update条件,用于MongoDB更改使用
     * @param obj
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update updateNoNull(Object obj) throws Exception {
        return updateNoNull(obj,false);
    }

    /**
     * 将对象中非空的数据转为Update条件,用于MongoDB更改使用
     * @param t 有数据的那个对象
     * @param cla 当前对象的类型
     * @param considerThread true:考虑现成安全,false:不考虑
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update updateNoNull(Object obj, Boolean considerThread) throws Exception {
        Class<?> clazz = null;
        if (considerThread) {
            //通过类装载器获取t类对象
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            clazz = loader.loadClass(obj.getClass().getName());
        } else {
            clazz = obj.getClass();
        }
        Update update = new Update();
        // 获取t对象的所有属性
        Field[] fields = clazz.getDeclaredFields();

        // TODO: 2021/3/26 方式一,这种直接打开私有权限的,似乎不太安全。采用get方法获取,所以最终采用方法二。
//        for (Field field : fields) {
//             // 打开私有属性操作权限,这种方式似乎不是很安全。
//            field.setAccessible(true);
//            // 属性名
//            String fieldName = field.getName();
//
//            Object object = field.get(obj);
//            if (!ObjectUtils.isEmpty(object)){
//                update.set(fieldName,object);
//            }
//        }

        // TODO: 2021/3/26 方法二
        for (Field field : fields) {
            PropertyDescriptor pd = new PropertyDescriptor(field.getName(),clazz);
            String fieldName = field.getName();
            //获得get方法
            Method getMethod = pd.getReadMethod();
            //执行get方法返回一个Object,因为我这里是直接获取数据即可,不用管是什么数据类型。如果需要判断返回相应类型的数据,那么这里可以做一个类型匹配在做类型转换即可。
            Object object = getMethod.invoke(obj);
            if (!ObjectUtils.isEmpty(object)){
                update.set(fieldName,object);
            }
        }
        return update;
    }

    /**
     * 将对象中中的数据转为Update条件,用于MongoDB更改使用。这个方法不排除空数据,即使是空数据多添加到条件中。
     * @param obj
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update update(Object obj) throws Exception {
        return update(obj,false);
    }

    /**
     * 将对象中中的数据转为Update条件,用于MongoDB更改使用。这个方法不排除空数据,即使是空数据多添加到条件中。
     * @param t 有数据的那个对象
     * @param cla 当前对象的类型
     * @param considerThread true:考虑现成安全,false:不考虑
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update update(Object obj, Boolean considerThread) throws Exception {
        Class<?> clazz = null;
        if (considerThread) {
            //通过类装载器获取t类对象
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            clazz = loader.loadClass(obj.getClass().getName());
        } else {
            clazz = obj.getClass();
        }
        Update update = new Update();
        // 获取t对象的所有属性
        Field[] fields = clazz.getDeclaredFields();

        // TODO: 2021/3/26 方法二
        for (Field field : fields) {
            PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
            String fieldName = field.getName();
            //获得get方法
            Method getMethod = pd.getReadMethod();
            //执行get方法返回一个Object,因为我这里是直接获取数据即可,不用管是什么数据类型。如果需要判断返回相应类型的数据,那么这里可以做一个类型匹配在做类型转换即可。
            Object object = getMethod.invoke(obj);
            update.set(fieldName, object);
        }
        return update;
    }
  • 使用
@Autowired
    private MongoTemplate mongoTemplate ;

    /**
     * 将对象非空判断保存一个update对象,用于MongoDB更改使用。
     */
    @Test
    public void test7() throws Exception {

        CarDO carDO = new CarDO();
        carDO.setId(454684464l);
        carDO.setName("这是小汽车名称");
        carDO.setDateOfManufacture(new Date());

        // 执行更改操作
        UpdateResult updateResult = mongoTemplate.updateFirst(
                new Query(),
                HandleMapToObject.updateNoNull(carDO),
                CarDO.class);
    }

方式二(推荐)

重点代码,这种方式是获取对象中的属性和方法。获取属性的原因是,我们的实体类里面一般会使用 @Data注解,当前注解除了生成getset方法以外还有其他方法,所以需要获取当前属性来明确当前对象的get方法。

public void getGetMethod(Object ob){
        Method[] methods = ob.getClass().getMethods();
        Field[] fields = ob.getClass().getDeclaredFields();

        Arrays.stream(fields).forEach(field -> {
            Arrays.stream(methods).forEach(method -> {
                if (("get" + field.getName()).toLowerCase().equals(method.getName().toLowerCase())){
                    try {
                        // 这里就可以获取到数据了。
                        System.out.println(method.invoke(ob));
                    }catch (Exception e){}
                }
            });
        });
    }
  • 完整代码
 /***
     * 将对象中非空的数据转为Update条件,用于MongoDB更改使用
     * @param obj
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update update(Object obj) {
        try {
            return update(obj,false);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null ;
    }

    /**
     * 将对象中非空的数据转为Update条件,用于MongoDB更改使用
     * @param t 有数据的那个对象
     * @param cla 当前对象的类型
     * @param considerThread true:考虑现成安全,false:不考虑
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update update(Object obj, Boolean considerThread) throws ClassNotFoundException {
        Class<?> clazz = null;
        if (considerThread) {
            //通过类装载器获取t类对象
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            clazz = loader.loadClass(obj.getClass().getName());
        } else {
            clazz = obj.getClass();
        }
        Update update = new Update();
        // 获取对象的所有属性
        Field[] fields = clazz.getDeclaredFields();
        // 获取对象所有方法
        Method[] methods = clazz.getMethods();

        // TODO: 2021/3/26 方式一,这种直接打开私有权限的,似乎不太安全。采用get方法获取,所以最终采用方法二。
//        for (Field field : fields) {
//             // 打开私有属性操作权限,这种方式似乎不是很安全。
//            field.setAccessible(true);
//            // 属性名
//            String fieldName = field.getName();
//
//            Object object = field.get(obj);
//            if (!ObjectUtils.isEmpty(object)){
//                update.set(fieldName,object);
//            }
//        }

        // TODO: 2021/3/26 方法二
        Arrays.stream(fields).forEach(field -> {
            // 属性名
            String fieldName = field.getName();
            // 这个属性不用管,是实现序列化时定义的属性。
            if ("serialVersionUID".equals(fieldName)) {
                return;
            }
            Arrays.stream(methods).forEach(method -> {
                if (("get" + fieldName).toLowerCase().equals(method.getName().toLowerCase())) {
                    try {
                        // 这里就可以获取到数据了。
                        Object invoke = method.invoke(obj);
                        if (!ObjectUtils.isEmpty(invoke)) {
                            update.set(fieldName, invoke);
                        }
                    } catch (IllegalAccessException e) {
                    } catch (InvocationTargetException e) {
                    }
                }
            });
        });
        return update;
    }

    /**
     * 将对象中中的数据转为Update条件,用于MongoDB更改使用。这个方法不排除空数据,即使是空数据多添加到条件中。
     * @param obj
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update updateOfNull(Object obj) throws Exception {
        return updateOfNull(obj,false);
    }

    /**
     * 将对象中中的数据转为Update条件,用于MongoDB更改使用。这个方法不排除空数据,即使是空数据多添加到条件中。
     * @param t 有数据的那个对象
     * @param cla 当前对象的类型
     * @param considerThread true:考虑现成安全,false:不考虑
     * @param <T>
     * @return
     * @throws Exception
     */
    public static<T> Update updateOfNull(Object obj, Boolean considerThread) throws Exception {
        Class<?> clazz = null;
        if (considerThread) {
            //通过类装载器获取t类对象
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            clazz = loader.loadClass(obj.getClass().getName());
        } else {
            clazz = obj.getClass();
        }
        Update update = new Update();
        // 获取对象的所有属性
        Field[] fields = clazz.getDeclaredFields();
        // 获取对象所有方法
        Method[] methods = clazz.getMethods();

        // TODO: 2021/3/26 方法二
        // TODO: 2021/3/26 方法二
        Arrays.stream(fields).forEach(field -> {
            // 属性名
            String fieldName = field.getName();
            Arrays.stream(methods).forEach(method -> {
                if (("get" + fieldName).toLowerCase().equals(method.getName().toLowerCase())) {
                    try {
                        // 这里就可以获取到数据了。
                        Object invoke = method.invoke(obj);
                        if (!ObjectUtils.isEmpty(invoke)) {
                            update.set(fieldName, invoke);
                        }
                    } catch (IllegalAccessException e) {
                    } catch (InvocationTargetException e) {
                    }
                }
            });
        });
        return update;
    }
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐