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

org.jumpmind.symmetric.load.TableTemplate Maven / Gradle / Ivy

Go to download

SymmetricDS is an open source database synchronization solution. It is platform-independent, web-enabled, and database-agnostic. SymmetricDS was first built to replicate changes between 'retail store' databases and ad centralized 'corporate' database.

The newest version!
/*
 * Licensed to JumpMind Inc under one or more contributor 
 * license agreements.  See the NOTICE file distributed
 * with this work for additional information regarding 
 * copyright ownership.  JumpMind Inc licenses this file
 * to you under the GNU Lesser General Public License (the
 * "License"); you may not use this file except in compliance
 * with the License. 
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see           
 * .
 * 
 * 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.jumpmind.symmetric.load;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.jumpmind.symmetric.db.IDbDialect;
import org.jumpmind.symmetric.ddl.model.Column;
import org.jumpmind.symmetric.ddl.model.Table;
import org.jumpmind.symmetric.load.StatementBuilder.DmlType;
import org.jumpmind.symmetric.util.ArgTypePreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * An instance of TableTemplate knows how to insert, update, and delete from a
 * single table. It uses metadata from the IDbDialect to get the columns for the
 * table in the target database in case the table is missing or has missing
 * columns. It uses a cache of StatementBuilder objects that contain the SQL and
 * PreparedStatement.
 */
public class TableTemplate {

    private JdbcTemplate jdbcTemplate;

    private IDbDialect dbDialect;

    private Table table;

    private String schema;

    private String catalog;

    private String tableName;

    private String[] keyNames;

    private String[] columnNames;

    private String[] oldData;

    private Map allMetaData;

    private HashMap statementMap;

    private List columnFilters = new ArrayList();

    private boolean dontIncludeKeysInUpdateStatement = false;

    public TableTemplate(JdbcTemplate jdbcTemplate, IDbDialect dbDialect, String tableName,
            List columnFilters, boolean dontIncludeKeysInUpdateStatement,
            String schema, String catalog) {
        this.jdbcTemplate = jdbcTemplate;
        this.dbDialect = dbDialect;
        this.tableName = tableName;
        this.schema = StringUtils.isBlank(schema) ? dbDialect.getDefaultSchema() : schema;
        this.catalog = StringUtils.isBlank(catalog) ? null : catalog;
        this.setupColumnFilters(columnFilters, dbDialect);
        this.dontIncludeKeysInUpdateStatement = dontIncludeKeysInUpdateStatement;
        resetMetaData(true);
    }

    public void resetMetaData(boolean useCache) {
        table = dbDialect.getTable(catalog, schema, tableName, useCache);
        allMetaData = new HashMap();
        statementMap = new HashMap();

        if (table != null) {
            for (Column column : table.getColumns()) {
                allMetaData.put(column.getName().trim().toUpperCase(), column);
            }
        }
    }

    private void setupColumnFilters(List pluginFilters, IDbDialect dbDialect) {
        if (pluginFilters != null) {
            for (IColumnFilter columnFilter : pluginFilters) {
                this.columnFilters.add(columnFilter);
            }
        }
        IColumnFilter filter = dbDialect.newDatabaseColumnFilter();
        if (filter != null) {
            this.columnFilters.add(filter);
        }
    }

    public String getTableName() {
        return tableName;
    }

    final public boolean isIgnoreThisTable() {
        return table == null;
    }

    public int insert(IDataLoaderContext ctx, String[] columnValues) {
        StatementBuilder st = getStatementBuilder(ctx, DmlType.INSERT, columnNames);
        return execute(ctx, st, columnValues);
    }

    public int update(IDataLoaderContext ctx, String[] columnValues, String[] keyValues) {
        try {
            StatementBuilder st = null;
            ArrayList changedColumnNameList = new ArrayList();
            ArrayList changedColumnValueList = new ArrayList();
            ArrayList changedColumnMetaList = new ArrayList();
            for (int i = 0; i < columnValues.length; i++) {
                Column column = allMetaData.get(columnNames[i].trim().toUpperCase());
                if (column != null) {
                    if (doesColumnNeedUpdated(ctx, i, column, keyValues, columnValues)) {
                        changedColumnNameList.add(columnNames[i]);
                        changedColumnMetaList.add(column);
                        changedColumnValueList.add(columnValues[i]);
                    }
                }
            }
            if (changedColumnNameList.size() > 0) {
                st = getStatementBuilder(ctx, DmlType.UPDATE, changedColumnNameList
                        .toArray(new String[changedColumnNameList.size()]));
                columnValues = (String[]) changedColumnValueList
                        .toArray(new String[changedColumnValueList.size()]);
                String[] values = (String[]) ArrayUtils.addAll(columnValues, keyValues);
                return execute(ctx, st, values);
            } else {
                // There was no change to apply
                return 1;
            }
        } finally {
            oldData = null;
        }

    }

