前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >0基础学习Mybatis系列数据库操作框架——查询结果过滤器

0基础学习Mybatis系列数据库操作框架——查询结果过滤器

作者头像
方亮
发布2024-05-24 19:27:47
600
发布2024-05-24 19:27:47
举报
文章被收录于专栏:方亮方亮
大纲

  • 代码
    • Java模型类
    • 对象工厂
      • 处理null值问题
      • 过滤
      • 完整代码
  • 配置
  • 测试
  • 参考资料

《0基础学习Mybatis系列数据库操作框架——自定义拦截器》中,我们在Mybatis向数据库发起请求前,拦截了Delete操作。而如果有些数据不希望业务代码查询到,则可以使用本文介绍的“查询结果过滤器”。 Mybatis并没有设计这样的组件,但是我们可以通过自定义对象工厂来解决这个问题。 我们将基于《0基础学习Mybatis系列数据库操作框架——最小Demo》来设计本案例。

代码

实现自定义工厂只需要继承org.apache.ibatis.reflection.factory.DefaultObjectFactory,并覆盖下面两个方法

代码语言:javascript
复制
public <T> T create(Class<T> type);
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

我们着重关注第二个方法,因为它在constructorArgs中传递了构造参数的值。 但是要使得第二个方法生效,即要让constructorArgs有值,我们需要对Java模型类做个修改。

Java模型类

我们删除了默认构造函数,显式声明了带参数的构造函数。这样第二个方法被调用时,Mybatis会用数据库返回的数据填充constructorArgs,进而让我们有拦截返回结果的可能。

代码语言:javascript
复制
public class AllType {
//    public  AllType() {
//    }

    public AllType(int info_int, byte info_tint, short info_sint) {
        this.info_int = info_int;
        this.info_tint = info_tint;
        this.info_sint = info_sint;
    }    
……
	private int info_int;

    private byte info_tint;

    private short info_sint;
}

这样的修改也会带来一个副作用:如果数据库中info_int、info_tint、info_sint中任意一个字段为null,将会导致Mybatis反射出现错误。出现诸如下面的错误:

org.apache.ibatis.reflection.ReflectionException: Error instantiating class org.example.model.AllType with invalid types (int,byte,short) or values (103,null,null). Cause: java.lang.IllegalArgumentException: java.lang.NullPointerException: Cannot invoke “java.lang.Number.shortValue()” because the return value of “sun.invoke.util.ValueConversions.primitiveConversion(sun.invoke.util.Wrapper, Object, boolean)” is null

我们会在对象工程类里来处理这个问题。

对象工厂

首先我们做一个基本判断,constructorArgTypes要和constructorArgs长度一致,即构造函数的类型列表长度和构造函数的值列表长度相同。

代码语言:javascript
复制
 @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        if (type.equals(AllType.class) && constructorArgTypes != null && constructorArgs != null) {
            if (constructorArgs.size() == constructorArgTypes.size()) {
处理null值问题

我们会遍历每个参数,然后对于为null的值设置一个默认值。这样就解决了我们关闭了默认构造函数,而表中含有null值,导致Mybatis反射失败的问题。

代码语言:javascript
复制
                for (int i = 0; i < constructorArgs.size(); i++) {
                    Object a = constructorArgs.get(i);
                    if (a == null) {
                        String className = constructorArgTypes.get(i).getName();
                        if (className.equals("int")) {
                            int vi = 0;
                            constructorArgs.set(i, vi);
                        } else if (className.equals("long")) {
                            long vl = 0;
                            constructorArgs.set(i, vl);
                        } else if (className.equals("float")) {
                            float vf = 0;
                            constructorArgs.set(i, vf);
                        } else if (className.equals("double")) {
                            double vd = 0;
                            constructorArgs.set(i, vd);
                        } else if (className.equals("boolean")) {
                            boolean vb = false;
                            constructorArgs.set(i, vb);
                        } else if (className.equals("char")) {
                            char vc = 0;
                            constructorArgs.set(i, vc);
                        } else if (className.equals("byte")) {
                            byte vb = 0;
                            constructorArgs.set(i, vb);
                        } else if (className.equals("short")) {
                            short sb = 0;
                            constructorArgs.set(i, sb);
                        } else {
                            try {
                                Constructor<?> constructor = Class.forName(className).getConstructor();
                                constructorArgs.set(i, constructor.newInstance());
                            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    }
过滤
代码语言:javascript
复制
                    if (i == 0) {
                        int intInfo = (int)constructorArgs.get(i);
                        if (intInfo > this.maxIntInfo) {
                            return null;
                        }
                    }
                }
            }
        }
        return super.create(type, constructorArgTypes, constructorArgs);
    }

我们的过滤条件是:如果表中info_int大于我们定义的最大值,则返回null;否则创建这个对象。 this.maxIntInfo的值来源于配置,读取这个配置的代码如下

代码语言:javascript
复制
    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
        this.maxIntInfo = Integer.parseInt(this.properties.getProperty("max_int_info", "1"));
    }
