Package org.apache.isis.runtimes.dflt.objectstores.sql.auto

Source Code of org.apache.isis.runtimes.dflt.objectstores.sql.auto.AutoMapper

/*
*  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.isis.runtimes.dflt.objectstores.sql.auto;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Vector;

import org.apache.log4j.Logger;

import org.apache.isis.applib.Identifier;
import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.commons.debug.DebuggableWithTitle;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.ResolveState;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.adapter.util.InvokeUtils;
import org.apache.isis.core.metamodel.adapter.version.SerialNumberVersion;
import org.apache.isis.core.metamodel.facets.notpersisted.NotPersistedFacet;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.ObjectSpecificationException;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.runtimes.dflt.objectstores.sql.CollectionMapper;
import org.apache.isis.runtimes.dflt.objectstores.sql.DatabaseConnector;
import org.apache.isis.runtimes.dflt.objectstores.sql.Defaults;
import org.apache.isis.runtimes.dflt.objectstores.sql.FieldMappingLookup;
import org.apache.isis.runtimes.dflt.objectstores.sql.IdMapping;
import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMapping;
import org.apache.isis.runtimes.dflt.objectstores.sql.ObjectMappingLookup;
import org.apache.isis.runtimes.dflt.objectstores.sql.Results;
import org.apache.isis.runtimes.dflt.objectstores.sql.Sql;
import org.apache.isis.runtimes.dflt.objectstores.sql.SqlObjectStoreException;
import org.apache.isis.runtimes.dflt.objectstores.sql.TitleMapping;
import org.apache.isis.runtimes.dflt.objectstores.sql.VersionMapping;
import org.apache.isis.runtimes.dflt.objectstores.sql.mapping.FieldMapping;
import org.apache.isis.runtimes.dflt.runtime.persistence.ConcurrencyException;
import org.apache.isis.runtimes.dflt.runtime.persistence.ObjectNotFoundException;
import org.apache.isis.runtimes.dflt.runtime.persistence.PersistorUtil;
import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindByPattern;
import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManager;

public class AutoMapper extends AbstractAutoMapper implements ObjectMapping, DebuggableWithTitle {
    private static final Logger LOG = Logger.getLogger(AutoMapper.class);
    private final IdMapping idMapping;
    private final VersionMapping versionMapping;
    private final TitleMapping titleMapping;
    private final boolean useVersioning;
    final AdapterManager adapterManager;

    public AutoMapper(final String className, final String parameterBase, final FieldMappingLookup lookup, final ObjectMappingLookup objectMapperLookup) {
        super(className, parameterBase, lookup, objectMapperLookup);
        idMapping = lookup.createIdMapping();
        versionMapping = lookup.createVersionMapping();
        titleMapping = lookup.createTitleMapping();

        adapterManager = IsisContext.getPersistenceSession().getAdapterManager();
        useVersioning = Defaults.useVersioning(specification.getShortIdentifier());

        setUpFieldMappers();
    }

    protected VersionMapping getVersionMapping() {
        return versionMapping;
    }

    protected IdMapping getIdMapping() {
        return idMapping;
    }

    @Override
    public void createTables(final DatabaseConnector connection) {
        if (!connection.hasTable(table)) {
            final StringBuffer sql = new StringBuffer();
            sql.append("create table ");
            sql.append(table);
            sql.append(" (");
            idMapping.appendCreateColumnDefinitions(sql);
            sql.append(", ");
            for (final FieldMapping mapping : fieldMappings) {
                mapping.appendColumnDefinitions(sql);
                sql.append(",");
            }
            titleMapping.appendColumnDefinitions(sql);
            sql.append(", ");
            sql.append(versionMapping.appendColumnDefinitions());
            sql.append(")");
            connection.update(sql.toString());
        }
        for (int i = 0; collectionMappers != null && i < collectionMappers.length; i++) {
            if (collectionMappers[i].needsTables(connection)) {
                collectionMappers[i].createTables(connection);
            }
        }
    }

    @Override
    public void createObject(final DatabaseConnector connector, final ObjectAdapter object) {
        final int versionSequence = 1;
        final SerialNumberVersion version = createVersion(versionSequence);

        final StringBuffer sql = new StringBuffer();
        sql.append("insert into " + table + " (");
        idMapping.appendColumnNames(sql);
        sql.append(", ");
        final String columnList = columnList(fieldMappings);
        if (columnList.length() > 0) {
            sql.append(columnList);
            sql.append(", ");
        }
        titleMapping.appendColumnNames(sql);
        sql.append(", ");
        sql.append(versionMapping.insertColumns());
        sql.append(") values (");
        idMapping.appendInsertValues(connector, sql, object);
        sql.append(", ");
        sql.append(values(connector, object));
        titleMapping.appendInsertValues(connector, sql, object);
        sql.append(", ");
        sql.append(versionMapping.insertValues(connector, version));
        sql.append(") ");

        connector.insert(sql.toString());
        object.setOptimisticLock(version);

        for (final CollectionMapper collectionMapper : collectionMappers) {
            collectionMapper.saveInternalCollection(connector, object);
        }
    }

    @Override
    public void destroyObject(final DatabaseConnector connector, final ObjectAdapter object) {
        final StringBuffer sql = new StringBuffer();
        sql.append("delete from " + table + " WHERE ");
        idMapping.appendWhereClause(connector, sql, object.getOid());
        sql.append(" AND ");
        sql.append(versionMapping.whereClause(connector, (SerialNumberVersion) object.getVersion()));
        final int updateCount = connector.update(sql.toString());
        if (updateCount == 0) {
            LOG.info("concurrency conflict object " + this + "; no deletion performed");
            throw new ConcurrencyException("", object.getOid());
        }
    }

    @Override
    public Vector<ObjectAdapter> getInstances(final DatabaseConnector connector, final ObjectSpecification spec) {
        final StringBuffer sql = createSelectStatement();
        final Vector<ObjectAdapter> instances = new Vector<ObjectAdapter>();
        loadInstancesToVector(connector, spec, completeSelectStatement(sql), instances);
        return instances;
    }

    @Override
    public Vector<ObjectAdapter> getInstances(final DatabaseConnector connector, final ObjectSpecification spec, final PersistenceQueryFindByPattern query) {
        final Vector<ObjectAdapter> instances = new Vector<ObjectAdapter>();

        final StringBuffer sql = createSelectStatement();
        int initialLength = 0;

        int foundFields = 0;
        final ObjectAdapter pattern = query.getPattern();

        // for all fields in the query.getPattern, build a SQL select clause for
        // this spec.
        final Object o = pattern.getObject();
        final ObjectSpecification patternSpec = pattern.getSpecification();
        final List<ObjectAssociation> patternAssociations = patternSpec.getAssociations();
        for (final ObjectAssociation patternAssoc : patternAssociations) {
            final Method method;
            final Identifier identifier = patternAssoc.getIdentifier();
            final String memberName = identifier.getMemberName();
            final String methodName = memberName.substring(0, 1).toUpperCase() + memberName.substring(1);

            try {
                if (true) {
                    final ObjectAdapter field = patternAssoc.get(pattern);
                    if (field != null) {
                        final String id = patternAssoc.getId();
                        try {
                            final ObjectAssociation oa = spec.getAssociation(id);
                            final NotPersistedFacet fc = oa.getFacet(NotPersistedFacet.class);
                            if (fc != null) {
                                continue;
                            }
                        } catch (final ObjectSpecificationException e) {
                            // this is OK
                        }

                        if (foundFields == 0) {
                            sql.append(" WHERE ");
                            initialLength = sql.length();
                        }

                        if (sql.length() > initialLength) {
                            sql.append(" AND ");
                        }

                        final FieldMapping fieldMapping = fieldMappingFor(patternAssoc);
                        if (fieldMapping != null) {
                            fieldMapping.appendWhereClause(connector, sql, pattern);
                        } else {
                            // Have to use getXXX method if the fieldMapping is
                            // null..
                            final ObjectSpecification specification = patternAssoc.getSpecification();

                            method = o.getClass().getMethod("get" + methodName, (Class<?>[]) null);
                            final Object res = InvokeUtils.invoke(method, o);

                            if (specification.isValue()) {
                                // If the property (memberName) is a value type,
                                // use the value.
                                final String fieldName = Sql.sqlFieldName(identifier.getMemberName());
                                sql.append(fieldName + "=?");
                                connector.addToQueryValues(res);
                            } else {
                                throw new SqlObjectStoreException("Unhandled combination!");
                            }
                        }
                        foundFields++;
                    }
                }
            } catch (final SecurityException e) {
                LOG.debug(e.getMessage());
            } catch (final NoSuchMethodException e) {
                LOG.info("Unable to invode method: get" + methodName + " in getInstances");
                LOG.debug(e.getMessage());
            }
        }
        // if (foundFields > 0) {
        loadInstancesToVector(connector, spec, completeSelectStatement(sql), instances);
        // }
        return instances;
    }

    @Override
    public Vector<ObjectAdapter> getInstances(final DatabaseConnector connector, final ObjectSpecification spec, final String title) {
        final Vector<ObjectAdapter> instances = new Vector<ObjectAdapter>();

        final StringBuffer sql = createSelectStatement();
        sql.append(" WHERE ");
        titleMapping.appendWhereClause(sql, title);
        loadInstancesToVector(connector, spec, completeSelectStatement(sql), instances);
        return instances;
    }

    @Override
    public ObjectAdapter getObject(final DatabaseConnector connector, final Oid oid, final ObjectSpecification hint) {
        final StringBuffer sql = createSelectStatement();
        sql.append(" WHERE ");
        idMapping.appendWhereClause(connector, sql, oid);
        final Results rs = connector.select(completeSelectStatement(sql));
        if (rs.next()) {
            return loadObject(connector, hint, rs);
        } else {
            throw new ObjectNotFoundException("No object with with " + oid + " in table " + table);
        }
    }

    @Override
    public boolean hasInstances(final DatabaseConnector connector, final ObjectSpecification cls) {
        final String statement = "select count(*) from " + table;
        final int instances = connector.count(statement);
        return instances > 0;
    }

    private StringBuffer createSelectStatement() {
        final StringBuffer sql = new StringBuffer();
        sql.append("select ");
        idMapping.appendColumnNames(sql);
        sql.append(", ");
        final String columnList = columnList(fieldMappings);
        if (columnList.length() > 0) {
            sql.append(columnList);
            sql.append(", ");
        }
        sql.append(versionMapping.insertColumns());
        sql.append(" from " + table);
        return sql;
    } /*
       * if (whereClause != null) { sql.append(" WHERE ");
       * sql.append(whereClause); } else if (whereClause != null) {
       * sql.append(" WHERE "); idMapping.appendWhereClause(sql, oid); }
       */

    private String completeSelectStatement(final StringBuffer sql) {
        sql.append(" order by ");
        idMapping.appendColumnNames(sql);
        return sql.toString();
    }

    protected void loadFields(final ObjectAdapter object, final Results rs) {
        PersistorUtil.start(object, ResolveState.RESOLVING);
        for (final FieldMapping mapping : fieldMappings) {
            mapping.initializeField(object, rs);
        }
        /*
         * for (int i = 0; i < oneToManyProperties.length; i++) { /* Need to set
         * up collection to be a ghost before we access as below
         */
        // CollectionAdapter collection = (CollectionAdapter)
        /*
         * oneToManyProperties[i].get(object); }
         */
        object.setOptimisticLock(versionMapping.getLock(rs));
        PersistorUtil.end(object);
    }

    // KAM
    private void loadCollections(final DatabaseConnector connector, final ObjectAdapter instance) {

        for (final CollectionMapper mapper : collectionMappers) {
            mapper.loadInternalCollection(connector, instance, true);
        }
    }

    private void loadInstancesToVector(final DatabaseConnector connector, final ObjectSpecification cls, final String selectStatment, final Vector<ObjectAdapter> instances) {
        LOG.debug("loading instances from SQL " + table);

        try {
            final Results rs = connector.select(selectStatment);
            final int maxInstances = Defaults.getMaxInstances();
            for (int count = 0; rs.next() && count < maxInstances; count++) {
                final ObjectAdapter instance = loadObject(connector, cls, rs);
                LOG.debug("  instance  " + instance);
                instances.addElement(instance);
            }
            rs.close();
        } catch (final SqlObjectStoreException e) {
            // Invalid SELECT means no object found.. don't worry about it,
            // here.
        }
    }

    private ObjectAdapter loadObject(final DatabaseConnector connector, final ObjectSpecification cls, final Results rs) {
        final Oid oid = idMapping.recreateOid(rs, specification);
        final ObjectAdapter instance = getAdapter(cls, oid);

        if (instance.getResolveState().isValidToChangeTo(ResolveState.RESOLVING)) {
            loadFields(instance, rs);
            loadCollections(connector, instance); // KAM
        }
        return instance;
    }

    @Override
    public void resolve(final DatabaseConnector connector, final ObjectAdapter object) {
        LOG.debug("loading data from SQL " + table + " for " + object);
        final StringBuffer sql = new StringBuffer();
        sql.append("select ");
        sql.append(columnList(fieldMappings));
        sql.append(",");
        sql.append(versionMapping.appendColumnNames());
        sql.append(" from " + table + " WHERE ");
        idMapping.appendWhereClause(connector, sql, object.getOid());

        final Results rs = connector.select(sql.toString());
        if (rs.next()) {
            loadFields(object, rs);
            rs.close();

            for (final CollectionMapper collectionMapper : collectionMappers) {
                collectionMapper.loadInternalCollection(connector, object, true);
            }
        } else {
            rs.close();
            throw new SqlObjectStoreException("Unable to load data from " + table + " with id " + idMapping.primaryKey(object.getOid()));
        }
    }

    @Override
    public void resolveCollection(final DatabaseConnector connector, final ObjectAdapter object, final ObjectAssociation field) {
        if (collectionMappers.length > 0) {
            final DatabaseConnector secondConnector = connector.getConnectionPool().acquire();
            for (final CollectionMapper collectionMapper : collectionMappers) {
                collectionMapper.loadInternalCollection(secondConnector, object, true);
            }
            connector.getConnectionPool().release(secondConnector);
        }
    }

    @Override
    public void startup(final DatabaseConnector connector, final ObjectMappingLookup objectMapperLookup) {
        if (needsTables(connector)) {
            createTables(connector);
        }
    }

    @Override
    public void save(final DatabaseConnector connector, final ObjectAdapter object) {
        final SerialNumberVersion version = (SerialNumberVersion) object.getVersion();
        final long nextSequence;
        if (useVersioning) {
            nextSequence = version.getSequence() + 1;
        } else {
            nextSequence = version.getSequence();
        }

        final StringBuffer sql = new StringBuffer();
        sql.append("UPDATE " + table + " SET ");
        for (final FieldMapping mapping : fieldMappings) {
            mapping.appendUpdateValues(connector, sql, object);
            sql.append(", ");
        }
        sql.append(versionMapping.updateAssigment(connector, nextSequence));
        sql.append(", ");
        titleMapping.appendUpdateAssignment(connector, sql, object);
        sql.append(" WHERE ");
        idMapping.appendWhereClause(connector, sql, object.getOid());
        if (useVersioning) {
            sql.append(" AND ");
            sql.append(versionMapping.whereClause(connector, (SerialNumberVersion) object.getVersion()));
        }

        final int updateCount = connector.update(sql.toString());
        if (updateCount == 0) {
            LOG.info("concurrency conflict object " + this + "; no update performed");
            throw new ConcurrencyException("", object.getOid());
        } else {
            object.setOptimisticLock(createVersion(nextSequence));
        }

        // TODO update collections - change only when needed rather than
        // reinserting from scratch
        for (final CollectionMapper collectionMapper : collectionMappers) {
            collectionMapper.saveInternalCollection(connector, object);
        }
    }

    @Override
    public boolean saveCollection(final DatabaseConnector connection, final ObjectAdapter parent, final String fieldName) {
        int i = 0;
        for (final String collectionFieldName : collectionMapperFields) {
            if (collectionFieldName.equals(fieldName)) {
                final CollectionMapper fieldMapper = collectionMappers[i];
                fieldMapper.saveInternalCollection(connection, parent);
                return true;
            }
            i++;
        }
        return false;
    }

    @Override
    public String toString() {
        return "AutoMapper [table=" + table + ",id=" + idMapping + ",noColumns=" + fieldMappings.size() + ",specification=" + specification.getFullIdentifier() + "]";
    }

    @Override
    public void debugData(final DebugBuilder debug) {
        debug.appendln("ID mapping", idMapping);
        debug.appendln("ID mapping", versionMapping);
        debug.appendln("ID mapping", titleMapping);
        for (final FieldMapping mapping : fieldMappings) {
            mapping.debugData(debug);
        }
        for (final CollectionMapper collectionMapper : collectionMappers) {
            collectionMapper.debugData(debug);
        }

    }

    @Override
    public String debugTitle() {
        return toString();
    }

}
TOP

Related Classes of org.apache.isis.runtimes.dflt.objectstores.sql.auto.AutoMapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.