| /* |
| * 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.openjpa.persistence.jdbc; |
| |
| import org.apache.openjpa.jdbc.identifier.DBIdentifier; |
| import org.apache.openjpa.jdbc.identifier.Normalizer; |
| import org.apache.openjpa.jdbc.meta.ClassMapping; |
| import org.apache.openjpa.jdbc.meta.Discriminator; |
| import org.apache.openjpa.jdbc.meta.FieldMapping; |
| import org.apache.openjpa.jdbc.meta.MappingDefaultsImpl; |
| import org.apache.openjpa.jdbc.meta.ValueMapping; |
| import org.apache.openjpa.jdbc.meta.ValueMappingImpl; |
| import org.apache.openjpa.jdbc.meta.Version; |
| import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.MultiColumnVersionStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.NoneDiscriminatorStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.NoneVersionStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.NumberVersionStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.SubclassJoinDiscriminatorStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.ValueMapDiscriminatorStrategy; |
| import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy; |
| import org.apache.openjpa.jdbc.schema.Column; |
| import org.apache.openjpa.jdbc.schema.Schema; |
| import org.apache.openjpa.jdbc.schema.Table; |
| import org.apache.openjpa.jdbc.sql.JoinSyntaxes; |
| import org.apache.openjpa.lib.util.ClassUtil; |
| import org.apache.openjpa.meta.FieldMetaData; |
| import org.apache.openjpa.meta.JavaTypes; |
| |
| /** |
| * Supplies default mapping information in accordance with JPA spec. |
| * |
| * @author Steve Kim |
| * @author Abe White |
| */ |
| public class PersistenceMappingDefaults |
| extends MappingDefaultsImpl { |
| |
| private boolean _prependFieldNameToJoinTableInverseJoinColumns = true; |
| |
| public PersistenceMappingDefaults() { |
| setDefaultMissingInfo(true); |
| setStoreEnumOrdinal(true); |
| setOrderLists(false); |
| setAddNullIndicator(false); |
| setDiscriminatorColumnName("DTYPE"); |
| } |
| |
| /** |
| * Whether to prepend the field name to the default name of inverse join |
| * columns within join tables. Defaults to true per spec, but set to false |
| * for compatibility with older versions of OpenJPA. |
| */ |
| public boolean getPrependFieldNameToJoinTableInverseJoinColumns() { |
| return _prependFieldNameToJoinTableInverseJoinColumns; |
| } |
| |
| /** |
| * Whether to prepend the field name to the default name of inverse join |
| * columns within join tables. Defaults to true per spec, but set to false |
| * for compatibility with older versions of OpenJPA. |
| */ |
| public void setPrependFieldNameToJoinTableInverseJoinColumns(boolean val) { |
| _prependFieldNameToJoinTableInverseJoinColumns = val; |
| } |
| |
| @Override |
| public Object getStrategy(Version vers, boolean adapt) { |
| Object strat = super.getStrategy(vers, adapt); |
| ClassMapping cls = vers.getClassMapping(); |
| if (strat != null || cls.getJoinablePCSuperclassMapping() != null |
| || cls.getVersionField() != null) |
| return strat; |
| |
| int nColumn = vers.getMappingInfo().getColumns().size(); |
| switch (nColumn) { |
| case 0 : return NoneVersionStrategy.getInstance(); |
| case 1 : return new NumberVersionStrategy(); |
| default: return new MultiColumnVersionStrategy(); |
| } |
| } |
| |
| @Override |
| public Object getStrategy(Discriminator disc, boolean adapt) { |
| Object strat = super.getStrategy(disc, adapt); |
| ClassMapping cls = disc.getClassMapping(); |
| if (strat != null || cls.getJoinablePCSuperclassMapping() != null |
| || disc.getMappingInfo().getValue() != null) |
| return strat; |
| |
| // don't use a column-based discriminator approach unless user has set |
| // a column explicitly or is using flat inheritance explicitly |
| if (!disc.getMappingInfo().getColumns().isEmpty()) |
| return new ValueMapDiscriminatorStrategy(); |
| |
| ClassMapping base = cls; |
| while (base.getMappingInfo().getHierarchyStrategy() == null |
| && base.getPCSuperclassMapping() != null) |
| base = base.getPCSuperclassMapping(); |
| |
| strat = base.getMappingInfo().getHierarchyStrategy(); |
| if (FlatClassStrategy.ALIAS.equals(strat)) |
| return new ValueMapDiscriminatorStrategy(); |
| if (VerticalClassStrategy.ALIAS.equals(strat) |
| && dict.joinSyntax != JoinSyntaxes.SYNTAX_TRADITIONAL) |
| return new SubclassJoinDiscriminatorStrategy(); |
| return NoneDiscriminatorStrategy.getInstance(); |
| } |
| |
| @Override |
| public String getTableName(ClassMapping cls, Schema schema) { |
| if (cls.getTypeAlias() != null) |
| return cls.getTypeAlias(); |
| return ClassUtil.getClassName(cls.getDescribedType()).replace('$', '_'); |
| } |
| |
| @Override |
| public String getTableName(FieldMapping fm, Schema schema) { |
| return getTableIdentifier(fm, schema).getName(); |
| } |
| |
| @Override |
| public DBIdentifier getTableIdentifier(FieldMapping fm, Schema schema) { |
| // base name is table of defining type + '_' |
| ClassMapping clm = fm.getDefiningMapping(); |
| Table table = getTable(clm); |
| |
| DBIdentifier sName = DBIdentifier.NULL; |
| if (fm.isElementCollection()) |
| sName = DBIdentifier.newTable(clm.getTypeAlias()); |
| else |
| sName = table.getIdentifier(); |
| |
| // if this is an assocation table, spec says to suffix with table of |
| // the related type. spec doesn't cover other cases; we're going to |
| // suffix with the field name |
| ClassMapping rel = fm.getElementMapping().getTypeMapping(); |
| boolean assoc = rel != null && rel.getTable() != null |
| && fm.getTypeCode() != JavaTypes.MAP; |
| DBIdentifier sName2 = DBIdentifier.NULL; |
| if (assoc) { |
| sName2 = rel.getTable().getIdentifier(); |
| } |
| else { |
| sName2 = DBIdentifier.newTable(fm.getName().replace('$', '_')); |
| } |
| |
| sName = DBIdentifier.combine(sName, sName2.getName()); |
| |
| return sName; |
| } |
| |
| private Table getTable(ClassMapping clm) { |
| Table table = clm.getTable(); |
| if (table == null) { |
| ValueMappingImpl value = |
| (ValueMappingImpl)clm.getEmbeddingMetaData(); |
| if (value == null) |
| return table; |
| FieldMetaData field = value.getFieldMetaData(); |
| clm = (ClassMapping)field.getDefiningMetaData(); |
| return getTable(clm); |
| } |
| return table; |
| } |
| |
| @Override |
| public void populateJoinColumn(FieldMapping fm, Table local, Table foreign, |
| Column col, Object target, int pos, int cols) { |
| // only use spec defaults with column targets |
| if (!(target instanceof Column)) |
| return; |
| |
| // if this is a bidi relation, prefix with inverse field name, else |
| // prefix with owning entity name |
| FieldMapping[] inverses = fm.getInverseMappings(); |
| DBIdentifier sName = DBIdentifier.NULL; |
| if (inverses.length > 0) |
| sName = DBIdentifier.newColumn(inverses[0].getName()); |
| else |
| sName = DBIdentifier.newColumn(fm.getDefiningMapping().getTypeAlias()); |
| DBIdentifier targetName = ((Column) target).getIdentifier(); |
| DBIdentifier tempName = DBIdentifier.NULL; |
| if ((sName.length() + targetName.length()) >= dict.maxColumnNameLength) |
| tempName = DBIdentifier.truncate(sName, dict.maxColumnNameLength |
| - targetName.length() - 1); |
| // suffix with '_' + target column |
| if (DBIdentifier.isNull(tempName)) |
| tempName = sName; |
| sName = DBIdentifier.combine(tempName, targetName.getName()); |
| sName = dict.getValidColumnName(sName, foreign); |
| col.setIdentifier(sName); |
| } |
| |
| @Override |
| public void populateForeignKeyColumn(ValueMapping vm, String name, |
| Table local, Table foreign, Column col, Object target, boolean inverse, |
| int pos, int cols) { |
| populateForeignKeyColumn(vm, DBIdentifier.newColumn(name), local, |
| foreign, col, target, inverse, pos, cols); |
| } |
| |
| @Override |
| public void populateForeignKeyColumn(ValueMapping vm, DBIdentifier sName, |
| Table local, Table foreign, Column col, Object target, boolean inverse, |
| int pos, int cols) { |
| boolean elem = vm == vm.getFieldMapping().getElement() |
| && vm.getFieldMapping().getTypeCode() != JavaTypes.MAP; |
| |
| // if this is a non-inverse collection element key, it must be in |
| // a join table: if we're not prepending the field name, leave the |
| // default |
| if (!_prependFieldNameToJoinTableInverseJoinColumns && !inverse && elem) |
| return; |
| |
| // otherwise jpa always uses <field>_<pkcol> for column name, even |
| // when only one col |
| if (target instanceof Column) { |
| if (DBIdentifier.isNull(sName)) { |
| sName = col.getIdentifier(); |
| } else { |
| if (elem) |
| sName = DBIdentifier.newColumn(vm.getFieldMapping().getName()); |
| if (isRemoveHungarianNotation()) |
| sName = DBIdentifier.newColumn(Normalizer.removeHungarianNotation(sName.getName())); |
| sName = DBIdentifier.combine(sName, ((Column)target).getIdentifier().getName()); |
| |
| // No need to check for uniqueness. |
| sName = dict.getValidColumnName(sName, local, false); |
| } |
| col.setIdentifier(sName); |
| } |
| } |
| |
| @Override |
| public void populateColumns(Version vers, Table table, Column[] cols) { |
| // check for version field and use its name as column name |
| FieldMapping fm = vers.getClassMapping().getVersionFieldMapping(); |
| if (fm != null && cols.length == 1) |
| cols[0].setIdentifier(DBIdentifier.newColumn(fm.getName())); |
| else |
| super.populateColumns(vers, table, cols); |
| } |
| } |