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

io.github.pustike.persist.sql.SelectQuery Maven / Gradle / Ivy

/*
 * Copyright (C) 2016-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.github.pustike.persist.sql;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import io.github.pustike.persist.metadata.ColumnType;
import io.github.pustike.persist.metadata.EntityData;
import io.github.pustike.persist.metadata.FieldData;
import io.github.pustike.persist.metadata.Schema;

final class SelectQuery {
    private static final Logger logger = System.getLogger(SelectQuery.class.getName());
    private final SqlQuery sqlQuery;
    private final Class entityClass;
    private final String fieldGroup;
    private final Object primaryKey;
    private final String alias;

    SelectQuery(SqlQuery sqlQuery, Class entityClass, String fieldGroup, Object primaryKey) {
        this.sqlQuery = sqlQuery;
        this.entityClass = entityClass;
        this.fieldGroup = fieldGroup;
        this.primaryKey = Objects.requireNonNull(primaryKey);
        this.alias = "x";
    }

     E find(boolean forUpdate) {
        Map> aliasFieldNamesMap = new HashMap<>();
        String queryString = buildQuery(aliasFieldNamesMap);
        if (forUpdate) {
            queryString = queryString + " for update of " + alias;
        }
        logger.log(Level.INFO, queryString);
        try (PreparedStatement stmt = sqlQuery.getConnection().prepareStatement(queryString)) {
            stmt.setObject(1, primaryKey);
            try (ResultSet resultSet = stmt.executeQuery()) {
                return resultSet.next() ? toResultData(resultSet, aliasFieldNamesMap) : null;
            }
        } catch (Exception e) {
            throw new RuntimeException("Couldn't execute query", e);
        }
    }

    private String buildQuery(Map> aliasFieldNamesMap) {
        Schema schema = sqlQuery.getSchema();
        EntityData entityData = schema.getEntityData(entityClass);
        Set fieldGroupFields = entityData.getFieldGroupFields(fieldGroup);
        int fkCounter = 0;
        List selectedFieldNames = new ArrayList<>();
        StringBuilder queryBuilder = new StringBuilder("select");
        StringBuilder joinBuilder = new StringBuilder();
        for (String fieldName : fieldGroupFields) {
            int fgIndex = fieldName.indexOf('@');
            String joinFieldGroup = null;
            if (fgIndex != -1) {
                joinFieldGroup = fieldName.substring(fgIndex + 1);
                fieldName = fieldName.substring(0, fgIndex);
            }
            FieldData fieldData = entityData.getFieldData(fieldName);
            if (fieldData.getColumnType() == ColumnType.ForeignKey) {
                EntityData fkEntityData = schema.getEntityData(fieldData.getFieldType());
                String asAlias = "t" + (fkCounter++);
                joinBuilder.append(getJoinClause(fkEntityData, fieldData, asAlias));
                List selectedFkFieldNames = new ArrayList<>();
                for (String fkFieldName : fkEntityData.getJoinFetchFields(joinFieldGroup)) {
                    int fgIndex2 = fkFieldName.indexOf('@');
                    fkFieldName = fgIndex2 == -1 ? fkFieldName : fkFieldName.substring(0, fgIndex2);
                    FieldData fkFieldData = fkEntityData.getFieldData(fkFieldName);
                    selectedFkFieldNames.add(fkFieldData.getName());
                    queryBuilder.append(' ').append(asAlias).append('.')
                        .append(fkFieldData.getColumnName()).append(',');
                }
                selectedFieldNames.add(fieldData.getName() + '@' + asAlias);
                aliasFieldNamesMap.put(fieldData.getFieldType().getSimpleName() + '@' + asAlias, selectedFkFieldNames);
            } else {
                selectedFieldNames.add(fieldData.getName());
                queryBuilder.append(' ').append(alias).append('.').append(fieldData.getColumnName()).append(',');
            }
        }
        queryBuilder.setLength(queryBuilder.length() - 1);
        aliasFieldNamesMap.put(entityClass.getSimpleName() + '@' + alias, selectedFieldNames);
        //
        queryBuilder.append(" FROM ").append(schema.toSchemaTableName(entityData)).append(" as ").append(alias);
        queryBuilder.append(joinBuilder);
        //
        FieldData idField = entityData.getIdField();
        queryBuilder.append(" WHERE ").append(alias).append('.').append(idField.getColumnName()).append(" = ?");
        return queryBuilder.toString();
    }

    @SuppressWarnings("unchecked")
    private  T toResultData(ResultSet resultSet, Map> aliasFieldNamesMap) throws Exception {
        int index = 1;
        T instance = (T) entityClass.getDeclaredConstructor().newInstance();
        Schema schema = sqlQuery.getSchema();
        EntityData entityData = schema.getEntityData(entityClass);
        List fieldAliasList = aliasFieldNamesMap.get(entityClass.getSimpleName() + '@' + alias);
        for (String fieldAlias : fieldAliasList) {
            String[] fieldAliasSplit = fieldAlias.split("@");
            FieldData fieldData = entityData.getFieldData(fieldAliasSplit[0]);
            if (fieldData.getColumnType() == ColumnType.ForeignKey) {
                Class fkFieldType = fieldData.getFieldType();
                String key = fkFieldType.getSimpleName() + '@' + fieldAliasSplit[1];
                List joinFetchFields = aliasFieldNamesMap.get(key);
                EntityData fkEntityData = schema.getEntityData(fkFieldType);
                Object fkInstance = null;// TODO cache these instances based on id to avoid multiple object creations
                for (String fieldName : joinFetchFields) {
                    FieldData fkFieldData = fkEntityData.getFieldData(fieldName);
                    if (fkFieldData.getColumnType() != ColumnType.ForeignKey) {
                        Class resultType = fkFieldData.getResultType();
                        Object value = resultType == null ? resultSet.getObject(index++)
                            : resultSet.getObject(index++, resultType);
                        if (value != null && fkInstance == null) {
                            fkInstance = fkEntityData.getEntityClass().getDeclaredConstructor().newInstance();
                        }
                        if (fkInstance != null) {
                            fkFieldData.setValue(fkInstance, value);
                        }
                    } else {// only load the id from the 2nd level foreign key!
                        Class fkFieldType2 = fkFieldData.getFieldType();
                        EntityData fkEntityData2 = schema.getEntityData(fkFieldType2);
                        FieldData fkIdField2 = fkEntityData2.getIdField();
                        Class resultType = fkIdField2.getResultType();
                        Object value = resultType == null ? resultSet.getObject(index++)
                            : resultSet.getObject(index++, resultType);
                        if (value != null) {
                            Object fkInstance2 = fkEntityData2.getEntityClass().getDeclaredConstructor().newInstance();
                            fkIdField2.setValue(fkInstance2, value);
                            fkFieldData.setValue(fkInstance, fkInstance2);
                        }
                    }
                }
                fieldData.setValue(instance, fkInstance);
            } else {
                Class resultType = fieldData.getResultType();
                fieldData.setValue(instance, resultType == null ? resultSet.getObject(index++)
                    : resultSet.getObject(index++, resultType));
            }
        }
        return instance;
    }

    private String getJoinClause(EntityData fkData, FieldData fieldData, String asAlias) {
        String joinType = fieldData.isOptional() ? " left outer join " : " inner join ";
        StringBuilder queryBuilder = new StringBuilder(joinType).append(sqlQuery.getSchema().toSchemaTableName(fkData))
            .append(" as ").append(asAlias).append(" on ")
            .append(asAlias).append('.').append(fkData.getIdField().getColumnName()).append(" = ");
        return queryBuilder.append(alias).append('.').append(fieldData.getColumnName()).toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy