blob: 71122da2309b6daf94fa37c7ad7ee3dffacf2bc2 [file] [log] [blame]
/*
* 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);
}
}