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

org.apache.cayenne.dba.JdbcAdapter Maven / Gradle / Ivy

There is a newer version: 2.0.4
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.dba;

import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.OperationObserver;
import org.apache.cayenne.access.QueryTranslator;
import org.apache.cayenne.access.trans.DeleteTranslator;
import org.apache.cayenne.access.trans.ProcedureTranslator;
import org.apache.cayenne.access.trans.QualifierTranslator;
import org.apache.cayenne.access.trans.QueryAssembler;
import org.apache.cayenne.access.trans.SelectTranslator;
import org.apache.cayenne.access.trans.UpdateTranslator;
import org.apache.cayenne.access.types.BooleanType;
import org.apache.cayenne.access.types.ByteArrayType;
import org.apache.cayenne.access.types.CharType;
import org.apache.cayenne.access.types.ExtendedType;
import org.apache.cayenne.access.types.ExtendedTypeMap;
import org.apache.cayenne.access.types.UtilDateType;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.DerivedDbEntity;
import org.apache.cayenne.query.BatchQuery;
import org.apache.cayenne.query.DeleteQuery;
import org.apache.cayenne.query.ProcedureQuery;
import org.apache.cayenne.query.Query;
import org.apache.cayenne.query.SQLAction;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.query.UpdateQuery;
import org.apache.cayenne.util.ResourceLocator;
import org.apache.cayenne.util.Util;

/**
 * A generic DbAdapter implementation. Can be used as a default adapter or as a superclass
 * of a concrete adapter implementation.
 * 
 * @author Andrei Adamchik
 */
public class JdbcAdapter implements DbAdapter {

    protected PkGenerator pkGenerator;
    protected TypesHandler typesHandler;
    protected ExtendedTypeMap extendedTypes;
    protected boolean supportsBatchUpdates;
    protected boolean supportsFkConstraints;
    protected boolean supportsUniqueConstraints;
    protected boolean supportsGeneratedKeys;

    /**
     * Creates new JdbcAdapter with a set of default parameters.
     */
    public JdbcAdapter() {
        // init defaults
        this.setSupportsBatchUpdates(false);
        this.setSupportsUniqueConstraints(true);
        this.setSupportsFkConstraints(true);

        this.pkGenerator = this.createPkGenerator();
        this.typesHandler = TypesHandler.getHandler(findAdapterResource("/types.xml"));
        this.extendedTypes = new ExtendedTypeMap();
        this.configureExtendedTypes(extendedTypes);
    }

    /**
     * Returns default separator - a semicolon.
     * 
     * @since 1.0.4
     */
    public String getBatchTerminator() {
        return ";";
    }