完整代码
代码语言:javascript
复制
package org.example.factory;

import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.example.model.AllType;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Properties;

public class ResultFactory extends DefaultObjectFactory {
    @Override
    public <T> T create(Class<T> type) {
        return super.create(type);
    }

    @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        if (type.equals(AllType.class) && constructorArgTypes != null && constructorArgs != null) {
            if (constructorArgs.size() == constructorArgTypes.size()) {
                for (int i = 0; i < constructorArgs.size(); i++) {
                    Object a = constructorArgs.get(i);
                    if (a == null) {
                        String className = constructorArgTypes.get(i).getName();
                        if (className.equals("int")) {
                            int vi = 0;
                            constructorArgs.set(i, vi);
                        } else if (className.equals("long")) {
                            long vl = 0;
                            constructorArgs.set(i, vl);
                        } else if (className.equals("float")) {
                            float vf = 0;
                            constructorArgs.set(i, vf);
                        } else if (className.equals("double")) {
                            double vd = 0;
                            constructorArgs.set(i, vd);
                        } else if (className.equals("boolean")) {
                            boolean vb = false;
                            constructorArgs.set(i, vb);
                        } else if (className.equals("char")) {
                            char vc = 0;
                            constructorArgs.set(i, vc);
                        } else if (className.equals("byte")) {
                            byte vb = 0;
                            constructorArgs.set(i, vb);
                        } else if (className.equals("short")) {
                            short sb = 0;
                            constructorArgs.set(i, sb);
                        } else {
                            try {
                                Constructor<?> constructor = Class.forName(className).getConstructor();
                                constructorArgs.set(i, constructor.newInstance());
                            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    if (i == 0) {
                        int intInfo = (int)constructorArgs.get(i);
                        if (intInfo > this.maxIntInfo) {
                            return null;
                        }
                    }
                }
            }
        }
        return super.create(type, constructorArgTypes, constructorArgs);
    }

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
        this.maxIntInfo = Integer.parseInt(this.properties.getProperty("max_int_info", "1"));
    }

    @Override
    public <T> boolean isCollection(Class<T> type) {
        return Collection.class.isAssignableFrom(type);
    }

    private Properties properties;
    private int maxIntInfo = 1;
}

配置

我们只需要在mybatis-config.xml中加入如下配置即可

代码语言:javascript
复制
    <objectFactory type="org.example.factory.ResultFactory">
        <property name="max_int_info" value="102"/>
    </objectFactory>

上面setProperties方法的参数properties就是从这个XML中解析的。

测试

相较于之前的测试代码,我们需要做个改动:遍历List<AllType>后要判断元素是否为null。因为我们只是没有构造需要过滤的对象,但是返回了null。所以数组中数据个数并没有剔除需要过滤的对象——它们只是被null替换掉了。

代码语言:javascript
复制
package org.example;

import org.example.model.AllType;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.example.mapper.AllTypeMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class ResultFactoryTest {
    private static SqlSessionFactory sqlSF;

    @BeforeAll
    static void CreateSessionFactory() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis/config/mybatis-config-result-factory.xml");
        sqlSF = new SqlSessionFactoryBuilder().build(in);
    }

    @Test
    void testFindAll() {
        List<AllType> all = null;
        try (SqlSession s = sqlSF.openSession()) {
            all = s.selectList("org.example.mapper.AllTypeMapper.findAll");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        for (AllType a : Objects.requireNonNull(all)) {
            if (a == null) {
                continue;
            }
            System.out.println(a.getInfo_int());
        }
    }

    @Test
    void testFind() {
        try (SqlSession s = sqlSF.openSession()) {
            AllTypeMapper all_type_mapper = s.getMapper(AllTypeMapper.class);
            List<AllType> all = all_type_mapper.find(103);
            for (AllType a : Objects.requireNonNull(all)) {
                if (a == null) {
                    continue;
                }
                System.out.println(a.getInfo_int());
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

参考资料

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 大纲
  • 代码
    • Java模型类
      • 对象工厂
        • 处理null值问题
        • 过滤
        • 完整代码
    • 配置
    • 测试
    • 参考资料
    相关产品与服务
    数据库
    云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档