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

com.centit.support.database.orm.OrmDaoUtils Maven / Gradle / Ivy

Go to download

数据库操作通用方法和函数,从以前的util包中分离出来,并且整合了部分sys-module中的函数

There is a newer version: 5.3.2302
Show newest version
package com.centit.support.database.orm;

import com.centit.support.algorithm.ListOpt;
import com.centit.support.algorithm.NumberBaseOpt;
import com.centit.support.algorithm.ReflectionOpt;
import com.centit.support.algorithm.StringBaseOpt;
import com.centit.support.database.jsonmaptable.GeneralJsonObjectDao;
import com.centit.support.database.jsonmaptable.JsonObjectDao;
import com.centit.support.database.metadata.SimpleTableReference;
import com.centit.support.database.metadata.TableInfo;
import com.centit.support.database.utils.*;
import com.centit.support.json.JSONOpt;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * Created by codefan on 17-8-29.
 */
@SuppressWarnings("unused")
public abstract class OrmDaoUtils {
    private OrmDaoUtils() {
        throw new IllegalAccessError("Utility class");
    }

    private static final Logger logger = LoggerFactory.getLogger(OrmDaoUtils.class);

    /**
     * MySql使用存储过程来模拟序列的
     * 获取 Sequence 的值
     * @param connection 数据库连接
     * @param sequenceName 序列名称
     * @return 序列值
     */
    public static Long getSequenceNextValue(Connection connection, final String sequenceName) {
        try {
            return GeneralJsonObjectDao.createJsonObjectDao(connection)
                    .getSequenceNextValue(sequenceName);
        } catch (SQLException| IOException e) {
            throw  new PersistenceException(e);
        }
    }

    public static JsonObjectDao getJsonObjectDao(Connection connection, TableMapInfo mapInfo){
        try {
            return GeneralJsonObjectDao.createJsonObjectDao(connection,mapInfo);
        } catch (SQLException e){
            throw  new PersistenceException(e);
        }
    }

    public static  int saveNewObject(Connection connection, T object) throws PersistenceException {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            object = OrmUtils.prepareObjectForInsert(object, mapInfo, sqlDialect);
            return sqlDialect.saveNewObject(OrmUtils.fetchObjectDatabaseField(object, mapInfo));
        }catch (NoSuchFieldException | IOException | SQLException e){
            throw  new PersistenceException(e);
        }
    }

    public static  int updateObject(Connection connection, T object) throws PersistenceException {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            object = OrmUtils.prepareObjectForUpdate(object,mapInfo,sqlDialect );

            return sqlDialect.updateObject( OrmUtils.fetchObjectDatabaseField(object,mapInfo));
        }catch (NoSuchFieldException | IOException | SQLException e){
            throw  new PersistenceException(e);
        }
    }

    /**
     * 只更改对象object的部分属性 fields
     * @param connection 数据库连接
     * @param fields 需要修改的属性
     * @param object 修改的对象,主键必须有值
     * @param  对象类型
     * @return 更改的记录数
     * @throws PersistenceException 运行时异常
     */
    public static  int updateObject(Connection connection, Collection fields,  T object)
            throws PersistenceException  {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            object = OrmUtils.prepareObjectForUpdate(object,mapInfo,sqlDialect );

            return sqlDialect.updateObject(fields, OrmUtils.fetchObjectDatabaseField(object,mapInfo));
        }catch (NoSuchFieldException | IOException | SQLException e){
            throw  new PersistenceException(e);
        }
    }

    /**
     * 批量修改 对象
     * @param connection 数据库连接
     * @param fields 需要修改的属性,对应的值从 object 对象中找
     * @param object   对应 fields 中的属性必须有值,如果没有值 将被设置为null
     * @param propertiesFilter 过滤条件对
     * @param  类型
     * @return 更改的条数
     * @throws PersistenceException 运行时异常
     */
    public static  int batchUpdateObject(
            Connection connection, Collection fields,   T object,
                Map propertiesFilter)
            throws PersistenceException  {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            object = OrmUtils.prepareObjectForUpdate(object,mapInfo,sqlDialect );

            return sqlDialect.updateObjectsByProperties(
                    fields,
                    OrmUtils.fetchObjectDatabaseField(object,mapInfo),
                    propertiesFilter);
        }catch (NoSuchFieldException | IOException | SQLException e){
            throw  new PersistenceException(e);
        }
    }

    /**
     * 批量修改 对象
     * @param connection 数据库连接
     * @param type 对象类型
     * @param propertiesValue 值对
     * @param propertiesFilter 过滤条件对
     * @return 更改的条数
     * @throws PersistenceException 运行时异常
     */
    public static int batchUpdateObject(
            Connection connection, Class type,
            Map propertiesValue,
            Map propertiesFilter)
            throws PersistenceException  {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);

            return sqlDialect.updateObjectsByProperties(
                    propertiesValue.keySet(),
                    propertiesValue,
                    propertiesFilter);
        }catch (SQLException e){
            throw  new PersistenceException(e);
        }
    }

    public static  int mergeObject(Connection connection, T object) throws PersistenceException {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            object = OrmUtils.prepareObjectForMerge(object,mapInfo,sqlDialect );
            return sqlDialect.mergeObject( OrmUtils.fetchObjectDatabaseField(object,mapInfo));
        }catch (NoSuchFieldException | IOException | SQLException e){
            throw  new PersistenceException(e);
        }
    }

    public interface FetchDataWork {
        T execute(ResultSet rs) throws SQLException, IOException,NoSuchFieldException,
                InstantiationException, IllegalAccessException;
    }
    /**
     * 查询数据库模板代码
     * @param conn 数据库链接
     * @param sqlAndParams 命名查询语句
     * @param fetchDataWork 获取数据的方法
     * @param  返回类型嗯
     * @return 返回结果
     * @throws PersistenceException 异常
     */

    private final static  T queryParamsSql(Connection conn, QueryAndParams sqlAndParams ,
                                             FetchDataWork fetchDataWork)
            throws PersistenceException {
        QueryLogUtils.printSql(logger, sqlAndParams.getQuery(), sqlAndParams.getParams() );
        try(PreparedStatement stmt = conn.prepareStatement(sqlAndParams.getQuery())){
            DatabaseAccess.setQueryStmtParameters(stmt,sqlAndParams.getParams());
            try(ResultSet rs = stmt.executeQuery()) {
                return fetchDataWork.execute(rs);
            }
            //rs.close();
            //stmt.close();
            //return obj;
        }catch (SQLException e) {
            throw  new PersistenceException(sqlAndParams.getQuery(), e);
        }catch (NoSuchFieldException | IOException | InstantiationException | IllegalAccessException e){
            throw  new PersistenceException(PersistenceException.ILLEGALACCESS_EXCEPTION,e);
        }
    }

    private final static  T queryParamsSql(Connection conn, QueryAndParams sqlAndParams ,
                                              int startPos, int maxSize, FetchDataWork fetchDataWork)
            throws PersistenceException {
        sqlAndParams.setQuery(QueryUtils.buildLimitQuerySQL(
                sqlAndParams.getQuery(),  startPos , maxSize , false , DBType.mapDBType(conn)
            ));
        return queryParamsSql(conn,  sqlAndParams , fetchDataWork);
    }
    /**
     * 查询数据库模板代码
     * @param conn 数据库链接
     * @param sqlAndParams 命名查询语句
     * @param fetchDataWork 获取数据的方法
     * @param  返回类型嗯
     * @return 返回结果
     * @throws PersistenceException 异常
     */
    private static  T queryNamedParamsSql(Connection conn, QueryAndNamedParams sqlAndParams,
                                                   FetchDataWork fetchDataWork)
            throws PersistenceException {
        QueryAndParams qap = QueryAndParams.createFromQueryAndNamedParams(sqlAndParams);
        return queryParamsSql(conn, qap ,fetchDataWork);
    }

    private static  T queryNamedParamsSql(Connection conn, QueryAndNamedParams sqlAndParams,
                                             int startPos, int maxSize, FetchDataWork fetchDataWork)
            throws PersistenceException {
        QueryAndParams qap = QueryAndParams.createFromQueryAndNamedParams(sqlAndParams);
        return queryParamsSql(conn, qap,  startPos, maxSize ,fetchDataWork);
    }


    public static  T getObjectBySql(Connection connection, String sql, Map properties, Class type)
            throws PersistenceException{
        //JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,
                        properties),
                (rs) -> OrmUtils.fetchObjectFormResultSet(rs, type)
        );
    }

    public static  T getObjectById(Connection connection, Object id, final Class type)
            throws PersistenceException {

        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        Pair q = GeneralJsonObjectDao.buildGetObjectSqlByPk(mapInfo);

        if(ReflectionOpt.isScalarType(id.getClass())){
            if(mapInfo.getPkColumns()==null || mapInfo.getPkColumns().size()!=1)
                throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,
                        "表"+mapInfo.getTableName()+"不是单主键表,这个方法不适用。");
            return getObjectBySql(connection,q.getKey(),
                    QueryUtils.createSqlParamsMap(mapInfo.getPkColumns().get(0),id), type);
        }else{
            Map idObj = OrmUtils.fetchObjectField(id);
            if(! GeneralJsonObjectDao.checkHasAllPkColumns(mapInfo,idObj)){
                throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,
                        "缺少主键对应的属性。");
            }
            return getObjectBySql(connection, q.getKey(),
                    idObj, type);
        }

    }

    public static  T getObjectIncludeLazyById(Connection connection, Object id, final Class type)
            throws PersistenceException {

        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        String  sql =  "select " + mapInfo.buildFieldIncludeLazySql("") +
                " from " +mapInfo.getTableName() + " where " +
                GeneralJsonObjectDao.buildFilterSqlByPk(mapInfo,null);

        if(ReflectionOpt.isScalarType(id.getClass())){
            if(mapInfo.getPkColumns()==null || mapInfo.getPkColumns().size()!=1)
                throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"表"+mapInfo.getTableName()+"不是单主键表,这个方法不适用。");
            return getObjectBySql(connection, sql,
                    QueryUtils.createSqlParamsMap(mapInfo.getPkColumns().get(0),id), type);

        }else{
            Map idObj = OrmUtils.fetchObjectField(id);
            if(! GeneralJsonObjectDao.checkHasAllPkColumns(mapInfo,idObj)){
                throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"缺少主键对应的属性。");
            }
            return getObjectBySql(connection, sql, idObj, type);
        }

    }

    public static  T getObjectCascadeShallowById(Connection connection, Object id, final Class type)
            throws PersistenceException {

        T object = getObjectById(connection, id, type);
        fetchObjectReferences(connection, object);
        return object;
    }

    public static  T getObjectCascadeById(Connection connection, Object id, final Class type)
            throws PersistenceException {

        T object = getObjectById(connection, id, type);
        fetchObjectReferencesCascade(connection, object,type);
        return object;
    }

    private static int deleteObjectById(Connection connection, Map id, TableMapInfo mapInfo) throws PersistenceException {
        try{
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            return sqlDialect.deleteObjectById(id);
        }catch (SQLException e) {
            throw  new PersistenceException(e);
        }
    }

    public static  int deleteObjectById(Connection connection, Map id,  Class type) throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        return deleteObjectById(connection, id, mapInfo);
    }

    public static  int deleteObject(Connection connection, T object) throws PersistenceException {

        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        Map idMap = OrmUtils.fetchObjectDatabaseField(object,mapInfo);
        return deleteObjectById(connection, idMap,mapInfo);
    }

    public static  int deleteObjectById(Connection connection, Object id, Class type)
            throws PersistenceException {

        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        if(ReflectionOpt.isScalarType(id.getClass())){
            if(mapInfo.getPkColumns()==null || mapInfo.getPkColumns().size()!=1)
                throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"表"+mapInfo.getTableName()+"不是单主键表,这个方法不适用。");
            return deleteObjectById(connection,
                    QueryUtils.createSqlParamsMap( mapInfo.getPkColumns().get(0),id),
                    mapInfo);

        }else{
            Map idObj = OrmUtils.fetchObjectField(id);
            if(! GeneralJsonObjectDao.checkHasAllPkColumns(mapInfo,idObj)){
                throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"缺少主键对应的属性。");
            }
            return deleteObjectById(connection, idObj, mapInfo);
        }
    }

    public static  T getObjectByProperties(Connection connection, Map properties, Class type)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        Pair q = GeneralJsonObjectDao.buildFieldSqlWithFieldName(mapInfo,null);
        String filter = GeneralJsonObjectDao.buildFilterSql(mapInfo,null,properties.keySet());
        String sql = "select " + q.getLeft() +" from " +mapInfo.getTableName();
        if(StringUtils.isNotBlank(filter))
            sql = sql + " where " + filter;
        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,
                        properties),
                (rs) -> OrmUtils.fetchObjectFormResultSet(rs, type));
    }

    public static  List listAllObjects(Connection connection, Class type)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        Pair q = GeneralJsonObjectDao.buildFieldSqlWithFieldName(mapInfo,null);
        String sql = "select " + q.getLeft() +" from " +mapInfo.getTableName();

        if(StringUtils.isNotBlank(mapInfo.getOrderBy()))
            sql = sql + " order by " + mapInfo.getOrderBy();
        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,
                        new HashMap<>(1)),
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  List listObjectsByProperties(Connection connection, Map properties, Class type)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        Pair q = GeneralJsonObjectDao.buildFieldSqlWithFieldName(mapInfo,null);
        String filter = GeneralJsonObjectDao.buildFilterSql(mapInfo,null,properties.keySet());
        String sql = "select " + q.getLeft() +" from " +mapInfo.getTableName();
        if(StringUtils.isNotBlank(filter))
            sql = sql + " where " + filter;
        if(StringUtils.isNotBlank(mapInfo.getOrderBy()))
            sql = sql + " order by " + mapInfo.getOrderBy();

        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,
                        properties),
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  List listObjectsByProperties(Connection connection, Map properties, Class type,
                                               final int startPos, final int maxSize)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
        Pair q = GeneralJsonObjectDao.buildFieldSqlWithFieldName(mapInfo,null);
        String filter = GeneralJsonObjectDao.buildFilterSql(mapInfo,null,properties.keySet());
        String sql = "select " + q.getLeft() +" from " +mapInfo.getTableName();
        if(StringUtils.isNotBlank(filter))
            sql = sql + " where " + filter;
        if(StringUtils.isNotBlank(mapInfo.getOrderBy()))
            sql = sql + " order by " + mapInfo.getOrderBy();

        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,
                        properties),startPos, maxSize,
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  List queryObjectsBySql(Connection connection, String sql, Class type)
            throws PersistenceException {
        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,
                        new HashMap<>()),
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  List queryObjectsByParamsSql(Connection connection, String sql, Object[] params, Class type)
            throws PersistenceException {
        return queryParamsSql(
                connection, new QueryAndParams(sql,params),
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  List queryObjectsByNamedParamsSql(Connection connection, String sql,
                                              Map params, Class type)
            throws PersistenceException {
        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,params),
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }


    public static  List queryObjectsBySql(Connection connection, String sql, Class type,
                                         int startPos,  int maxSize)
            throws PersistenceException {
        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,
                        new HashMap<>()), startPos, maxSize,
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  List queryObjectsByParamsSql(Connection connection, String sql, Object[] params, Class type,
                                               int startPos,  int maxSize)
            throws PersistenceException {
        return queryParamsSql(
                connection, new QueryAndParams(sql,params),startPos, maxSize,
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  List queryObjectsByNamedParamsSql(Connection connection, String sql,
                                                    Map params, Class type,
                                                    int startPos,  int maxSize)
            throws PersistenceException {
        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,params), startPos, maxSize,
                (rs) -> OrmUtils.fetchObjectListFormResultSet(rs, type));
    }

    public static  T fetchObjectLazyColumn(Connection connection, T object,String columnName)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        Map idMap = OrmUtils.fetchObjectDatabaseField(object,mapInfo);
        if(! GeneralJsonObjectDao.checkHasAllPkColumns(mapInfo,idMap)){
            throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION, "缺少主键对应的属性。");
        }

        String  sql =  "select " + mapInfo.findFieldByName(columnName).getColumnName() +
                " from " +mapInfo.getTableName() + " where " +
                GeneralJsonObjectDao.buildFilterSqlByPk(mapInfo,null);

        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,idMap),
                (rs) -> OrmUtils.fetchFieldsFormResultSet(rs,object,mapInfo));
    }

    public static  T fetchObjectLazyColumns(Connection connection, T object)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        String fieldSql = mapInfo.buildLazyFieldSql(null);
        if(fieldSql==null)
            return object;
        Map idMap = OrmUtils.fetchObjectDatabaseField(object,mapInfo);
        if(! GeneralJsonObjectDao.checkHasAllPkColumns(mapInfo,idMap)){
            throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"缺少主键对应的属性。");
        }

        String  sql =  "select " + fieldSql +
                " from " +mapInfo.getTableName() + " where " +
                GeneralJsonObjectDao.buildFilterSqlByPk(mapInfo,null);

        return queryNamedParamsSql(
                connection, new QueryAndNamedParams(sql,idMap),
                (rs) -> OrmUtils.fetchFieldsFormResultSet(rs,object,mapInfo));
    }

    private static  T fetchObjectReference(Connection connection, T object,SimpleTableReference ref,
                                              TableMapInfo mapInfo, boolean casecade)
            throws PersistenceException {

        if(ref==null || ref.getReferenceColumns().size()<1)
            return object;

        Class refType = ref.getTargetEntityType();
        TableMapInfo refMapInfo = JpaMetadata.fetchTableMapInfo( refType );
        if( refMapInfo == null )
            return object;

        Map properties = new HashMap<>(6);
        for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
            properties.put(ent.getValue(), ReflectionOpt.getFieldValue(object,ent.getKey()));
        }

        List refs = listObjectsByProperties( connection, properties, refType);
        if(refs!=null && refs.size()>0) {
            if(casecade){
                for(Object refObject : refs){
                    fetchObjectReferencesCascade(connection, refObject,refType);
                }
            }
            if (//ref.getReferenceType().equals(refType) || oneToOne
                    ref.getReferenceType().isAssignableFrom(refType) ){
                ref.setObjectFieldValue(object, refs.get(0));
            }else if(Set.class.isAssignableFrom(ref.getReferenceType())){
                ref.setObjectFieldValue(object, new HashSet<>(refs));
            }else if(List.class.isAssignableFrom(ref.getReferenceType())){
                ref.setObjectFieldValue(object, refs);
            }
        }
        return object;
    }

    private static  T fetchObjectReference(Connection connection, T object,SimpleTableReference ref ,TableMapInfo mapInfo )
            throws PersistenceException {
        return fetchObjectReference(connection, object,ref ,mapInfo , false);
    }

    private static  T fetchObjectReferenceCascade(Connection connection, T object,SimpleTableReference ref ,TableMapInfo mapInfo )
            throws PersistenceException {
        return fetchObjectReference(connection, object,ref ,mapInfo , true);
    }

    private static  T fetchObjectReferencesCascade(Connection connection, T object, Class objType ){
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                fetchObjectReferenceCascade(connection, object, ref, mapInfo);
            }
        }
        return object;
    }


    public static  T fetchObjectReference(Connection connection, T object, String reference  )
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        SimpleTableReference ref = mapInfo.findReference(reference);

        return fetchObjectReference(connection, object,ref,mapInfo);
    }

    public static  T fetchObjectReferences(Connection connection, T object)
            throws PersistenceException {

        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                fetchObjectReference(connection, object, ref, mapInfo);
            }
        }
        return object;
    }


    public static  int deleteObjectByProperties(Connection connection, Map properties, Class type)
            throws PersistenceException {
        try{
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            return sqlDialect.deleteObjectsByProperties(properties);
        }catch (SQLException e) {
            throw  new PersistenceException(e);
        }
    }

    public static  int deleteObjectReference(Connection connection, T object,SimpleTableReference ref)
            throws PersistenceException {

        if(ref==null || ref.getReferenceColumns().size()<1)
            return 0;

        Class refType = ref.getTargetEntityType();
        TableMapInfo refMapInfo = JpaMetadata.fetchTableMapInfo( refType );
        if( refMapInfo == null )
            return 0;

        Map properties = new HashMap<>(6);
        for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
            properties.put(ent.getValue(), ReflectionOpt.getFieldValue(object,ent.getKey()));
        }

        return deleteObjectByProperties(connection, properties, refType);
    }

    public static  int deleteObjectReference(Connection connection, T object, String reference)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        SimpleTableReference ref = mapInfo.findReference(reference);
        return deleteObjectReference(connection, object,ref);
    }

    public static  int deleteObjectReferences(Connection connection, T object)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        int  n=0;
        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                n+= deleteObjectReference(connection, object,ref);
            }
        }
        return n;
    }

    public static  int deleteObjectCascadeShallow(Connection connection, T object)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        Map idMap = OrmUtils.fetchObjectDatabaseField(object,mapInfo);

        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                deleteObjectReference(connection, object,ref);
            }
        }

        return deleteObjectById(connection, idMap,mapInfo);
    }

    public static  int deleteObjectCascade(Connection connection, T object)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        Map idMap = OrmUtils.fetchObjectDatabaseField(object,mapInfo);
        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                Map properties = new HashMap<>(6);
                Class refType = ref.getTargetEntityType();
                for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
                    properties.put(ent.getValue(), ReflectionOpt.getFieldValue(object,ent.getKey()));
                }

                List refs = listObjectsByProperties(connection,  properties, refType);
                for(Object refObject : refs){
                    deleteObjectCascade(connection, refObject);
                }
            }
        }
        return deleteObject(connection, object);
    }

    public static  int deleteObjectCascadeShallowById(Connection connection, Object id, final Class type)
            throws PersistenceException {

        return deleteObjectCascadeShallow(connection, getObjectById(connection, id, type));
    }

    public static  int deleteObjectCascadeById(Connection connection, Object id, final Class type)
            throws PersistenceException {

        return deleteObjectCascade(connection, getObjectById(connection, id, type));
    }

    public static class OrmObjectComparator implements Comparator{
        private TableInfo tableInfo;
        public  OrmObjectComparator(TableMapInfo tableInfo){
            this.tableInfo = tableInfo;
        }
        @Override
        public int compare(T o1, T o2) {
            for(String pkc : tableInfo.getPkColumns() ){
                Object f1 = ReflectionOpt.getFieldValue(o1,pkc);
                Object f2 = ReflectionOpt.getFieldValue(o2,pkc);
                if(f1==null){
                    if(f2!=null)
                        return -1;
                }else{
                    if(f2==null)
                        return 1;
                    if( ReflectionOpt.isNumberType(f1.getClass())){
                        double db1 = ((Number)f1).doubleValue();
                        double db2 = ((Number)f2).doubleValue();
                        if(db1>db2)
                            return 1;
                        if(db1 int replaceObjectsAsTabulation(Connection connection, List dbObjects,List newObjects)
            throws PersistenceException {
        if(newObjects == null || newObjects.size()==0){
            if(dbObjects==null || dbObjects.size()==0){
                return 0;
            }
            for(T obj: dbObjects){
                deleteObject(connection, obj);
            }
            return dbObjects.size();
        }
        Class objType =(Class) newObjects.get(0).getClass();
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(objType);
        Triple, List>, List>
                comRes=
                ListOpt.compareTwoList(dbObjects, newObjects,
                        new OrmObjectComparator<>( mapInfo) );
        int resN = 0;
        if(comRes.getLeft() != null) {
            for (T obj : comRes.getLeft()) {
                resN += saveNewObject(connection, obj);
            }
        }
        if(comRes.getRight() != null) {
            for (T obj : comRes.getRight()) {
                resN += deleteObject(connection, obj);
            }
        }
        if(comRes.getMiddle() != null) {
            for (Pair pobj : comRes.getMiddle()) {
                resN += updateObject(connection, pobj.getRight());
            }
        }
        return resN;
    }

    public static  int replaceObjectsAsTabulation(Connection connection, List newObjects,
                           final String propertyName, final Object propertyValue )
            throws PersistenceException {
        return replaceObjectsAsTabulation(connection, newObjects,
                JSONOpt.createHashMap(propertyName,propertyValue));
    }

    public static  int replaceObjectsAsTabulation(Connection connection, List newObjects,
                                               Map properties)
            throws PersistenceException {
        if(newObjects==null || newObjects.size()<1)
            return 0;
        Class objType =(Class) newObjects.iterator().next().getClass();
        List dbObjects = listObjectsByProperties(connection, properties, objType);
        return replaceObjectsAsTabulation(connection, dbObjects,newObjects);
    }

    private static  int saveNewObjectReferenceCascade(Connection connection, T object,
                                                         SimpleTableReference ref ,TableMapInfo mapInfo )
            throws PersistenceException {

        if(ref==null || ref.getReferenceColumns().size()<1)
            return 0;

        Object newObj = ref.getObjectFieldValue(object);
        //ReflectionOpt.getFieldValue( object, ref.getReferenceName());
        if(newObj==null){
            return 0;
        }

        Class refType = ref.getTargetEntityType();
        TableMapInfo refMapInfo = JpaMetadata.fetchTableMapInfo( refType );
        if( refMapInfo == null )
            return 0;
        if (//ref.getReferenceType().equals(refType) || oneToOne
                ref.getReferenceType().isAssignableFrom(refType) ){
            for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
                Object obj = mapInfo.findFieldByName(ent.getKey()).getObjectFieldValue(object);
                refMapInfo.findFieldByName(ent.getValue()).setObjectFieldValue(newObj,obj);
            }
            saveNewObjectCascade(connection, newObj);
        }else if(newObj instanceof Collection){
            for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
                Object obj = mapInfo.findFieldByName(ent.getKey()).getObjectFieldValue(object);
                for(Object subObj : (Collection)newObj) {
                    refMapInfo.findFieldByName(ent.getValue()).setObjectFieldValue(subObj, obj);
                }
            }
            for(Object subObj : (Collection)newObj){
                saveNewObjectCascade(connection, subObj);
            }
        }
        return 1;
    }

    private static  int saveObjectReference(Connection connection, T object,SimpleTableReference ref ,TableMapInfo mapInfo )
            throws PersistenceException {

        if(ref==null || ref.getReferenceColumns().size()<1)
            return 0;

        Object newObj = ref.getObjectFieldValue(object);
        //ReflectionOpt.getFieldValue( object, ref.getReferenceName());
        if(newObj==null){
            return deleteObjectReference(connection, object,ref);
        }

        Class refType = ref.getTargetEntityType();
        TableMapInfo refMapInfo = JpaMetadata.fetchTableMapInfo( refType );
        if( refMapInfo == null )
            return 0;

        Map properties = new HashMap<>(6);
        for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
            properties.put(ent.getValue(), ReflectionOpt.getFieldValue(object,ent.getKey()));
        }

        List refs = listObjectsByProperties(connection,  properties, refType);

        if (//ref.getReferenceType().equals(refType) || oneToOne
                ref.getReferenceType().isAssignableFrom(refType) ){
            for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
                Object obj = mapInfo.findFieldByName(ent.getKey()).getObjectFieldValue(object);
                refMapInfo.findFieldByName(ent.getValue()).setObjectFieldValue(newObj,obj);
            }
            if(refs!=null && refs.size()>0){
                updateObject(connection, newObj);
            }else{
                saveNewObject(connection, newObj);
            }
        }else {
            List newListObj = Set.class.isAssignableFrom(ref.getReferenceType())?
                  new ArrayList<>((Set) newObj):(List) newObj;

            for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
                Object obj = mapInfo.findFieldByName(ent.getKey()).getObjectFieldValue(object);
                for(Object subObj : newListObj) {
                    refMapInfo.findFieldByName(ent.getValue()).setObjectFieldValue(subObj, obj);
                }
            }
            replaceObjectsAsTabulation(connection, (List) refs, newListObj);

        }
        return 1;
    }

    public static  int saveObjectReference (Connection connection, T object, String reference)
            throws PersistenceException {

        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        SimpleTableReference ref = mapInfo.findReference(reference);
        return saveObjectReference(connection, object,ref,mapInfo);
    }

    public static  int saveObjectReferences (Connection connection, T object)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        int n=0;
        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                n += saveObjectReference(connection, object, ref, mapInfo);
            }
        }
        return n;
    }

    public static  int saveNewObjectCascadeShallow (Connection connection, T object)
            throws PersistenceException {
        return saveNewObject(connection, object)
                + saveObjectReferences(connection, object);
    }

    public static  int saveNewObjectCascade (Connection connection, T object)
            throws PersistenceException {

        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        int n= saveNewObject(connection, object);
        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                n += saveNewObjectReferenceCascade(connection, object, ref, mapInfo);
            }
        }
        return n;
    }

    public static  int updateObjectCascadeShallow (Connection connection, T object)
            throws PersistenceException {
        return updateObject(connection, object)
           + saveObjectReferences(connection, object);
    }

    private static  int replaceObjectsAsTabulationCascade(Connection connection, List dbObjects,List newObjects)
            throws PersistenceException {

        if(newObjects == null || newObjects.size()==0){
            if(dbObjects==null || dbObjects.size()==0){
                return 0;
            }
            for(T obj: dbObjects){
                deleteObjectCascade(connection, obj);
            }
            return dbObjects.size();
        }

        Class objType =(Class) newObjects.get(0).getClass();
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(objType);
        Triple, List>, List>
                comRes=
                ListOpt.compareTwoList(dbObjects, newObjects,
                        new OrmObjectComparator<>(mapInfo) );
        int resN = 0;
        if(comRes.getLeft() != null) {
            for (T obj : comRes.getLeft()) {
                resN += saveNewObjectCascade(connection, obj);
            }
        }
        if(comRes.getRight() != null) {
            for (T obj : comRes.getRight()) {
                resN += deleteObjectCascade(connection, obj);
            }
        }
        if(comRes.getMiddle() != null) {
            for (Pair pobj : comRes.getMiddle()) {
                resN += updateObjectCascade(connection, pobj.getRight());
            }
        }
        return resN;
    }

    private static  int updateObjectReferenceCascade(Connection connection, T object,SimpleTableReference ref ,TableMapInfo mapInfo )
            throws PersistenceException {

        if(ref==null || ref.getReferenceColumns().size()<1)
            return 0;

        Object newObj = ref.getObjectFieldValue(object);
        // ReflectionOpt.getFieldValue( object, ref.getReferenceName());
        Class refType = ref.getTargetEntityType();
        TableMapInfo refMapInfo = JpaMetadata.fetchTableMapInfo( refType );
        if( refMapInfo == null )
            return 0;

        Map properties = new HashMap<>(6);
        for(Map.Entry ent : ref.getReferenceColumns().entrySet()){
            properties.put(ent.getValue(), ReflectionOpt.getFieldValue(object,ent.getKey()));
        }
        int  n = 0;
        List refs = listObjectsByProperties(connection,  properties, refType);
        if(newObj==null){
            if(refs!=null && refs.size()>0) {
                if (//ref.getReferenceType().equals(refType) || oneToOne
                        ref.getReferenceType().isAssignableFrom(refType) ){
                    n += deleteObjectCascade(connection, refs.get(0));
                } else {
                    for (Object subObj : refs) {
                        n += deleteObjectCascade(connection, subObj);
                    }
                }
            }
            return n;
        }

        if (//ref.getReferenceType().equals(refType) || oneToOne
                ref.getReferenceType().isAssignableFrom(refType) ){
            if(refs!=null && refs.size()>0){
                updateObjectCascade(connection, newObj);
            }else{
                saveNewObjectCascade(connection, newObj);
            }
        }else if(Set.class.isAssignableFrom(ref.getReferenceType())){
            replaceObjectsAsTabulationCascade(connection,  (List) refs,
                    new ArrayList<>((Set) newObj));
        }else if(List.class.isAssignableFrom(ref.getReferenceType())){
            replaceObjectsAsTabulationCascade(connection,  (List) refs,
                    (List) newObj );
        }

        return 1;
    }

    public static  int updateObjectCascade (Connection connection, T object) throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        int n= updateObject(connection, object);
        if(mapInfo.hasReferences()) {
            for (SimpleTableReference ref : mapInfo.getReferences()) {
                n += updateObjectReferenceCascade(connection, object, ref, mapInfo);
            }
        }
        return n;
    }

    public static  int checkObjectExists(Connection connection, T object)
            throws PersistenceException {
        TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
        Map objectMap = OrmUtils.fetchObjectDatabaseField(object,mapInfo);

        if(! GeneralJsonObjectDao.checkHasAllPkColumns(mapInfo,objectMap)){
            throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"缺少主键对应的属性。");
        }
        String sql =
                "select count(*) as checkExists from " + mapInfo.getTableName()
                        + " where " +  GeneralJsonObjectDao.checkHasAllPkColumns(mapInfo,null);

        try {
            Long checkExists = NumberBaseOpt.castObjectToLong(
                    DatabaseAccess.getScalarObjectQuery(connection, sql, objectMap));
            return checkExists==null?0:checkExists.intValue();
        }catch (SQLException e) {
            throw  new PersistenceException(sql,e);
        }catch (IOException e){
            throw  new PersistenceException(e);
        }
    }

    public static  int fetchObjectsCount(Connection connection, Map properties, Class type)
            throws PersistenceException {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(type);
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            return sqlDialect.fetchObjectsCount(properties).intValue();
        } catch (SQLException | IOException e){
            throw  new PersistenceException(e);
        }
    }

    public static  int fetchObjectsCount(Connection connection, String sql , Map properties)
            throws PersistenceException {
        try {
            return NumberBaseOpt.castObjectToInteger(
                    DatabaseAccess.getScalarObjectQuery(connection,sql,properties));
        } catch (SQLException e) {
            throw  new PersistenceException(sql,e);
        } catch (IOException e){
            throw  new PersistenceException(e);
        }
    }

    private static  T prepareObjectForMerge(Connection connection, T object) throws PersistenceException {
        try {
            TableMapInfo mapInfo = JpaMetadata.fetchTableMapInfo(object.getClass());
            JsonObjectDao sqlDialect = GeneralJsonObjectDao.createJsonObjectDao(connection, mapInfo);
            return OrmUtils.prepareObjectForMerge(object, mapInfo, sqlDialect);
        } catch (IOException | SQLException| NoSuchFieldException e) {
            throw new PersistenceException(e);
        }
    }
    public static  int mergeObjectCascadeShallow(Connection connection, T object)
            throws PersistenceException {
        object = prepareObjectForMerge(connection,  object);
        int  checkExists = checkObjectExists(connection, object);
        if(checkExists == 0){
            return saveNewObjectCascadeShallow(connection, object);
        }else if(checkExists == 1){
            return updateObjectCascadeShallow(connection, object);
        }else{
            throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"主键属性有误,返回多个条记录。");
        }
    }

    public static  int mergeObjectCascade(Connection connection, T object) throws PersistenceException {
        object = prepareObjectForMerge(connection,  object);
        int  checkExists = checkObjectExists(connection,object);
        if(checkExists == 0){
            return saveNewObjectCascade(connection,object);
        }else if(checkExists == 1){
            return updateObjectCascade(connection, object);
        }else{
            throw new PersistenceException(PersistenceException.ORM_METADATA_EXCEPTION,"主键属性有误,返回多个条记录。");
        }
    }
}