    /**
     * Locates and returns a named adapter resource. A resource can be an XML file, etc.
     * 

* This implementation is based on the premise that each adapter is located in its own * Java package and all resources are in the same package as well. Resource lookup is * recursive, so that if DbAdapter is a subclass of another adapter, parent adapter * package is searched as a failover. *

* * @since 1.1 */ public URL findAdapterResource(String name) { Class adapterClass = this.getClass(); while (adapterClass != null && JdbcAdapter.class.isAssignableFrom(adapterClass)) { String path = Util.getPackagePath(adapterClass.getName()) + name; URL url = ResourceLocator.findURLInClasspath(path); if (url != null) { return url; } adapterClass = adapterClass.getSuperclass(); } return null; } /** * Installs appropriate ExtendedTypes as converters for passing values between JDBC * and Java layers. Called from default constructor. */ protected void configureExtendedTypes(ExtendedTypeMap map) { // use BooleanType to ensure that returned booleans are an enum of Boolean.TRUE // and Boolean.FALSE map.registerType(new BooleanType()); // Create a default CHAR handler with some generic settings. // Subclasses may need to install their own CharType or reconfigure // this one to work better with the target database. map.registerType(new CharType(false, true)); // enable java.util.Dates as "persistent" values map.registerType(new UtilDateType()); // enable "small" BLOBs map.registerType(new ByteArrayType(false, true)); } /** * Creates and returns a primary key generator. This factory method should be * overriden by JdbcAdapter subclasses to provide custom implementations of * PKGenerator. */ protected PkGenerator createPkGenerator() { return new JdbcPkGenerator(); } /** * Returns primary key generator associated with this DbAdapter. */ public PkGenerator getPkGenerator() { return pkGenerator; } /** * Sets new primary key generator. * * @since 1.1 */ public void setPkGenerator(PkGenerator pkGenerator) { this.pkGenerator = pkGenerator; } /** * @deprecated since 1.2 this method is unneeded as customizations are done via custom * SQLActions. */ public QueryTranslator getQueryTranslator(Query query) throws Exception { Class queryClass = queryTranslatorClass(query); try { QueryTranslator t = (QueryTranslator) queryClass.newInstance(); t.setQuery(query); t.setAdapter(this); return t; } catch (Exception ex) { throw new CayenneRuntimeException("Can't load translator class: " + queryClass); } } /** * Returns a class of the query translator that should be used to translate the query * q to SQL. Exists mainly for the benefit of subclasses that can * override this method providing their own translator. * * @deprecated since 1.2 this method is unneeded as customizations are done via custom * SQLActions. */ protected Class queryTranslatorClass(Query q) { if (q == null) { throw new NullPointerException("Null query."); } else if (q instanceof SelectQuery) { return SelectTranslator.class; } else if (q instanceof UpdateQuery) { return UpdateTranslator.class; } else if (q instanceof org.apache.cayenne.query.InsertQuery) { return org.apache.cayenne.access.trans.InsertTranslator.class; } else if (q instanceof DeleteQuery) { return DeleteTranslator.class; } else if (q instanceof ProcedureQuery) { return ProcedureTranslator.class; } else { throw new CayenneRuntimeException("Unrecognized query class..." + q.getClass().getName()); } } /** * Returns true. */ public boolean supportsFkConstraints() { return supportsFkConstraints; } /** * @since 1.1 */ public void setSupportsFkConstraints(boolean flag) { this.supportsFkConstraints = flag; } /** * Returns true. * * @since 1.1 */ public boolean supportsUniqueConstraints() { return supportsUniqueConstraints; } /** * @since 1.1 */ public void setSupportsUniqueConstraints(boolean flag) { this.supportsUniqueConstraints = flag; } /** * Returns a SQL string to drop a table corresponding to ent DbEntity. */ public String dropTable(DbEntity ent) { return "DROP TABLE " + ent.getFullyQualifiedName(); } /** * Returns a SQL string that can be used to create database table corresponding to * ent parameter. */ public String createTable(DbEntity entity) { if (entity instanceof DerivedDbEntity) { throw new CayenneRuntimeException("Can't create table for derived DbEntity '" + entity.getName() + "'."); } StringBuffer sqlBuffer = new StringBuffer(); sqlBuffer.append("CREATE TABLE ").append(entity.getFullyQualifiedName()).append( " ("); // columns Iterator it = entity.getAttributes().iterator(); if (it.hasNext()) { boolean first = true; while (it.hasNext()) { if (first) { first = false; } else { sqlBuffer.append(", "); } DbAttribute column = (DbAttribute) it.next(); // attribute may not be fully valid, do a simple check if (column.getType() == TypesMapping.NOT_DEFINED) { throw new CayenneRuntimeException("Undefined type for attribute '" + entity.getFullyQualifiedName() + "." + column.getName() + "'."); } createTableAppendColumn(sqlBuffer, column); } createTableAppendPKClause(sqlBuffer, entity); } sqlBuffer.append(')'); return sqlBuffer.toString(); } /** * @since 1.2 */ protected void createTableAppendPKClause(StringBuffer sqlBuffer, DbEntity entity) { Iterator pkit = entity.getPrimaryKey().iterator(); if (pkit.hasNext()) { sqlBuffer.append(", PRIMARY KEY ("); boolean firstPk = true; while (pkit.hasNext()) { if (firstPk) firstPk = false; else sqlBuffer.append(", "); DbAttribute at = (DbAttribute) pkit.next(); sqlBuffer.append(at.getName()); } sqlBuffer.append(')'); } } /** * Appends SQL for column creation to CREATE TABLE buffer. * * @since 1.2 */ protected void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { String[] types = externalTypesForJdbcType(column.getType()); if (types == null || types.length == 0) { String entityName = column.getEntity() != null ? ((DbEntity) column .getEntity()).getFullyQualifiedName() : ""; throw new CayenneRuntimeException("Undefined type for attribute '" + entityName + "." + column.getName() + "': " + column.getType()); } String type = types[0]; sqlBuffer.append(column.getName()).append(' ').append(type); // append size and precision (if applicable) if (TypesMapping.supportsLength(column.getType())) { int len = column.getMaxLength(); int prec = TypesMapping.isDecimal(column.getType()) ? column.getPrecision() : -1; // sanity check if (prec > len) { prec = -1; } if (len > 0) { sqlBuffer.append('(').append(len); if (prec >= 0) { sqlBuffer.append(", ").append(prec); } sqlBuffer.append(')'); } } sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL"); } /** * Returns a DDL string to create a unique constraint over a set of columns. * * @since 1.1 */ public String createUniqueConstraint(DbEntity source, Collection columns) { if (columns == null || columns.isEmpty()) { throw new CayenneRuntimeException( "Can't create UNIQUE constraint - no columns specified."); } StringBuffer buf = new StringBuffer(); buf.append("ALTER TABLE ").append(source.getFullyQualifiedName()).append( " ADD UNIQUE ("); Iterator it = columns.iterator(); DbAttribute first = (DbAttribute) it.next(); buf.append(first.getName()); while (it.hasNext()) { DbAttribute next = (DbAttribute) it.next(); buf.append(", "); buf.append(next.getName()); } buf.append(")"); return buf.toString(); } /** * Returns a SQL string that can be used to create a foreign key constraint for the * relationship. */ public String createFkConstraint(DbRelationship rel) { StringBuffer buf = new StringBuffer(); StringBuffer refBuf = new StringBuffer(); buf.append("ALTER TABLE ").append( ((DbEntity) rel.getSourceEntity()).getFullyQualifiedName()).append( " ADD FOREIGN KEY ("); Iterator jit = rel.getJoins().iterator(); boolean first = true; while (jit.hasNext()) { DbJoin join = (DbJoin) jit.next(); if (!first) { buf.append(", "); refBuf.append(", "); } else first = false; buf.append(join.getSourceName()); refBuf.append(join.getTargetName()); } buf .append(") REFERENCES ") .append(((DbEntity) rel.getTargetEntity()).getFullyQualifiedName()) .append(" (") .append(refBuf.toString()) .append(')'); return buf.toString(); } public String[] externalTypesForJdbcType(int type) { return typesHandler.externalTypesForJdbcType(type); } public ExtendedTypeMap getExtendedTypes() { return extendedTypes; } public DbAttribute buildAttribute( String name, String typeName, int type, int size, int precision, boolean allowNulls) { DbAttribute attr = new DbAttribute(); attr.setName(name); attr.setType(type); attr.setMandatory(!allowNulls); if (size >= 0) { attr.setMaxLength(size); } if (precision >= 0) { attr.setPrecision(precision); } return attr; } public String tableTypeForTable() { return "TABLE"; } public String tableTypeForView() { return "VIEW"; } /** * Creates and returns a default implementation of a qualifier translator. */ public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) { return new QualifierTranslator(queryAssembler); } /** * Creates an instance of DataNode class. * * @deprecated since 1.2 this method is not used as node behavior customization is * done via SQLActionVisitor. */ public DataNode createDataNode(String name) { DataNode node = new DataNode(name); node.setAdapter(this); return node; } /** * Uses JdbcActionBuilder to create the right action. * * @since 1.2 */ public SQLAction getAction(Query query, DataNode node) { return query .createSQLAction(new JdbcActionBuilder(this, node.getEntityResolver())); } public void bindParameter( PreparedStatement statement, Object object, int pos, int sqlType, int precision) throws SQLException, Exception { if (object == null) { statement.setNull(pos, sqlType); } else { ExtendedType typeProcessor = getExtendedTypes().getRegisteredType( object.getClass()); typeProcessor.setJdbcObject(statement, object, pos, sqlType, precision); } } public boolean supportsBatchUpdates() { return this.supportsBatchUpdates; } public void setSupportsBatchUpdates(boolean flag) { this.supportsBatchUpdates = flag; } /** * @since 1.2 */ public boolean supportsGeneratedKeys() { return supportsGeneratedKeys; } /** * @since 1.2 */ public void setSupportsGeneratedKeys(boolean flag) { this.supportsGeneratedKeys = flag; } /** * Always returns true, letting DataNode to handle the query. * * @deprecated Since 1.2 this method is obsolete and is ignored across Cayenne. */ public boolean shouldRunBatchQuery( DataNode node, Connection con, BatchQuery query, OperationObserver delegate) throws SQLException, Exception { return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy