All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.nomou.mybatis.builder.AnnotatedMapperSupport Maven / Gradle / Ivy

There is a newer version: 1.0.0-BETA-20210819
Show newest version
package com.github.nomou.mybatis.builder;

import com.github.nomou.mybatis.util.Collections2;
import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.CacheNamespaceRef;
import org.apache.ibatis.annotations.Case;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Property;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.TypeDiscriminator;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.mapping.Discriminator;
import org.apache.ibatis.mapping.FetchType;
import org.apache.ibatis.mapping.ResultFlag;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.UnknownTypeHandler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * TODO DOC ME!.
 *
 * @author changhe.yang
 * @since 20191031
 */
public abstract class AnnotatedMapperSupport {
    protected final MapperBuilderAssistant assistant;

    /**
     * @param assistant
     */
    protected AnnotatedMapperSupport(final MapperBuilderAssistant assistant) {
        this.assistant = assistant;
    }

    /**
     * 配置给定的annotation缓存命名空间.
     *
     * @param cacheNs 缓存命名空间配置
     * @see org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseCache()
     */
    protected void useCache(final CacheNamespace cacheNs) {
        if (null != cacheNs) {
            final Integer size = 0 != cacheNs.size() ? cacheNs.size() : null;
            final Long flushInterval = 0 != cacheNs.flushInterval() ? cacheNs.flushInterval() : null;
            final Properties props = toProperties(cacheNs.properties(), assistant.getConfiguration());
            assistant.useNewCache(cacheNs.implementation(), cacheNs.eviction(), flushInterval, size, cacheNs.readWrite(), cacheNs.blocking(), props);
        }
    }

    /**
     * 配置给定的annotation缓存命名空间引用.
     *
     * @param cacheNsRef 缓存命名空间引用配置
     * @see org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseCacheRef()
     */
    protected void useCacheRef(final CacheNamespaceRef cacheNsRef) {
        if (null != cacheNsRef) {
            final String refName = cacheNsRef.name();
            final Class refType = cacheNsRef.value();
            if (void.class.equals(refType) && refName.isEmpty()) {
                throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
            }
            if (!void.class.equals(refType) && !refName.isEmpty()) {
                throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
            }
            final String namespace = !void.class.equals(refType) ? refType.getName() : refName;
            assistant.useCacheRef(namespace);
        }
    }

    /**
     * 转换给定{@link Property} 转换为 properties.
     *
     * @param props         属性配置
     * @param configuration 配置对象
     * @return properties
     * @see org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#convertToProperties(Property[])
     */
    protected Properties toProperties(final Property[] props, final Configuration configuration) {
        if (0 < props.length) {
            final Properties propsObj = new Properties();
            for (final Property prop : props) {
                propsObj.setProperty(prop.name(), PropertyParser.parse(prop.value(), configuration.getVariables()));
            }
            return propsObj;
        } else {
            return null;
        }
    }

    protected String createResultMap(final String resultMapId, final Class returnType,
                                     final Results results, final ConstructorArgs args, final TypeDiscriminator discriminator) {
        this.doCreateResultMap(
                resultMapId, returnType,
                null != args ? args.value() : new Arg[0],
                null != results ? results.value() : new Result[0],
                discriminator
        );
        return resultMapId;
    }

