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

org.apache.cayenne.dbsync.merge.DbAttributeMerger Maven / Gradle / Ivy

There is a newer version: 5.0-M1
Show newest version
/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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
 *
 *    http://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 org.apache.cayenne.dbsync.merge;

import java.sql.Types;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.cayenne.dbsync.merge.token.ValueForNullProvider;
import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
import org.apache.cayenne.dbsync.merge.token.MergerToken;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DetectedDbEntity;

class DbAttributeMerger extends AbstractMerger {

    private final ValueForNullProvider valueForNull;

    DbAttributeMerger(MergerTokenFactory tokenFactory, ValueForNullProvider valueForNull) {
        super(tokenFactory);
        this.valueForNull = valueForNull;
    }

    @Override
    MergerDictionaryDiff createDiff(DbEntity original, DbEntity imported) {
        return new MergerDictionaryDiff.Builder()
                .originalDictionary(new DbAttributeDictionary(original))
                .importedDictionary(new DbAttributeDictionary(imported))
                .build();
    }

    /**
     * Add column to db
     * @param original attribute found in model but missing in db
     */
    @Override
    Collection createTokensForMissingImported(DbAttribute original) {
        DbEntity originalDbEntity = original.getEntity();
        List tokens = new LinkedList<>();
        tokens.add(getTokenFactory().createAddColumnToDb(originalDbEntity, original));

        // Create not null check
        if (original.isMandatory()) {
            if (valueForNull.hasValueFor(originalDbEntity, original)) {
                tokens.add(getTokenFactory().createSetValueForNullToDb(originalDbEntity, original, valueForNull));
            }
            tokens.add(getTokenFactory().createSetNotNullToDb(originalDbEntity, original));
        }

        if(original.isPrimaryKey()
                && originalDbEntity instanceof DetectedDbEntity
                && "VIEW".equals(((DetectedDbEntity) originalDbEntity).getType())) {
            // Views doesn't has PKs in a database, but if the user selects some PKs in a model, we put these keys.
            return null;
        }
        return tokens;
    }

    /**
     * Drop column in db
     * @param imported attribute found in db but missing in model
     */
    @Override
    Collection createTokensForMissingOriginal(DbAttribute imported) {
        DbEntity originalDbEntity = getOriginalDictionary().getByName(imported.getEntity().getName().toUpperCase());
        return Collections.singleton(getTokenFactory().createDropColumnToDb(originalDbEntity, imported));
    }

    /**
     * Compare same attributes in model and db
     */
    @Override
    Collection createTokensForSame(MergerDiffPair same) {
        List tokens = new LinkedList<>();

        // isMandatory flag
        checkMandatory(same.getOriginal(), same.getImported(), tokens);
        // check type (including max length, scale and precision)
        checkType(same.getOriginal(), same.getImported(), tokens);
        // not implemented yet
        // isGenerated flag
        checkIsGenerated(same.getOriginal(), same.getImported(), tokens);

        return tokens;
    }

    private void checkMandatory(DbAttribute original, DbAttribute imported, List tokens) {
        if(original.isMandatory() == imported.isMandatory()) {
            return;
        }

        DbEntity originalDbEntity = original.getEntity();
        if (original.isMandatory()) {
            if (valueForNull.hasValueFor(originalDbEntity, original)) {
                tokens.add(getTokenFactory().createSetValueForNullToDb(originalDbEntity, original, valueForNull));
            }
            tokens.add(getTokenFactory().createSetNotNullToDb(originalDbEntity, original));
        } else {
            tokens.add(getTokenFactory().createSetAllowNullToDb(originalDbEntity, original));
        }
    }

    /**
     * Check whether attributes have same type, max length, scale and precision
     * @param original attribute in model
     * @param imported attribute from db
     * @return true if attributes not same
     */
    private boolean needUpdateType(DbAttribute original, DbAttribute imported) {
        if(original.getType() != imported.getType()) {
            // Decimal and NUMERIC types are effectively equal so skip their interchange
            if( (original.getType() == Types.DECIMAL || original.getType() == Types.NUMERIC) &&
                (imported.getType() == Types.DECIMAL || imported.getType() == Types.NUMERIC)) {
                return false;
            }
            return true;
        }

        if(original.getMaxLength() != imported.getMaxLength()) {
            int[] typesWithMaxLength = {Types.NCHAR, Types.NVARCHAR, Types.CHAR, Types.VARCHAR};
            for(int type : typesWithMaxLength) {
                if(original.getType() == type) {
                    return true;
                }
            }
        }

        if(needUpdateScale(original, imported)) {
            return true;
        }

        if(original.getAttributePrecision() != imported.getAttributePrecision()) {
            return true;
        }

        return false;
    }

    private boolean needUpdateScale(DbAttribute original, DbAttribute imported) {
        if(original.getScale() == imported.getScale()) {
            return false;
        }

        // -1 and 0 are actually equal values for scale
        if((original.getScale() == -1 || original.getScale() == 0)
                && (imported.getScale() == -1 || imported.getScale() == 0)) {
            return false;
        }

        return true;
    }

    private void checkType(DbAttribute original, DbAttribute imported, List tokens) {
        if(!needUpdateType(original, imported)) {
            return;
        }
        DbEntity originalDbEntity = original.getEntity();
        tokens.add(getTokenFactory().createSetColumnTypeToDb(originalDbEntity, imported, original));
    }

    private void checkIsGenerated(DbAttribute original, DbAttribute imported, List tokens) {
        if(original.isGenerated() == imported.isGenerated()) {
            return;
        }

        tokens.add(getTokenFactory().createSetGeneratedFlagToDb(original.getEntity(), original, original.isGenerated()));
    }

    @Override
    public List createMergeTokens() {
        throw new UnsupportedOperationException();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy