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

Java
45
0
0
2024-11-13
标签   MyBatis

代码

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

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,进而让我们有拦截返回结果的可能。

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长度一致,即构造函数的类型列表长度和构造函数的值列表长度相同。

 @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反射失败的问题。

                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);
    }

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

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
        this.maxIntInfo = Integer.parseInt(this.properties.getProperty("max_int_info", "1"));
    }
完整代码
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中加入如下配置即可

    <objectFactory type="org.example.factory.ResultFactory">
        <property name="max_int_info" value="102"/>
    </objectFactory>

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

测试

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

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());
        }
    }
}

参考资料