    private void doCreateResultMap(final String resultMapId, final Class returnType,
                                   final Arg[] args, final Result[] results, final TypeDiscriminator typeDiscriminator) {
        final List mappings = new ArrayList();
        Collections.addAll(mappings, createMappings(args, returnType));
        Collections.addAll(mappings, createMappings(results, returnType));

        final Discriminator discriminator = createDiscriminator(resultMapId, typeDiscriminator, returnType);
        assistant.addResultMap(resultMapId, returnType, null, discriminator, mappings, null);
        if (null != discriminator) {
            for (final Case c : typeDiscriminator.cases()) {
                final String caseResultMapId = resultMapId + "-" + c.value();
                final List innerMappings = new ArrayList();
                Collections.addAll(innerMappings, createMappings(args, returnType));
                Collections.addAll(innerMappings, createMappings(results, returnType));

                assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, innerMappings, null);
            }
        }
    }

    private ResultMapping[] createMappings(final Arg[] args, final Class resultType) {
        final ResultMapping[] mappings = new ResultMapping[args.length];
        for (int i = 0; i < args.length; i++) {
            mappings[i] = createMapping(args[i], resultType);
        }
        return mappings;
    }

    private ResultMapping[] createMappings(final Result[] results, final Class resultType) {
        final ResultMapping[] mappings = new ResultMapping[results.length];
        for (int i = 0; i < results.length; i++) {
            mappings[i] = createMapping(results[i], resultType);
        }
        return mappings;
    }

    private Discriminator createDiscriminator(final String resultMapId, final TypeDiscriminator discriminator, final Class resultType) {
        if (null != discriminator) {
            final String column = discriminator.column();
            final Class javaType = determineJavaType(discriminator.javaType(), String.class);
            final JdbcType jdbcType = determineJdbcType(discriminator.jdbcType(), null);
            final Class> typeHandler = determineTypeHandler(discriminator.typeHandler(), null);

            final Map discriminatorMap = Collections2.newHashMap(16);
            for (final Case c : discriminator.cases()) {
                final String caseResultMapId = resultMapId + "-" + c.value();
                discriminatorMap.put(c.value(), caseResultMapId);
            }
            return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private ResultMapping createMapping(final Arg arg, final Class resultType) {
        final List flags = arg.id() ? Arrays.asList(ResultFlag.CONSTRUCTOR, ResultFlag.ID) : Collections.singletonList(ResultFlag.CONSTRUCTOR);
        final Class> typeHandler = determineTypeHandler(arg.typeHandler(), null);
        final Class javaType = determineJavaType(arg.javaType(), null);
        final JdbcType jdbcType = !JdbcType.UNDEFINED.equals(arg.jdbcType()) ? arg.jdbcType() : null;
        final String property = nullOrEmpty(arg.name());
        final String column = nullOrEmpty(arg.column());
        final String nestedSelect = nullOrEmpty(arg.select());
        final String nestedResultMapId = nullOrEmpty(arg.resultMap());

        return assistant.buildResultMapping(
                resultType, property, column, javaType, jdbcType,
                nestedSelect, nestedResultMapId, null, null,
                typeHandler, flags, null, null, false
        );
    }

    private ResultMapping createMapping(final Result result, final Class resultType) {
        final List flags = result.id() ? Collections.singletonList(ResultFlag.ID) : Collections.emptyList();
        final Class> typeHandler = determineTypeHandler(result.typeHandler(), null);
        final Class javaType = determineJavaType(result.javaType(), null);
        final JdbcType jdbcType = determineJdbcType(result.jdbcType(), null);
        final String property = nullOrEmpty(result.property());
        final String column = nullOrEmpty(result.column());
        final String nestedSelect = hasNestedSelect(result) ? nestedSelectId(result) : null;
        final String nestedResultMapId = null;
        final boolean isLazy = isLazy(result);

        return assistant.buildResultMapping(
                resultType, property, column, javaType, jdbcType,
                nestedSelect, nestedResultMapId, null, null,
                typeHandler, flags, null, null, isLazy
        );
    }

    private boolean hasNestedSelect(final Result result) {
        final One one = result.one();
        final Many many = result.many();
        if (0 < one.select().length() && 0 < many.select().length()) {
            throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
        }
        return 0 < one.select().length() || 0 < many.select().length();
    }

    private String nestedSelectId(final Result result) {
        String nestedSelect = result.one().select();
        if (1 > nestedSelect.length()) {
            nestedSelect = result.many().select();
        }
        if (!nestedSelect.contains(".")) {
            final String namespace = assistant.getCurrentNamespace();
            if (null == namespace || namespace.isEmpty()) {
                throw new IllegalStateException("assistant namespace is not set");
            }
            nestedSelect = namespace + "." + nestedSelect;
        }
        return nestedSelect;
    }

    /**
     * 给定Result是否是延迟加载.
     *
     * @param result 配置的Result
     * @return 是否延迟加载
     * @see org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#isLazy(Result)
     */
    private boolean isLazy(final Result result) {
        final One one = result.one();
        final Many many = result.many();
        boolean isLazy = assistant.getConfiguration().isLazyLoadingEnabled();
        if (0 < one.select().length() && !FetchType.DEFAULT.equals(one.fetchType())) {
            isLazy = FetchType.LAZY.equals(one.fetchType());
        } else if (0 < many.select().length() && !FetchType.DEFAULT.equals(many.fetchType())) {
            isLazy = FetchType.LAZY.equals(many.fetchType());
        }
        return isLazy;
    }

    private String nullOrEmpty(final String value) {
        return null == value || value.trim().isEmpty() ? null : value;
    }

    private JdbcType determineJdbcType(final JdbcType jdbcType, final JdbcType def) {
        return !JdbcType.UNDEFINED.equals(jdbcType) ? jdbcType : def;
    }

    private Class determineJavaType(final Class javaType, final Class def) {
        return !void.class.equals(javaType) ? javaType : def;
    }

    @SuppressWarnings("unchecked")
    private Class> determineTypeHandler(final Class typeHandler,
                                                                 final Class defaultHandler) {
        return (Class>) (!UnknownTypeHandler.class.equals(typeHandler) ? typeHandler : defaultHandler);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy