| /***************************************************************** |
| * 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.derby; |
| |
| import org.apache.cayenne.CayenneRuntimeException; |
| import org.apache.cayenne.access.trans.QualifierTranslator; |
| import org.apache.cayenne.access.trans.QueryAssembler; |
| import org.apache.cayenne.access.types.ByteType; |
| import org.apache.cayenne.access.types.CharType; |
| import org.apache.cayenne.access.types.ExtendedType; |
| import org.apache.cayenne.access.types.ExtendedTypeFactory; |
| import org.apache.cayenne.access.types.ExtendedTypeMap; |
| import org.apache.cayenne.access.types.ShortType; |
| import org.apache.cayenne.configuration.Constants; |
| import org.apache.cayenne.configuration.RuntimeProperties; |
| import org.apache.cayenne.dba.JdbcAdapter; |
| import org.apache.cayenne.dba.PkGenerator; |
| import org.apache.cayenne.dba.QuotingStrategy; |
| import org.apache.cayenne.dba.TypesMapping; |
| import org.apache.cayenne.di.Inject; |
| import org.apache.cayenne.map.DbAttribute; |
| import org.apache.cayenne.map.DbEntity; |
| import org.apache.cayenne.merge.MergerFactory; |
| |
| import java.sql.PreparedStatement; |
| import java.sql.SQLException; |
| import java.sql.Types; |
| import java.util.List; |
| |
| /** |
| * DbAdapter implementation for the <a href="https://db.apache.org/derby/"> Derby RDBMS |
| * </a>. Sample connection settings to use with Derby are shown below. <h3>Embedded</h3> |
| * |
| * <pre> |
| * test-derby.jdbc.url = jdbc:derby:testdb;create=true |
| * test-derby.jdbc.driver = org.apache.derby.jdbc.EmbeddedDriver |
| * </pre> |
| * |
| * <h3>Network Server</h3> |
| * |
| * <pre> |
| * derbynet.jdbc.url = jdbc:derby://localhost/cayenne |
| * derbynet.jdbc.driver = org.apache.derby.jdbc.ClientDriver |
| * derbynet.jdbc.username = someuser |
| * derbynet.jdbc.password = secret; |
| * </pre> |
| */ |
| public class DerbyAdapter extends JdbcAdapter { |
| |
| static final String FOR_BIT_DATA_SUFFIX = " FOR BIT DATA"; |
| |
| public DerbyAdapter( |
| @Inject RuntimeProperties runtimeProperties, |
| @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, |
| @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, |
| @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories) { |
| super( |
| runtimeProperties, |
| defaultExtendedTypes, |
| userExtendedTypes, |
| extendedTypeFactories); |
| setSupportsGeneratedKeys(true); |
| setSupportsBatchUpdates(true); |
| } |
| |
| @Override |
| protected PkGenerator createPkGenerator() { |
| return new DerbyPkGenerator(this); |
| } |
| |
| /** |
| * Installs appropriate ExtendedTypes as converters for passing values between JDBC |
| * and Java layers. |
| */ |
| @Override |
| protected void configureExtendedTypes(ExtendedTypeMap map) { |
| super.configureExtendedTypes(map); |
| |
| // create specially configured CharType handler |
| map.registerType(new CharType(true, false)); |
| |
| // address Derby driver inability to handle java.lang.Short and java.lang.Byte |
| map.registerType(new ShortType(true)); |
| map.registerType(new ByteType(true)); |
| } |
| |
| /** |
| * Appends SQL for column creation to CREATE TABLE buffer. Only change for Derby is |
| * that " NULL" is not supported. |
| * |
| * @since 1.2 |
| */ |
| @Override |
| public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { |
| |
| boolean status; |
| if ((column.getEntity().getDataMap() != null) |
| && column.getEntity().getDataMap().isQuotingSQLIdentifiers()) { |
| status = true; |
| } |
| else { |
| status = false; |
| } |
| QuotingStrategy context = getQuotingStrategy(status); |
| String[] types = externalTypesForJdbcType(column.getType()); |
| if (types == null || types.length == 0) { |
| String entityName = column.getEntity() != null ? ((DbEntity) column |
| .getEntity()).getFullyQualifiedName() : "<null>"; |
| throw new CayenneRuntimeException("Undefined type for attribute '" |
| + entityName |
| + "." |
| + column.getName() |
| + "': " |
| + column.getType()); |
| } |
| |
| String type = types[0]; |
| |
| String length = ""; |
| if (typeSupportsLength(column.getType())) { |
| int len = column.getMaxLength(); |
| int scale = TypesMapping.isDecimal(column.getType()) ? column.getScale() : -1; |
| |
| // sanity check |
| if (scale > len) { |
| scale = -1; |
| } |
| |
| if (len > 0) { |
| length = " (" + len; |
| |
| if (scale >= 0) { |
| length += ", " + scale; |
| } |
| |
| length += ")"; |
| } |
| } |
| |
| // assemble... |
| // note that max length for types like XYZ FOR BIT DATA must be entered in the |
| // middle of type name, e.g. VARCHAR (100) FOR BIT DATA. |
| |
| sqlBuffer.append(context.quoteString(column.getName())); |
| |
| sqlBuffer.append(' '); |
| if (length.length() > 0 && type.endsWith(FOR_BIT_DATA_SUFFIX)) { |
| sqlBuffer.append(type.substring( |
| 0, |
| type.length() - FOR_BIT_DATA_SUFFIX.length())); |
| sqlBuffer.append(length); |
| sqlBuffer.append(FOR_BIT_DATA_SUFFIX); |
| } |
| else { |
| sqlBuffer.append(type).append(length); |
| } |
| |
| if (column.isMandatory()) { |
| sqlBuffer.append(" NOT NULL"); |
| } |
| |
| if (column.isGenerated()) { |
| sqlBuffer.append(" GENERATED BY DEFAULT AS IDENTITY"); |
| } |
| } |
| |
| public boolean typeSupportsLength(int type) { |
| // "BLOB" and "CLOB" type support length. default length is 1M. |
| switch (type) { |
| case Types.BLOB: |
| case Types.CLOB: |
| return true; |
| default: |
| return super.typeSupportsLength(type); |
| } |
| } |
| |
| /** |
| * Returns a trimming translator. |
| */ |
| @Override |
| public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) { |
| QualifierTranslator translator = new DerbyQualifierTranslator( |
| queryAssembler, |
| "RTRIM"); |
| translator.setCaseInsensitive(caseInsensitiveCollations); |
| return translator; |
| } |
| |
| @Override |
| public MergerFactory mergerFactory() { |
| return new DerbyMergerFactory(); |
| } |
| |
| @Override |
| public void bindParameter( |
| PreparedStatement statement, |
| Object object, |
| int pos, |
| int sqlType, |
| int precision) throws SQLException, Exception { |
| |
| if (object == null && sqlType == 0) { |
| statement.setNull(pos, Types.VARCHAR); |
| } |
| else { |
| super.bindParameter(statement, object, pos, sqlType, precision); |
| } |
| } |
| |
| } |