org.makersoft.activesql.builder.GenericStatementBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.makersoft.activesql.builder.GenericStatementBuilder.java

Source

/*
 * @(#)GenericStatementBuilder.java 20131223 ?23:33:33
 *
 * Copyright (c) 2011-2013 Makersoft.org all rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *
 */
package org.makersoft.activesql.builder;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.IfSqlNode;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SetSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.TextSqlNode;
import org.apache.ibatis.scripting.xmltags.TrimSqlNode;
import org.apache.ibatis.session.Configuration;
import org.makersoft.activesql.annotations.Column;
import org.makersoft.activesql.annotations.Delete;
import org.makersoft.activesql.annotations.Entity;
import org.makersoft.activesql.annotations.Id;
import org.makersoft.activesql.annotations.Insert;
import org.makersoft.activesql.annotations.Select;
import org.makersoft.activesql.annotations.Table;
import org.makersoft.activesql.annotations.Transient;
import org.makersoft.activesql.annotations.Update;
import org.makersoft.activesql.annotations.Version;
import org.makersoft.activesql.utils.AnnotationUtils;
import org.makersoft.activesql.utils.CaseFormatUtils;
import org.makersoft.activesql.utils.ReflectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;

/**
 * Class description goes here.
 * 
 */
public class GenericStatementBuilder extends BaseBuilder {

    private MapperBuilderAssistant assistant;

    private Class<?> entityClass;

    private String databaseId;
    private LanguageDriver lang;

    ////////~~~~~~~~~~~~~~~~~
    private String tableName;

    ////////~~~~~~~~~~~~~~~~~
    private Field idField;

    private Field versionField;

    private List<Field> columnFields = new ArrayList<Field>();

    //
    private Class<?> mapperType;

    private Entity entity;

    private String namespace;

    public GenericStatementBuilder(Configuration configuration, Class<?> entityClass) {
        super(configuration);
        this.entityClass = entityClass;

        String resource = entityClass.getName().replace('.', '/') + ".java (best guess)";
        assistant = new MapperBuilderAssistant(configuration, resource);

        entity = entityClass.getAnnotation(Entity.class);
        mapperType = entity.mapper();

        if (!mapperType.isAssignableFrom(Void.class)) {
            namespace = mapperType.getName();
        } else {
            namespace = entityClass.getName();
        }

        assistant.setCurrentNamespace(namespace);

        databaseId = super.getConfiguration().getDatabaseId();
        lang = super.getConfiguration().getDefaultScriptingLanuageInstance();

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Table table = entityClass.getAnnotation(Table.class);
        if (table == null) {
            tableName = CaseFormatUtils.camelToUnderScore(entityClass.getSimpleName());
        } else {
            tableName = table.name();
        }

        ///~~~~~~~~~~~~~~~~~~~~~~
        idField = AnnotationUtils.findDeclaredFieldWithAnnoation(Id.class, entityClass);

        versionField = AnnotationUtils.findDeclaredFieldWithAnnoation(Version.class, entityClass);

        ReflectionUtils.doWithFields(entityClass, new FieldCallback() {

            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                columnFields.add(field);
            }
        }, new FieldFilter() {

            @Override
            public boolean matches(Field field) {
                if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                    return false;
                }

                for (Annotation annotation : field.getAnnotations()) {
                    if (Transient.class.isAssignableFrom(annotation.getClass())
                            || Id.class.isAssignableFrom(annotation.getClass())) {
                        return false;
                    }
                }

                return true;
            }
        });
    }

    private String getColumnNameByField(Field field) {
        Column column = field.getAnnotation(Column.class);
        if (column == null) {
            return CaseFormatUtils.camelToUnderScore(field.getName());
        } else {
            return StringUtils.isNotBlank(column.name()) ? column.name()
                    : CaseFormatUtils.camelToUnderScore(field.getName());
        }
    }

    private String getTestByField(Field field) {
        Column column = field.getAnnotation(Column.class);
        if (column != null && StringUtils.isNotBlank(column.test())) {
            return column.test();
        } else {
            return field.getName() + "!= null";
        }

    }

    private String getIdColumnName() {
        Id id = idField.getAnnotation(Id.class);
        return StringUtils.isNotBlank(id.column()) ? id.column()
                : CaseFormatUtils.camelToUnderScore(idField.getName());
    }

    private String getIdFieldName() {
        return idField.getName();
    }

    private String getVersionSQL() {
        if (versionField != null) {
            return " AND " + getColumnNameByField(versionField) + " = #{" + versionField.getName() + "}";
        }

        return StringUtils.EMPTY;
    }

    public void build() {
        String insertStatementId = "insert";
        String deleteStatementId = "delete";
        String updateStatementId = "update";
        String selectStatementId = "get";

        if (!mapperType.isAssignableFrom(Void.class)) {
            List<Method> insertMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Insert.class);
            if (insertMethods != null && insertMethods.size() > 0) {
                if (insertMethods.size() > 1) {
                    throw new RuntimeException("@Insert");
                }
                insertStatementId = insertMethods.get(0).getName();
            }

            List<Method> deleteMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Delete.class);
            if (deleteMethods != null && deleteMethods.size() > 0) {
                if (deleteMethods.size() > 1) {
                    throw new RuntimeException("@Delete");
                }
                deleteStatementId = deleteMethods.get(0).getName();
            }

            List<Method> updateMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Update.class);
            if (updateMethods != null) {
                if (updateMethods.size() > 1 && updateMethods.size() > 0) {
                    throw new RuntimeException("@Update");
                }
                updateStatementId = updateMethods.get(0).getName();
            }

            List<Method> selectMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Select.class);
            if (selectMethods != null && selectMethods.size() > 0) {
                if (selectMethods.size() > 1) {
                    throw new RuntimeException("@Select");
                }
                selectStatementId = selectMethods.get(0).getName();
            }

        }

        if (!super.getConfiguration().hasStatement(namespace + "." + insertStatementId)) {
            buildInsert(insertStatementId);
        }

        if (!super.getConfiguration().hasStatement(namespace + "." + deleteStatementId)) {
            buildDelete(deleteStatementId);
        }

        if (!super.getConfiguration().hasStatement(namespace + "." + updateStatementId)) {
            buildUpdate(updateStatementId);
        }

        if (!super.getConfiguration().hasStatement(namespace + "." + selectStatementId)) {
            buildSelect(selectStatementId);
        }

    }

    private void buildInsert(String statementId) {
        //
        Integer fetchSize = null;
        Integer timeout = null;
        Class<?> parameterType = entityClass;

        ///~~~~~~~~~~
        boolean flushCache = true;
        boolean useCache = false;
        boolean resultOrdered = false;
        KeyGenerator keyGenerator = null;
        String keyProperty = null;
        String keyColumn = null;

        Id id = AnnotationUtils.findDeclaredAnnotation(Id.class, entityClass);
        if (id != null) {
            String keyStatementId = entityClass.getName() + ".insert" + SelectKeyGenerator.SELECT_KEY_SUFFIX;
            if (configuration.hasKeyGenerator(keyStatementId)) {
                keyGenerator = configuration.getKeyGenerator(keyStatementId);
            } else {
                keyGenerator = id.generatedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
            }

            keyProperty = idField.getName();
            keyColumn = StringUtils.isBlank(id.column()) ? CaseFormatUtils.camelToUnderScore(idField.getName())
                    : id.column();
        }

        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(this.getInsertSql());
        SqlSource sqlSource = new DynamicSqlSource(configuration, new MixedSqlNode(contents));

        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.INSERT,
                fetchSize, timeout, null, parameterType, null, null, ResultSetType.FORWARD_ONLY, flushCache,
                useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, lang);
    }

    private SqlNode getInsertSql() {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(new TextSqlNode("INSERT INTO " + tableName + " "));
        contents.add(getInsertColumns());
        contents.add(getInsertFileds());
        return new MixedSqlNode(contents);
    }

    private SqlNode getInsertFileds() {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        for (Field field : columnFields) {
            List<SqlNode> sqlNodes = new ArrayList<SqlNode>();

            if (Date.class.isAssignableFrom(field.getType()) && field.getAnnotation(Column.class) != null
                    && field.getAnnotation(Column.class).sysdate() == true) {
                sqlNodes.add(new TextSqlNode("now(),"));
            } else {
                sqlNodes.add(new TextSqlNode("#{" + field.getName() + "},"));
            }

            contents.add(new IfSqlNode(new MixedSqlNode(sqlNodes), getTestByField(field)));
        }

        return new TrimSqlNode(configuration, new MixedSqlNode(contents), " VALUES (", null, ")", ",");
    }

    private TrimSqlNode getInsertColumns() {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        for (Field field : columnFields) {
            List<SqlNode> sqlNodes = new ArrayList<SqlNode>();
            sqlNodes.add(new TextSqlNode(getColumnNameByField(field) + ","));

            contents.add(new IfSqlNode(new MixedSqlNode(sqlNodes), getTestByField(field)));
        }

        return new TrimSqlNode(configuration, new MixedSqlNode(contents), "(", null, ")", ",");
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //delete
    private void buildDelete(String statementId) {
        Integer timeout = null;
        Class<?> parameterType = entityClass;

        //~~~~~~~~~~~~~~~~~~~~~~~
        boolean flushCache = true;
        boolean useCache = false;
        boolean resultOrdered = false;
        KeyGenerator keyGenerator = new NoKeyGenerator();

        SqlNode sqlNode = new TextSqlNode("DELETE FROM " + tableName + " WHERE " + getIdColumnName() + " = #{"
                + getIdFieldName() + "} " + getVersionSQL());

        SqlSource sqlSource = new DynamicSqlSource(configuration, sqlNode);

        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.DELETE, null,
                timeout, null, parameterType, null, null, null, flushCache, useCache, resultOrdered, keyGenerator,
                null, null, databaseId, lang);
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //update
    private void buildUpdate(String statementId) {
        Integer timeout = null;
        Class<?> parameterType = entityClass;

        //~~~~~~~~~~~~~
        boolean flushCache = true;
        boolean useCache = false;
        boolean resultOrdered = false;
        KeyGenerator keyGenerator = new NoKeyGenerator();

        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(this.getUpdateSql());

        SqlSource sqlSource = new DynamicSqlSource(configuration, new MixedSqlNode(contents));

        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.UPDATE, null,
                timeout, null, parameterType, null, null, null, flushCache, useCache, resultOrdered, keyGenerator,
                null, null, databaseId, lang);
    }

    private SqlNode getUpdateSql() {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(new TextSqlNode("UPDATE " + tableName + " "));
        contents.add(getUpdateColumns());

        contents.add(new TextSqlNode(
                " WHERE " + getIdColumnName() + " = #{" + getIdFieldName() + "}" + getVersionSQL()));
        return new MixedSqlNode(contents);
    }

    private SqlNode getUpdateColumns() {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        for (Field field : columnFields) {
            List<SqlNode> sqlNodes = new ArrayList<SqlNode>();

            if (Date.class.isAssignableFrom(field.getType()) && field.getAnnotation(Column.class) != null
                    && field.getAnnotation(Column.class).sysdate() == true) {
                sqlNodes.add(new TextSqlNode(getColumnNameByField(field) + " = now(),"));
            } else {
                sqlNodes.add(new TextSqlNode(getColumnNameByField(field) + " = #{" + field.getName() + "},"));
            }

            contents.add(new IfSqlNode(new MixedSqlNode(sqlNodes), getTestByField(field)));
        }

        return new SetSqlNode(configuration, new MixedSqlNode(contents));
    }

    //~~~~~~~~~~~~~~~~~
    //get
    private void buildSelect(String statementId) {
        Integer fetchSize = null;
        Integer timeout = entity.timeout() == -1 ? null : entity.timeout();
        Class<?> resultType = entityClass;

        //~~~~~~~~~~~~~~~~~
        boolean flushCache = entity.flushCache();
        boolean useCache = entity.useCache();
        boolean resultOrdered = false;
        KeyGenerator keyGenerator = new NoKeyGenerator();

        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(this.getGetSql());

        SqlSource sqlSource = new DynamicSqlSource(configuration, new MixedSqlNode(contents));

        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.SELECT,
                fetchSize, timeout, null, null, null, resultType, null, flushCache, useCache, resultOrdered,
                keyGenerator, null, null, databaseId, lang);
    }

    private SqlNode getGetSql() {
        String sql = "SELECT " + getIdColumnName() + " AS " + getIdFieldName();

        for (Field field : columnFields) {
            sql += "," + getColumnNameByField(field) + " AS " + field.getName();
        }

        sql += " FROM " + tableName + " WHERE " + getIdColumnName() + " = #{" + getIdFieldName() + "}";

        return new TextSqlNode(sql);
    }

}