    final private boolean doesColumnNeedUpdated(IDataLoaderContext ctx, int columnIndex, Column column,
            String[] keyValues, String[] columnValues) {
        boolean needsUpdated = true;
        if (oldData != null) {
            needsUpdated = !StringUtils.equals(columnValues[columnIndex], oldData[columnIndex])
                    || (dbDialect.isLob(column.getTypeCode()) && (dbDialect.needsToSelectLobData() || StringUtils
                            .isBlank(oldData[columnIndex])));
        } else if (dontIncludeKeysInUpdateStatement) {
            // This is in support of creating update statements that don't use
            // the keys in the set portion of the update statement. 

In // oracle (and maybe not only in oracle) if there is no index on // child table on FK column and update is performing on PK on master // table, table lock is acquired on child table. Table lock is taken // not in exclusive mode, but lock contentions is possible. // // @see ParameterConstants#DATA_LOADER_NO_KEYS_IN_UPDATE needsUpdated = !column.isPrimaryKey() || !StringUtils.equals(columnValues[columnIndex], getKeyValue(ctx, column, keyValues)); } return needsUpdated; } protected String getKeyValue(IDataLoaderContext ctx, Column column, String[] keyValues) { int index = ctx.getKeyIndex(column.getName()); if (index >= 0 && keyValues != null && keyValues.length > index) { return keyValues[index]; } else { return null; } } public int delete(IDataLoaderContext ctx, String[] keyValues) { StatementBuilder st = getStatementBuilder(ctx, DmlType.DELETE, columnNames); return execute(ctx, st, keyValues); } public int count(IDataLoaderContext ctx, String[] keyValues) { StatementBuilder st = getStatementBuilder(ctx, DmlType.COUNT, columnNames); Object[] objectValues = dbDialect.getObjectValues(ctx.getBinaryEncoding(), keyValues, st.getKeys()); if (columnFilters != null) { for (IColumnFilter columnFilter : columnFilters) { objectValues = columnFilter.filterColumnsValues(ctx, st.getDmlType(), getTable(), objectValues); } } return jdbcTemplate.queryForInt(st.getSql(), objectValues, st.getTypes()); } final private StatementBuilder getStatementBuilder(IDataLoaderContext ctx, DmlType type, String[] statementColumnNames) { StatementBuilder st = statementMap.get(type); if (st == null) { String[] preFilteredColumnNames = statementColumnNames; if (columnFilters != null) { for (IColumnFilter columnFilter : columnFilters) { statementColumnNames = columnFilter.filterColumnsNames(ctx, type, getTable(), statementColumnNames); } } String tableName = table.getName(); if (!StringUtils.isBlank(schema)) { tableName = schema + "." + tableName; } if (!StringUtils.isBlank(catalog)) { tableName = catalog + "." + tableName; } st = new StatementBuilder(type, tableName, getColumnMetaData(keyNames), getColumnMetaData(statementColumnNames), getColumnMetaData(preFilteredColumnNames), dbDialect .isDateOverrideToTimestamp(), dbDialect.getIdentifierQuoteString()); if (type != DmlType.UPDATE) { statementMap.put(type, st); } } return st; } public Object[] getObjectValues(IDataLoaderContext ctx, String[] values) { return dbDialect.getObjectValues(ctx.getBinaryEncoding(), values, getColumnMetaData(columnNames)); } public Object[] getObjectKeyValues(IDataLoaderContext ctx, String[] values) { return dbDialect.getObjectValues(ctx.getBinaryEncoding(), values, getColumnMetaData(keyNames)); } final private int execute(IDataLoaderContext ctx, StatementBuilder st, String[] values) { Object[] objectValues = dbDialect.getObjectValues(ctx.getBinaryEncoding(), values, st .getMetaData(true)); if (columnFilters != null) { for (IColumnFilter columnFilter : columnFilters) { objectValues = columnFilter.filterColumnsValues(ctx, st.getDmlType(), getTable(), objectValues); } } return jdbcTemplate.update(st.getSql(), new ArgTypePreparedStatementSetter(objectValues, st .getTypes(), dbDialect.getLobHandler())); } public void setKeyNames(String[] keyNames) { this.keyNames = keyNames; clear(); } public void setColumnNames(String[] columnNames) { this.columnNames = columnNames; clear(); } public void setOldData(String[] oldData) { this.oldData = oldData; } final private void clear() { statementMap.clear(); oldData = null; } final private Column[] getColumnMetaData(String[] names) { Column[] columns = new Column[names.length]; for (int i = 0; i < names.length; i++) { columns[i] = allMetaData.get(names[i].trim().toUpperCase()); } return columns; } public String[] getKeyNames() { return keyNames; } public String[] getColumnNames() { return columnNames; } public Table getTable() { return table; } public String[] getOldData() { return oldData; } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy