com.sxj.mybatis.orm.builder.GenericStatementBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.sxj.mybatis.orm.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 com.sxj.mybatis.orm.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.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.MapperBuilderAssistant;
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.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ResultMap;
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.ForEachSqlNode;
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.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;

import com.sxj.mybatis.orm.ConfigurationProperties;
import com.sxj.mybatis.orm.annotations.BatchDelete;
import com.sxj.mybatis.orm.annotations.BatchInsert;
import com.sxj.mybatis.orm.annotations.BatchUpdate;
import com.sxj.mybatis.orm.annotations.Column;
import com.sxj.mybatis.orm.annotations.Delete;
import com.sxj.mybatis.orm.annotations.Entity;
import com.sxj.mybatis.orm.annotations.GeneratedValue;
import com.sxj.mybatis.orm.annotations.GenerationType;
import com.sxj.mybatis.orm.annotations.Get;
import com.sxj.mybatis.orm.annotations.Id;
import com.sxj.mybatis.orm.annotations.Insert;
import com.sxj.mybatis.orm.annotations.MultiGet;
import com.sxj.mybatis.orm.annotations.Sn;
import com.sxj.mybatis.orm.annotations.Table;
import com.sxj.mybatis.orm.annotations.Transient;
import com.sxj.mybatis.orm.annotations.Update;
import com.sxj.mybatis.orm.annotations.Version;
import com.sxj.mybatis.orm.keygen.Jdbc4KeyGenerator;
import com.sxj.mybatis.orm.keygen.ShardJdbc4KeyGenerator;
import com.sxj.mybatis.orm.keygen.ShardKeyGenerator;
import com.sxj.mybatis.orm.keygen.ShardSnGenerator;
import com.sxj.mybatis.orm.keygen.ShardUuidKeyGenerator;
import com.sxj.mybatis.orm.keygen.SnGenerator;
import com.sxj.mybatis.orm.keygen.UuidKeyGenerator;
import com.sxj.spring.modules.util.AnnotationUtils;
import com.sxj.spring.modules.util.CaseFormatUtils;
import com.sxj.spring.modules.util.Collections3;
import com.sxj.spring.modules.util.ReflectUtils;

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

    private MapperBuilderAssistant assistant;

    private static Map<String, ShardKeyGenerator> shardedKeyGenerators = new HashMap<String, ShardKeyGenerator>();

    private static Map<String, ShardSnGenerator> shardSnGenerators = new HashMap<String, ShardSnGenerator>();

    private static Map<String, SnGenerator> snGenerators = new HashMap<String, SnGenerator>();

    private boolean containSn = false;

    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;

    private static final String ITEM = "item";

    private boolean sharded = false;

    public GenericStatementBuilder(Configuration configuration, final Class<?> entityClass) {
        super(configuration);
        this.entityClass = entityClass;
        sharded = ConfigurationProperties.isSharded(configuration);
        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);
        Collection<String> cacheNames = configuration.getCacheNames();
        for (String name : cacheNames)
            if (namespace.equals(name)) {
                assistant.useCacheRef(name);
                break;
            }

        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);
        if (!sharded && (this.idField.isAnnotationPresent(GeneratedValue.class))
                && (((GeneratedValue) this.idField.getAnnotation(GeneratedValue.class))
                        .strategy() == GenerationType.UUID))
            columnFields.add(idField);
        else
            columnFields.add(idField);
        versionField = AnnotationUtils.findDeclaredFieldWithAnnoation(Version.class, entityClass);

        ReflectionUtils.doWithFields(entityClass, new FieldCallback() {

            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                if (field.isAnnotationPresent(Column.class))
                    columnFields.add(field);
                if (field.isAnnotationPresent(Sn.class))
                    containSn = true;

            }
        }, new FieldFilter() {

            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) {
            Id idColumn = field.getAnnotation(Id.class);
            if (idColumn != null)
                return StringUtils.isNotBlank(idColumn.column()) ? idColumn.column()
                        : CaseFormatUtils.camelToUnderScore(field.getName());
            return CaseFormatUtils.camelToUnderScore(field.getName());
        } else {
            return StringUtils.isNotBlank(column.name()) ? column.name()
                    : CaseFormatUtils.camelToUnderScore(field.getName());
        }
    }

    private String getTestByField(String prefix, Field field) {
        Column column = field.getAnnotation(Column.class);
        if (column != null && StringUtils.isNotBlank(column.test())) {
            return column.test();
        } else {
            return (StringUtils.isEmpty(prefix) ? "" : prefix + ".") + 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";
        String batchInsertStatementId = "batchinsert";
        String batchDeleteStatementId = "batchDelete";
        String multiGetStatementId = "multiGet";

        if (!mapperType.isAssignableFrom(Void.class)) {
            List<Method> insertMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Insert.class);
            if (Collections3.isNotEmpty(insertMethods)) {
                if (insertMethods.size() > 1) {
                    throw new RuntimeException("@Insert");
                }
                insertStatementId = insertMethods.get(0).getName();
                if (!super.getConfiguration().hasStatement(namespace + "." + insertStatementId)) {
                    buildInsert(namespace + "." + insertStatementId);
                }
            }

            List<Method> deleteMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Delete.class);
            if (Collections3.isNotEmpty(deleteMethods)) {
                if (deleteMethods.size() > 1) {
                    throw new RuntimeException("@Delete");
                }
                deleteStatementId = deleteMethods.get(0).getName();
                if (!super.getConfiguration().hasStatement(namespace + "." + deleteStatementId)) {
                    buildDelete(namespace + "." + deleteStatementId);
                }
            }

            List<Method> updateMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Update.class);
            if (Collections3.isNotEmpty(updateMethods)) {
                if (updateMethods.size() > 1 && updateMethods.size() > 0) {
                    throw new RuntimeException("@Update");
                }
                updateStatementId = updateMethods.get(0).getName();
                if (!super.getConfiguration().hasStatement(namespace + "." + updateStatementId)) {
                    buildUpdate(namespace + "." + updateStatementId);
                }
            }

            List<Method> selectMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, Get.class);
            if (Collections3.isNotEmpty(selectMethods)) {
                if (selectMethods.size() > 1) {
                    throw new RuntimeException("@Select");
                }
                selectStatementId = selectMethods.get(0).getName();
                if (!super.getConfiguration().hasStatement(namespace + "." + selectStatementId)) {
                    buildSelect(namespace + "." + selectStatementId);
                }
            }

            List<Method> batchInsertMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, BatchInsert.class);
            if (Collections3.isNotEmpty(batchInsertMethods)) {
                if (batchInsertMethods.size() > 1) {
                    throw new RuntimeException("@BatchInsert");
                }
                batchInsertStatementId = batchInsertMethods.get(0).getName();
                if (!super.getConfiguration().hasCache(namespace + "." + batchInsertStatementId)) {
                    buildBatchInsert(namespace + "." + batchInsertStatementId,
                            getCollection(batchInsertMethods.get(0)));
                }
            }
            List<Method> batchDeleteMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, BatchDelete.class);
            if (Collections3.isNotEmpty(batchDeleteMethods)) {
                if (batchDeleteMethods.size() > 1) {
                    throw new RuntimeException("@BatchDelete");
                }
                batchDeleteStatementId = batchDeleteMethods.get(0).getName();
                if (!super.getConfiguration().hasCache(namespace + "." + batchDeleteStatementId)) {
                    buildBatchDelete(namespace + "." + batchDeleteStatementId,
                            getCollection(batchDeleteMethods.get(0)));
                }
            }
            List<Method> batchUpdateMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, BatchUpdate.class);
            if (Collections3.isNotEmpty(batchUpdateMethods)) {
                if (batchUpdateMethods.size() > 1) {
                    throw new RuntimeException("@BatchUpdate");
                }
                batchDeleteStatementId = batchUpdateMethods.get(0).getName();
                if (!super.getConfiguration().hasCache(namespace + "." + batchDeleteStatementId)) {
                    buildBatchUpdate(namespace + "." + batchDeleteStatementId,
                            getCollection(batchUpdateMethods.get(0)));
                }
            }
            List<Method> multiGetMethods = ReflectUtils.findMethodsAnnotatedWith(mapperType, MultiGet.class);
            if (Collections3.isNotEmpty(multiGetMethods)) {
                if (multiGetMethods.size() > 1) {
                    throw new RuntimeException("@MultiGet");
                }
                multiGetStatementId = multiGetMethods.get(0).getName();
                if (!super.getConfiguration().hasCache(namespace + "." + multiGetStatementId)) {
                    buildMultiGet(namespace + "." + multiGetStatementId, getCollection(multiGetMethods.get(0)));
                }
            }

        }

    }

    private String getCollection(Method method)

    {
        //        Method method = methods.get(0);
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (parameterTypes.length != 1)
            throw new RuntimeException("@Batch?");
        Class<?> parameterType = parameterTypes[0];
        if (parameterType.equals(List.class))
            return "list";
        else
            return "array";
    }

    private void buildMultiGet(String statementId, String collection) {
        Integer fetchSize = null;
        Integer timeout = null;
        Class<?> resultType = entityClass;
        //~~~~~~~~~~~~~~~~~~~~~~~
        boolean flushCache = true;
        boolean useCache = false;
        boolean resultOrdered = false;
        KeyGenerator keyGenerator = new NoKeyGenerator();

        SqlSource sqlSource = new DynamicSqlSource(configuration, getMultiGetSql(collection));

        String resultMap = null;
        Iterator<String> resultMapNames = configuration.getResultMapNames().iterator();
        while (resultMapNames.hasNext()) {
            String name = resultMapNames.next();
            ResultMap temp = configuration.getResultMap(name);
            if (temp.getType().equals(entityClass)) {
                resultMap = temp.getId();
                break;
            }
        }
        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.SELECT,
                fetchSize, timeout, null, idField.getType(), resultMap, resultType, null, flushCache, useCache,
                resultOrdered, keyGenerator, null, null, databaseId, lang);
    }

    private void buildBatchDelete(String statementId, String collection) {
        Integer timeout = null;
        Class<?> parameterType = idField.getType();

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

        SqlSource sqlSource = new DynamicSqlSource(configuration, getBatchDeleteSql(collection));

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

    private void buildBatchInsert(String statementId, String collection) {
        Integer fetchSize = null;
        Integer timeout = null;
        Class<?> parameterType = entityClass;

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

        Id id = AnnotationUtils.findDeclaredAnnotation(Id.class, entityClass);
        GeneratedValue generatedValue = AnnotationUtils.findDeclaredAnnotation(GeneratedValue.class, entityClass);
        if (id != null) {
            String keyStatementId = entityClass.getName() + ".insert" + SelectKeyGenerator.SELECT_KEY_SUFFIX;
            if (!sharded) {
                if (containSn)
                    snGenerators.put(statementId, new SnGenerator());

                if (configuration.hasKeyGenerator(keyStatementId)) {
                    keyGenerator = configuration.getKeyGenerator(keyStatementId);
                } else if (generatedValue != null) {
                    if (generatedValue.strategy() == GenerationType.UUID) {
                        keyGenerator = new UuidKeyGenerator(generatedValue.length());

                    }
                } else {
                    keyGenerator = id.generatedKeys() ? new Jdbc4KeyGenerator() : new NoKeyGenerator();
                }
            } else {
                if (containSn)
                    shardSnGenerators.put(statementId, new ShardSnGenerator());
                if (generatedValue != null) {
                    if (generatedValue.strategy() == GenerationType.UUID) {
                        shardedKeyGenerators.put(statementId, new ShardUuidKeyGenerator(generatedValue.length()));
                    } else if (generatedValue.strategy() == GenerationType.TABLE
                            || generatedValue.strategy() == GenerationType.AUTO) {
                        shardedKeyGenerators.put(statementId, new ShardJdbc4KeyGenerator());
                    }
                }
            }
            keyProperty = idField.getName();
            keyColumn = StringUtils.isBlank(id.column()) ? CaseFormatUtils.camelToUnderScore(idField.getName())
                    : id.column();
        }

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

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

    public void refresh(MappedStatement mappedStatement) {
        Integer timeout = null;
        Class<?> parameterType = entityClass;

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

        Id id = AnnotationUtils.findDeclaredAnnotation(Id.class, entityClass);
        GeneratedValue generatedValue = AnnotationUtils.findDeclaredAnnotation(GeneratedValue.class, entityClass);
        if (id != null) {
            String keyStatementId = entityClass.getName() + ".insert" + SelectKeyGenerator.SELECT_KEY_SUFFIX;
            if (!sharded) {

                if (containSn)
                    snGenerators.put(mappedStatement.getId(), new SnGenerator());
                if (configuration.hasKeyGenerator(keyStatementId)) {
                    keyGenerator = configuration.getKeyGenerator(keyStatementId);
                } else if (generatedValue != null) {
                    if (generatedValue.strategy() == GenerationType.UUID) {
                        keyGenerator = new UuidKeyGenerator(generatedValue.length());
                    }
                } else {
                    keyGenerator = id.generatedKeys() ? new Jdbc4KeyGenerator() : new NoKeyGenerator();
                }
            } else {
                if (containSn)
                    shardSnGenerators.put(mappedStatement.getId(), new ShardSnGenerator());
                if (generatedValue != null) {
                    if (generatedValue.strategy() == GenerationType.UUID) {
                        shardedKeyGenerators.put(mappedStatement.getId(),
                                new ShardUuidKeyGenerator(generatedValue.length()));
                    } else if (generatedValue.strategy() == GenerationType.TABLE
                            || generatedValue.strategy() == GenerationType.AUTO) {
                        shardedKeyGenerators.put(mappedStatement.getId(), new ShardJdbc4KeyGenerator());
                    }
                }
                //                shardedKeyGenerators.put(statementId, new shardeduu)
            }
            keyProperty = idField.getName();
            keyColumn = StringUtils.isBlank(id.column()) ? CaseFormatUtils.camelToUnderScore(idField.getName())
                    : id.column();
        }

        SqlSource sqlSource = mappedStatement.getSqlSource();
        String parameterMap = null;
        Iterator<String> parameterMapNames = configuration.getParameterMapNames().iterator();
        while (parameterMapNames.hasNext()) {
            String name = parameterMapNames.next();
            ParameterMap temp = configuration.getParameterMap(name);
            if (temp.getType().equals(entityClass)) {
                parameterMap = temp.getId();
                break;
            }
        }
        assistant.addMappedStatement(mappedStatement.getId(), sqlSource, StatementType.PREPARED,
                SqlCommandType.INSERT, null, timeout, parameterMap, parameterType, null, null, null, flushCache,
                useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, lang);
    }

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

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

        Id id = AnnotationUtils.findDeclaredAnnotation(Id.class, entityClass);
        GeneratedValue generatedValue = AnnotationUtils.findDeclaredAnnotation(GeneratedValue.class, entityClass);
        if (id != null) {
            String keyStatementId = entityClass.getName() + ".insert" + SelectKeyGenerator.SELECT_KEY_SUFFIX;
            if (!sharded) {

                if (containSn)
                    snGenerators.put(statementId, new SnGenerator());
                if (configuration.hasKeyGenerator(keyStatementId)) {
                    keyGenerator = configuration.getKeyGenerator(keyStatementId);
                } else if (generatedValue != null) {
                    if (generatedValue.strategy() == GenerationType.UUID) {
                        keyGenerator = new UuidKeyGenerator(generatedValue.length());

                    }
                } else {
                    keyGenerator = id.generatedKeys() ? new Jdbc4KeyGenerator() : new NoKeyGenerator();
                }
            } else {
                if (containSn)
                    shardSnGenerators.put(statementId, new ShardSnGenerator());
                if (generatedValue != null) {
                    if (generatedValue.strategy() == GenerationType.UUID) {
                        shardedKeyGenerators.put(statementId, new ShardUuidKeyGenerator(generatedValue.length()));
                    } else if (generatedValue.strategy() == GenerationType.TABLE
                            || generatedValue.strategy() == GenerationType.AUTO) {
                        shardedKeyGenerators.put(statementId, new ShardJdbc4KeyGenerator());
                    }
                }
                //                shardedKeyGenerators.put(statementId, new shardeduu)
            }
            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));
        String parameterMap = null;
        Iterator<String> parameterMapNames = configuration.getParameterMapNames().iterator();
        while (parameterMapNames.hasNext()) {
            String name = parameterMapNames.next();
            ParameterMap temp = configuration.getParameterMap(name);
            if (temp.getType().equals(entityClass)) {
                parameterMap = temp.getId();
                break;
            }
        }
        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.INSERT, null,
                timeout, parameterMap, parameterType, null, null, null, flushCache, useCache, resultOrdered,
                keyGenerator, keyProperty, keyColumn, databaseId, lang);
    }

    private SqlNode getBatchInsertSql(String collection) {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(new TextSqlNode("INSERT INTO " + tableName + " "));
        contents.add(getBatchInsertColumns());
        contents.add(getBatchInsertFields(collection));
        return new MixedSqlNode(contents);
    }

    private SqlNode getBatchDeleteSql(String collection) {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(new TextSqlNode("DELETE FROM " + tableName + " WHERE " + getIdColumnName() + " in "));
        contents.add(getBatchDeleteFields(collection));
        return new MixedSqlNode(contents);
    }

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

        for (Field field : columnFields) {
            if (!getColumnNameByField(field).equals(getIdColumnName()))
                sql += "," + getColumnNameByField(field) + " AS " + getColumnNameByField(field);
        }

        sql += " FROM " + tableName + " WHERE " + getIdColumnName() + " in";
        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(new TextSqlNode(sql));
        contents.add(getMultiGetFields(collection));
        return new MixedSqlNode(contents);
    }

    private SqlNode getMultiGetFields(String collection) {
        TextSqlNode fieldSqlNode = new TextSqlNode("#{" + ITEM + "}");
        ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, fieldSqlNode, collection, "index", ITEM,
                "(", ")", ",");
        return forEachSqlNode;
    }

    private SqlNode getBatchDeleteFields(String collection) {
        TextSqlNode fieldSqlNode = new TextSqlNode("#{" + ITEM + "}");
        ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, fieldSqlNode, collection, "index", ITEM,
                "(", ")", ",");
        return forEachSqlNode;
    }

    private SqlNode getBatchInsertFields(String collection) {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        for (Field field : columnFields) {
            List<SqlNode> sqlNodes = new ArrayList<SqlNode>();
            Column column = field.getAnnotation(Column.class);
            if (Date.class.isAssignableFrom(field.getType()) && column != null && column.sysdate() == true) {
                sqlNodes.add(new TextSqlNode("now(),"));
            } else {
                sqlNodes.add(new TextSqlNode("#{item." + field.getName() + "},"));
            }

            contents.add(new MixedSqlNode(sqlNodes));
        }
        TrimSqlNode fieldSqlNode = new TrimSqlNode(configuration, new MixedSqlNode(contents), " (", null, ")", ",");

        ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, fieldSqlNode, collection, "index", ITEM,
                "", "", ",");

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

    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>();
            Column column = field.getAnnotation(Column.class);
            if (Date.class.isAssignableFrom(field.getType()) && column != null && column.sysdate() == true) {
                sqlNodes.add(new TextSqlNode("now(),"));
            } else {
                sqlNodes.add(new TextSqlNode("#{" + field.getName() + "},"));
            }

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

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

    private TrimSqlNode getBatchInsertColumns() {
        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 MixedSqlNode(sqlNodes));
        }

        return new TrimSqlNode(configuration, new MixedSqlNode(contents), "(", 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(null, 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();
        List<SqlNode> contents = new ArrayList<SqlNode>();
        SqlNode sqlNode = new TextSqlNode(
                "DELETE FROM " + tableName + " WHERE " + getIdColumnName() + " = #{" + getIdFieldName() + "} ");
        contents.add(sqlNode);
        //        if (versionField != null)
        //            contents.add(new IfSqlNode(new TextSqlNode(getVersionSQL()),
        //                    getTestByField(null, versionField)));
        SqlSource sqlSource = new DynamicSqlSource(configuration, new MixedSqlNode(contents));

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

    private void buildBatchUpdate(String statementId, String collection) {
        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.getBatchUpdateSql(collection));

        SqlSource sqlSource = new DynamicSqlSource(configuration, new MixedSqlNode(contents));
        String parameterMap = null;
        Iterator<String> parameterMapNames = configuration.getParameterMapNames().iterator();
        while (parameterMapNames.hasNext()) {
            String name = parameterMapNames.next();
            ParameterMap temp = configuration.getParameterMap(name);
            if (temp.getType().equals(entityClass)) {
                parameterMap = temp.getId();
                break;
            }
        }
        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.UPDATE, null,
                timeout, parameterMap, 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));
        String parameterMap = null;
        Iterator<String> parameterMapNames = configuration.getParameterMapNames().iterator();
        while (parameterMapNames.hasNext()) {
            String name = parameterMapNames.next();
            ParameterMap temp = configuration.getParameterMap(name);
            if (temp.getType().equals(entityClass)) {
                parameterMap = temp.getId();
                break;
            }
        }
        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.UPDATE, null,
                timeout, parameterMap, 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() + "}"));
        if (versionField != null)
            contents.add(new IfSqlNode(new TextSqlNode(getVersionSQL()), getTestByField(null, versionField)));
        return new MixedSqlNode(contents);
    }

    private SqlNode getBatchUpdateSql(String collection) {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        contents.add(new TextSqlNode("UPDATE " + tableName + " "));
        contents.add(getBatchUpdateColumns());

        contents.add(
                new TextSqlNode(" WHERE " + getIdColumnName() + " = #{" + ITEM + "." + getIdFieldName() + "}"));
        if (versionField != null)
            contents.add(new IfSqlNode(new TextSqlNode(getVersionSQL()), getTestByField(null, versionField)));
        MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
        return new ForEachSqlNode(configuration, mixedSqlNode, collection, "index", ITEM, "", "", ";");
    }

    private SqlNode getBatchUpdateColumns() {
        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) + " = #{" + ITEM + "." + field.getName() + "},"));
            }

            contents.add(new IfSqlNode(new MixedSqlNode(sqlNodes), getTestByField(ITEM, field)));
        }
        if (versionField != null)
            contents.add(new TextSqlNode(
                    getColumnNameByField(versionField) + "=" + getColumnNameByField(versionField) + "+1"));

        return new SetSqlNode(configuration, 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 if (!field.isAnnotationPresent(Version.class)) {
                sqlNodes.add(new TextSqlNode(getColumnNameByField(field) + " = #{" + field.getName() + "},"));
            }

            contents.add(new IfSqlNode(new MixedSqlNode(sqlNodes), getTestByField(null, field)));
        }
        if (versionField != null) {
            contents.add(new TextSqlNode(
                    getColumnNameByField(versionField) + "=" + getColumnNameByField(versionField) + "+1"));
        }

        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));
        String resultMap = null;
        Iterator<String> resultMapNames = configuration.getResultMapNames().iterator();
        while (resultMapNames.hasNext()) {
            String name = resultMapNames.next();
            ResultMap temp = configuration.getResultMap(name);
            if (temp.getType().equals(entityClass)) {
                resultMap = temp.getId();
                break;
            }
        }
        assistant.addMappedStatement(statementId, sqlSource, StatementType.PREPARED, SqlCommandType.SELECT,
                fetchSize, timeout, null, idField.getType(), resultMap, resultType, null, flushCache, useCache,
                resultOrdered, keyGenerator, null, null, databaseId, lang);
    }

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

        for (Field field : columnFields) {
            if (!getColumnNameByField(field).equals(getIdColumnName()))
                sql += "," + getColumnNameByField(field) + " AS " + getColumnNameByField(field);
        }

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

        return new TextSqlNode(sql);
    }

    public static Map<String, ShardKeyGenerator> getShardedKeyGenerators() {
        return shardedKeyGenerators;
    }

    public static Map<String, SnGenerator> getSnGenerators() {
        return snGenerators;
    }

    public static Map<String, ShardSnGenerator> getShardSnGenerators() {
        return shardSnGenerators;
    }

}