OPENJPA-962 Committing code and tests contributed by Dianne Richards

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@774860 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jeremy Bauer 2009-05-14 18:19:34 +00:00
parent df96cc6129
commit e1ccb46414
11 changed files with 758 additions and 100 deletions

View File

@ -366,8 +366,18 @@ public class TableJDBCSeq
* Creates the object-level representation of the sequence table. * Creates the object-level representation of the sequence table.
*/ */
private void buildTable() { private void buildTable() {
String tableName = Strings.getClassName(_table); String tableName = null;
String schemaName = Strings.getPackageName(_table); String schemaName = "";
if (StringUtils.contains(_table,'.')) {
String[] tableParts = StringUtils.split(_table, '.');
// TODO: do we need to check for length? Could we have xxx. or .xxx?
schemaName = tableParts[0];
tableName = tableParts[1];
}
else {
tableName = _table;
}
if (schemaName.length() == 0) if (schemaName.length() == 0)
schemaName = Schemas.getNewTableSchema(_conf); schemaName = Schemas.getNewTableSchema(_conf);

View File

@ -24,6 +24,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.jdbc.conf.JDBCSeqValue; import org.apache.openjpa.jdbc.conf.JDBCSeqValue;
import org.apache.openjpa.jdbc.kernel.ClassTableJDBCSeq; import org.apache.openjpa.jdbc.kernel.ClassTableJDBCSeq;
import org.apache.openjpa.jdbc.kernel.TableJDBCSeq; import org.apache.openjpa.jdbc.kernel.TableJDBCSeq;
@ -159,10 +160,37 @@ public class SequenceMapping
protected void addStandardProperties(StringBuffer props) { protected void addStandardProperties(StringBuffer props) {
super.addStandardProperties(props); super.addStandardProperties(props);
appendProperty(props, PROP_TABLE, _table); // Quotes are conditionally added to the following because the props
appendProperty(props, PROP_SEQUENCE_COL, _sequenceColumn); // are eventually passed to the Configurations.parseProperties()
appendProperty(props, PROP_PK_COL, _primaryKeyColumn); // method, which strips off quotes. This is a problem when these
appendProperty(props, PROP_PK_VALUE, _primaryKeyValue); // properties are intentionally delimited with quotes. So, an extra
// set preserves the intended ones. While this is an ugly solution,
// it's less ugly than other ones.
String table = _table;
if (table != null && table.startsWith("\"")
&& table.endsWith("\"")) {
table = "\"" + table + "\"";
}
String sequenceColumn = _sequenceColumn;
if (sequenceColumn != null && sequenceColumn.startsWith("\"")
&& sequenceColumn.endsWith("\"")) {
sequenceColumn = "\"" + sequenceColumn + "\"";
}
String primaryKeyColumn = _primaryKeyColumn;
if (primaryKeyColumn !=null && primaryKeyColumn.startsWith("\"")
&& primaryKeyColumn.endsWith("\"")) {
primaryKeyColumn = "\"" + primaryKeyColumn + "\"";
}
String primaryKeyValue = _primaryKeyValue;
if (primaryKeyValue != null && primaryKeyValue.startsWith("\"")
&& primaryKeyValue.endsWith("\"")) {
primaryKeyValue = "\"" + primaryKeyValue + "\"";
}
appendProperty(props, PROP_TABLE, table);
appendProperty(props, PROP_SEQUENCE_COL, sequenceColumn);
appendProperty(props, PROP_PK_COL, primaryKeyColumn);
appendProperty(props, PROP_PK_VALUE, primaryKeyValue);
// Array of unique column names are passed to configuration // Array of unique column names are passed to configuration
// as a single string "x|y|z". The configurable (TableJDBCSeq) must // as a single string "x|y|z". The configurable (TableJDBCSeq) must
// parse it back. // parse it back.

View File

@ -488,9 +488,17 @@ public class SchemaGenerator {
Table table; Table table;
String tableSchema; String tableSchema;
for (int i = 0; cols != null && i < cols.length; i++) { for (int i = 0; cols != null && i < cols.length; i++) {
tableName = cols[i].getTableName(); // TODO: Is this where we should handle this?
tableSchema = StringUtils.trimToNull(cols[i].getSchemaName()); if (tableName == null || tableName.equals("%")) {
tableName = cols[i].getTableName();
}
if (schemaName == null) {
tableSchema = StringUtils.trimToNull(cols[i].getSchemaName());
}
else {
tableSchema = schemaName;
}
// ignore special tables // ignore special tables
if (!_openjpaTables && if (!_openjpaTables &&
(tableName.toUpperCase().startsWith("OPENJPA_") (tableName.toUpperCase().startsWith("OPENJPA_")

View File

@ -470,6 +470,7 @@ public class SchemaTool {
Table dbTable; Table dbTable;
Column[] cols; Column[] cols;
Column col; Column col;
String delim = _dict.getDelimiter();
for (int i = 0; i < schemas.length; i++) { for (int i = 0; i < schemas.length; i++) {
tabs = schemas[i].getTables(); tabs = schemas[i].getTables();
for (int j = 0; j < tabs.length; j++) { for (int j = 0; j < tabs.length; j++) {
@ -477,14 +478,22 @@ public class SchemaTool {
dbTable = db.findTable(schemas[i], tabs[j].getFullName()); dbTable = db.findTable(schemas[i], tabs[j].getFullName());
for (int k = 0; k < cols.length; k++) { for (int k = 0; k < cols.length; k++) {
if (dbTable != null) { if (dbTable != null) {
col = dbTable.getColumn(cols[k].getName()); String colName = cols[k].getName();
boolean delimCol = false;
if (colName.startsWith(delim)
&& colName.endsWith(delim)) {
colName = colName.substring(1, colName.length()-1);
delimCol = true;
}
col = dbTable.getColumn(colName);
if (col == null) { if (col == null) {
if (addColumn(cols[k])) if (addColumn(cols[k]))
dbTable.importColumn(cols[k]); dbTable.importColumn(cols[k]);
else else
_log.warn(_loc.get("add-col", cols[k], _log.warn(_loc.get("add-col", cols[k],
tabs[j])); tabs[j]));
} else if (!cols[k].equalsColumn(col)) { // TODO: Find a way to compare these with delimCol
} else if (!delimCol && !cols[k].equalsColumn(col)) {
_log.warn(_loc.get("bad-col", new Object[]{ _log.warn(_loc.get("bad-col", new Object[]{
col, dbTable, col.getDescription(), col, dbTable, col.getDescription(),
cols[k].getDescription() })); cols[k].getDescription() }));

View File

@ -24,6 +24,7 @@ import java.sql.DatabaseMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -64,7 +65,7 @@ public class DB2Dictionary
public static final int db2ZOSV8xOrLater = 3; public static final int db2ZOSV8xOrLater = 3;
public static final int db2UDBV82OrLater = 4; public static final int db2UDBV82OrLater = 4;
public static final int db2ISeriesV5R4OrLater = 5; public static final int db2ISeriesV5R4OrLater = 5;
protected static final String forUpdate = "FOR UPDATE"; protected static final String forUpdate = "FOR UPDATE";
protected static final String withURClause = "WITH UR"; protected static final String withURClause = "WITH UR";
protected static final String withCSClause = "WITH CS"; protected static final String withCSClause = "WITH CS";
protected static final String withRSClause = "WITH RS"; protected static final String withRSClause = "WITH RS";
@ -82,6 +83,9 @@ public class DB2Dictionary
protected int min = 0; protected int min = 0;
private int defaultBatchLimit = 100; private int defaultBatchLimit = 100;
private EnumSet<DBIdentifiers> unsupportedDelimitedIds =
EnumSet.of(DBIdentifiers.COLUMN_COLUMN_DEFINITION);
public DB2Dictionary() { public DB2Dictionary() {
platform = "DB2"; platform = "DB2";
@ -244,9 +248,9 @@ public class DB2Dictionary
} }
public void connectedConfiguration(Connection conn) throws SQLException { public void connectedConfiguration(Connection conn) throws SQLException {
super.connectedConfiguration(conn); super.connectedConfiguration(conn);
DatabaseMetaData metaData = conn.getMetaData(); DatabaseMetaData metaData = conn.getMetaData();
String driverName = metaData.getDriverName(); String driverName = metaData.getDriverName();
if (driverName != null && driverName.startsWith("IBM DB2")) if (driverName != null && driverName.startsWith("IBM DB2"))
driverVendor = VENDOR_IBM; driverVendor = VENDOR_IBM;
@ -267,21 +271,21 @@ public class DB2Dictionary
db2ServerType = db2ISeriesV5R4OrLater; db2ServerType = db2ISeriesV5R4OrLater;
} }
if (db2ServerType == 0) { if (db2ServerType == 0) {
if (isJDBC3) { if (isJDBC3) {
maj = metaData.getDatabaseMajorVersion(); maj = metaData.getDatabaseMajorVersion();
min = metaData.getDatabaseMinorVersion(); min = metaData.getDatabaseMinorVersion();
} }
else else
getProductVersionMajorMinor(); getProductVersionMajorMinor();
// Determine the type of DB2 database for ZOS & UDB // Determine the type of DB2 database for ZOS & UDB
if (isDB2UDBV81OrEarlier()) if (isDB2UDBV81OrEarlier())
db2ServerType = db2UDBV81OrEarlier; db2ServerType = db2UDBV81OrEarlier;
else if (isDB2ZOSV8xOrLater()) else if (isDB2ZOSV8xOrLater())
db2ServerType = db2ZOSV8xOrLater; db2ServerType = db2ZOSV8xOrLater;
else if (isDB2UDBV82OrLater()) else if (isDB2UDBV82OrLater())
db2ServerType = db2UDBV82OrLater; db2ServerType = db2UDBV82OrLater;
} }
// verify that database product is supported // verify that database product is supported
@ -289,14 +293,14 @@ public class DB2Dictionary
throw new UnsupportedException(_loc.get("db-not-supported", throw new UnsupportedException(_loc.get("db-not-supported",
new Object[] {databaseProductName, databaseProductVersion })); new Object[] {databaseProductName, databaseProductVersion }));
if (maj >= 9 || (maj == 8 && min >= 2)) { if (maj >= 9 || (maj == 8 && min >= 2)) {
supportsLockingWithMultipleTables = true; supportsLockingWithMultipleTables = true;
supportsLockingWithInnerJoin = true; supportsLockingWithInnerJoin = true;
supportsLockingWithOuterJoin = true; supportsLockingWithOuterJoin = true;
forUpdateClause = "WITH RR USE AND KEEP UPDATE LOCKS"; forUpdateClause = "WITH RR USE AND KEEP UPDATE LOCKS";
if (maj >=9) if (maj >=9)
supportsXMLColumn = true; supportsXMLColumn = true;
} }
// platform specific settings // platform specific settings
switch (db2ServerType) { switch (db2ServerType) {
@ -843,10 +847,10 @@ public class DB2Dictionary
} }
String nullSafe(String s) { String nullSafe(String s) {
return s == null ? "" : s; return s == null ? "" : s;
} }
@Override @Override
protected Boolean matchErrorState(int subtype, Set<String> errorStates, protected Boolean matchErrorState(int subtype, Set<String> errorStates,
SQLException ex) { SQLException ex) {
Boolean recoverable = null; Boolean recoverable = null;
@ -864,4 +868,17 @@ public class DB2Dictionary
} }
return recoverable; return recoverable;
} }
/**
* @return the unsupportedDelimitedIds
*/
@Override
protected EnumSet<DBIdentifiers> getUnsupportedDelimitedIds() {
return unsupportedDelimitedIds;
}
@Override
protected void setDelimitedCase(DatabaseMetaData metaData) {
delimitedCase = SCHEMA_CASE_PRESERVE;
}
} }

View File

@ -53,6 +53,7 @@ import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -348,6 +349,36 @@ public class DBDictionary
protected final Set systemTableSet = new HashSet(); protected final Set systemTableSet = new HashSet();
protected final Set fixedSizeTypeNameSet = new HashSet(); protected final Set fixedSizeTypeNameSet = new HashSet();
protected final Set typeModifierSet = new HashSet(); protected final Set typeModifierSet = new HashSet();
private boolean delimitIds = false;
protected boolean supportsDelimitedIds = false;
protected String delimiter = "\"";
// Assume mixed case by default.
protected String delimitedCase = SCHEMA_CASE_PRESERVE;
// TODO: complete the list
public static enum DBIdentifiers {
TABLE_NAME,
TABLE_SCHEMA,
TABLE_CATALOG,
SECONDARY_TABLE_NAME,
SECONDARY_TABLE_SCHEMA,
SECONDARY_TABLE_CATALOG,
TABLE_GEN_TABLE,
TABLE_GEN_SCHEMA,
TABLE_GEN_PK_COLUMN,
TABLE_GEN_VALUE_COLUMN,
COLUMN_NAME,
COLUMN_COLUMN_DEFINITION,
COLUMN_TABLE
}
// TODO: describe; maybe make private
protected EnumSet<DBIdentifiers> unsupportedDelimitedIds =
EnumSet.noneOf(DBIdentifiers.class);
// TODO
// Should this be EnumSet.<DBIdentifiers>noneOf(....)
// or EnumSet<DBIdentifiers>.noneOf....?
/** /**
* If a native query begins with any of the values found here then it will * If a native query begins with any of the values found here then it will
@ -418,6 +449,10 @@ public class DBDictionary
conn.getAutoCommit(), conn.getHoldability(), conn.getAutoCommit(), conn.getHoldability(),
conn.getTransactionIsolation()})); conn.getTransactionIsolation()}));
} }
// While we have the metaData, set some values from it
setSupportsDelimitedIds(metaData);
setDelimitedCase(metaData);
} }
connected = true; connected = true;
} }
@ -2972,7 +3007,18 @@ public class DBDictionary
name = name.substring(1); name = name.substring(1);
String tableName = table.getName(); String tableName = table.getName();
int len = Math.min(tableName.length(), 7); int len = Math.min(tableName.length(), 7);
name = "I_" + shorten(tableName, len) + "_" + name; String shortTableName = shorten(tableName, len);
String delim = getDelimiter();
if (shortTableName.startsWith(delim)
&& shortTableName.endsWith(delim)) {
name = delim + "I_"
+ shortTableName.substring(1, shortTableName.length() - 1)
+ "_" + name + delim;
}
else {
name = "I_" + shortTableName + "_" + name;
}
return makeNameValid(name, table.getSchema().getSchemaGroup(), return makeNameValid(name, table.getSchema().getSchemaGroup(),
maxIndexNameLength, NAME_ANY); maxIndexNameLength, NAME_ANY);
} }
@ -3052,17 +3098,26 @@ public class DBDictionary
* '1', etc. * '1', etc.
* Note that the given max len may be 0 if the database metadata is * Note that the given max len may be 0 if the database metadata is
* incomplete. * incomplete.
*
* Note: If the name is delimited, make sure the ending delimiter is
* not stripped off.
*/ */
protected String makeNameValid(String name, NameSet set, int maxLen, protected String makeNameValid(String name, NameSet set, int maxLen,
int nameType, boolean checkForUniqueness) { int nameType, boolean checkForUniqueness) {
boolean delimited = false;
String delimiter = getDelimiter();
if (name.startsWith(delimiter) && name.endsWith(delimiter)) {
delimited = true;
}
if (maxLen < 1) if (maxLen < 1)
maxLen = 255; maxLen = 255;
if (name.length() > maxLen) if (name.length() > maxLen)
name = name.substring(0, maxLen); name = removeEndingChars(name, name.length() - maxLen,
delimited, delimiter);
if (reservedWordSet.contains(name.toUpperCase())) { if (reservedWordSet.contains(name.toUpperCase())) {
if (name.length() == maxLen) if (name.length() == maxLen)
name = name.substring(0, name.length() - 1); name = removeEndingChars(name, 1, delimited, delimiter);
name += "0"; name = addCharsToEnd(name, "0", delimited, delimiter);
} }
// now make sure the name is unique // now make sure the name is unique
@ -3088,16 +3143,65 @@ public class DBDictionary
// a single char for the version is probably enough, but might // a single char for the version is probably enough, but might
// as well be general about it... // as well be general about it...
if (version > 1) if (version > 1)
name = name.substring(0, name.length() - chars); name = removeEndingChars(name, chars, delimited, delimiter);
if (version >= Math.pow(10, chars)) if (version >= Math.pow(10, chars))
chars++; chars++;
if (name.length() + chars > maxLen) if (name.length() + chars > maxLen)
name = name.substring(0, maxLen - chars); name = removeEndingChars(name,
name = name + version; name.length() + chars - maxLen,
delimited, delimiter);
name = addCharsToEnd(name, new Integer(version).toString(),
delimited, delimiter);
} }
} }
if (delimited) {
String delimCase = getDelimitedCase();
if (delimCase.equals(SCHEMA_CASE_LOWER)) {
return name.toLowerCase();
}
else if (delimCase.equals(SCHEMA_CASE_UPPER)) {
return name.toUpperCase();
}
else {
return name;
}
}
// TODO: This is the original. Should the db supported case be checked?
return name.toUpperCase(); return name.toUpperCase();
} }
private String removeEndingChars(String name,
int charsToRemove,
boolean delimited,
String delimiter) {
if (delimited) {
name = name.substring(0, name.length() - delimiter.length());
name = name.substring(0, name.length() - charsToRemove);
name = name + delimiter;
}
else {
name = name.substring(0, name.length() - charsToRemove);
}
return name;
}
private String addCharsToEnd(String name,
String charsToAdd,
boolean delimited,
String delimiter) {
if (delimited) {
name = name.substring(0, name.length() - delimiter.length());
name = name + charsToAdd;
name = name + delimiter;
}
else {
name = name + charsToAdd;
}
return name;
}
/** /**
* Return a series of SQL statements to create the given table, complete * Return a series of SQL statements to create the given table, complete
@ -3997,6 +4101,25 @@ public class DBDictionary
if (objectName == null) if (objectName == null)
return null; return null;
// Handle delimited string differently. Return unquoted name.
if (delimitIds ||
objectName.startsWith(getDelimiter()) &&
objectName.endsWith(getDelimiter())) {
String delimCase = getDelimitedCase();
if (SCHEMA_CASE_UPPER.equals(delimCase)) {
objectName.toUpperCase();
}
else if (SCHEMA_CASE_LOWER.equals(delimCase)) {
objectName.toLowerCase();
}
// TODO: maybe have a different method to remove quotes and
// call it from the calling methods
int delimLen = getDelimiter().length();
return objectName.substring(delimLen,
objectName.length() - delimLen);
}
String scase = getSchemaCase(); String scase = getSchemaCase();
if (SCHEMA_CASE_LOWER.equals(scase)) if (SCHEMA_CASE_LOWER.equals(scase))
return objectName.toLowerCase(); return objectName.toLowerCase();
@ -4728,4 +4851,101 @@ public class DBDictionary
length)); length));
return name; return name;
} }
public String delimitString(String name, DBIdentifiers type) {
if (StringUtils.isEmpty(name)) {
return null;
}
if (!getSupportsDelimitedIds()) {
// TODO: log (or maybe log in the method itself; so maybe
// merge with next if stmt
return name;
}
if (!delimitIds) {
return name;
}
// TODO: merge with if stmt above (maybe not, may want to log this)
if (!supportsDelimitedId(type)) {
// TODO: log
return name;
}
String delimitedString = delimiter + name + delimiter;
return delimitedString;
}
/**
* @return the unsupportedDelimitedIds
*/
protected EnumSet<DBIdentifiers> getUnsupportedDelimitedIds() {
return unsupportedDelimitedIds;
}
protected boolean supportsDelimitedId(DBIdentifiers type) {
if (getUnsupportedDelimitedIds().contains(type)) {
return false;
}
return true;
}
/**
* @return the delimiter
*/
public String getDelimiter() {
return delimiter;
}
protected String getDelimitedCase() {
return delimitedCase;
}
protected void setDelimitedCase(DatabaseMetaData metaData) {
try {
if (metaData.storesMixedCaseQuotedIdentifiers()) {
delimitedCase = SCHEMA_CASE_PRESERVE;
}
else if (metaData.storesUpperCaseQuotedIdentifiers()) {
delimitedCase = SCHEMA_CASE_UPPER;
}
else if (metaData.storesLowerCaseQuotedIdentifiers()) {
delimitedCase = SCHEMA_CASE_LOWER;
}
} catch (SQLException e) {
// TODO log this
}
}
/**
* @return the supportsDelimitedIds
*/
public boolean getSupportsDelimitedIds() {
return supportsDelimitedIds;
}
/**
* @param supportsDelimitedIds the supportsDelimitedIds to set
*/
public void setSupportsDelimitedIds(DatabaseMetaData metaData) {
try {
supportsDelimitedIds =
metaData.supportsMixedCaseQuotedIdentifiers();
} catch (SQLException e) {
// TODO log this, or should we throw an exception?
}
}
/**
* @return the delimitIds
*/
public boolean isDelimitIds() {
return delimitIds;
}
/**
* @param delimitIds the delimitIds to set
*/
public void setDelimitIds(boolean delimitIds) {
this.delimitIds = delimitIds;
}
} }

View File

@ -20,9 +20,8 @@ package org.apache.openjpa.jdbc.sql;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.EnumSet;
import java.util.Set; import java.util.Set;
import javax.sql.DataSource; import javax.sql.DataSource;
@ -40,6 +39,9 @@ public class DerbyDictionary
* the DataSource. * the DataSource.
*/ */
public boolean shutdownOnClose = true; public boolean shutdownOnClose = true;
private EnumSet<DBIdentifiers> unsupportedDelimitedIds =
EnumSet.of(DBIdentifiers.COLUMN_COLUMN_DEFINITION);
public DerbyDictionary() { public DerbyDictionary() {
platform = "Apache Derby"; platform = "Apache Derby";
@ -120,4 +122,13 @@ public class DerbyDictionary
} }
return recoverable; return recoverable;
} }
/**
* @return the unsupportedDelimitedIds
*/
@Override
protected EnumSet<DBIdentifiers> getUnsupportedDelimitedIds() {
return unsupportedDelimitedIds;
}
} }

View File

@ -115,6 +115,8 @@ public class AnnotationPersistenceMappingParser
private static final Map<Class, MappingTag> _tags = private static final Map<Class, MappingTag> _tags =
new HashMap<Class, MappingTag>(); new HashMap<Class, MappingTag>();
private DBDictionary _dict;
static { static {
_tags.put(AssociationOverride.class, ASSOC_OVERRIDE); _tags.put(AssociationOverride.class, ASSOC_OVERRIDE);
@ -199,6 +201,7 @@ public class AnnotationPersistenceMappingParser
public AnnotationPersistenceMappingParser(JDBCConfiguration conf) { public AnnotationPersistenceMappingParser(JDBCConfiguration conf) {
super(conf); super(conf);
_dict = conf.getDBDictionaryInstance();
} }
@Override @Override
@ -252,9 +255,17 @@ public class AnnotationPersistenceMappingParser
meta = (SequenceMapping) getRepository().addSequenceMetaData(name); meta = (SequenceMapping) getRepository().addSequenceMetaData(name);
meta.setSequencePlugin(SequenceMapping.IMPL_VALUE_TABLE); meta.setSequencePlugin(SequenceMapping.IMPL_VALUE_TABLE);
meta.setTable(toTableName(gen.schema(), gen.table())); String schema = _dict.delimitString(gen.schema(),
meta.setPrimaryKeyColumn(gen.pkColumnName()); DBDictionary.DBIdentifiers.TABLE_GEN_SCHEMA);
meta.setSequenceColumn(gen.valueColumnName()); String table = _dict.delimitString(gen.table(),
DBDictionary.DBIdentifiers.TABLE_GEN_TABLE);
meta.setTable(toTableName(schema,table));
String pkColumnName = _dict.delimitString(gen.pkColumnName(),
DBDictionary.DBIdentifiers.TABLE_GEN_PK_COLUMN);
meta.setPrimaryKeyColumn(pkColumnName);
String valueColumnName = _dict.delimitString(gen.valueColumnName(),
DBDictionary.DBIdentifiers.TABLE_GEN_VALUE_COLUMN);
meta.setSequenceColumn(valueColumnName);
meta.setPrimaryKeyValue(gen.pkColumnValue()); meta.setPrimaryKeyValue(gen.pkColumnValue());
meta.setInitialValue(gen.initialValue()); meta.setInitialValue(gen.initialValue());
meta.setAllocate(gen.allocationSize()); meta.setAllocate(gen.allocationSize());
@ -494,13 +505,18 @@ public class AnnotationPersistenceMappingParser
Log log = getLog(); Log log = getLog();
String name; String name;
String schema;
List<Column> joins = null; List<Column> joins = null;
for (SecondaryTable table : tables) { for (SecondaryTable table : tables) {
name = table.name(); name = _dict.delimitString(table.name(),
DBDictionary.DBIdentifiers.SECONDARY_TABLE_NAME) ;
if (StringUtils.isEmpty(name)) if (StringUtils.isEmpty(name))
throw new MetaDataException(_loc.get("second-name", cm)); throw new MetaDataException(_loc.get("second-name", cm));
if (!StringUtils.isEmpty(table.schema())) if (!StringUtils.isEmpty(table.schema())) {
name = table.schema() + "." + name; schema = _dict.delimitString(table.schema(),
DBDictionary.DBIdentifiers.SECONDARY_TABLE_SCHEMA);
name = schema + "." + name;
}
if (table.pkJoinColumns().length > 0) { if (table.pkJoinColumns().length > 0) {
joins = new ArrayList<Column>(table.pkJoinColumns().length); joins = new ArrayList<Column>(table.pkJoinColumns().length);
for (PrimaryKeyJoinColumn join : table.pkJoinColumns()) for (PrimaryKeyJoinColumn join : table.pkJoinColumns())
@ -517,42 +533,46 @@ public class AnnotationPersistenceMappingParser
* Set class table. * Set class table.
*/ */
private void parseTable(ClassMapping cm, Table table) { private void parseTable(ClassMapping cm, Table table) {
String tableName = toTableName(table.schema(), table.name()); String tableName = _dict.delimitString(table.name(),
if (tableName != null) DBDictionary.DBIdentifiers.TABLE_NAME);
cm.getMappingInfo().setTableName(tableName); String schemaName = _dict.delimitString(table.schema(),
DBDictionary.DBIdentifiers.TABLE_SCHEMA);
String fullTableName = toTableName(schemaName, tableName);
if (fullTableName != null)
cm.getMappingInfo().setTableName(fullTableName);
addUniqueConstraints(tableName, cm, cm.getMappingInfo(), addUniqueConstraints(fullTableName, cm, cm.getMappingInfo(),
table.uniqueConstraints()); table.uniqueConstraints());
} }
Unique createUniqueConstraint(MetaDataContext ctx, UniqueConstraint anno) { Unique createUniqueConstraint(MetaDataContext ctx, UniqueConstraint anno) {
String[] columnNames = anno.columnNames(); String[] columnNames = anno.columnNames();
if (columnNames == null || columnNames.length == 0) if (columnNames == null || columnNames.length == 0)
throw new UserException(_loc.get("unique-no-column", ctx)); throw new UserException(_loc.get("unique-no-column", ctx));
Unique uniqueConstraint = new Unique(); Unique uniqueConstraint = new Unique();
for (int i=0; i<columnNames.length; i++) { for (int i = 0; i < columnNames.length; i++) {
if (StringUtils.isEmpty(columnNames[i])) if (StringUtils.isEmpty(columnNames[i]))
throw new UserException(_loc.get("unique-empty-column", throw new UserException(_loc.get("unique-empty-column",
Arrays.toString(columnNames), ctx)); Arrays.toString(columnNames), ctx));
Column column = new Column(); Column column = new Column();
column.setName(columnNames[i]); column.setName(columnNames[i]);
uniqueConstraint.addColumn(column); uniqueConstraint.addColumn(column);
} }
return uniqueConstraint; return uniqueConstraint;
} }
void addUniqueConstraints(String table, MetaDataContext ctx, void addUniqueConstraints(String table, MetaDataContext ctx,
MappingInfo info, UniqueConstraint...uniqueConstraints) { MappingInfo info, UniqueConstraint... uniqueConstraints) {
for (UniqueConstraint anno : uniqueConstraints) { for (UniqueConstraint anno : uniqueConstraints) {
Unique unique = createUniqueConstraint(ctx, anno); Unique unique = createUniqueConstraint(ctx, anno);
unique.setTableName(table); unique.setTableName(table);
if (info instanceof ClassMappingInfo) if (info instanceof ClassMappingInfo)
((ClassMappingInfo)info).addUnique(table, unique); ((ClassMappingInfo) info).addUnique(table, unique);
else if (info instanceof FieldMappingInfo) else if (info instanceof FieldMappingInfo)
((FieldMappingInfo)info).addJoinTableUnique(unique); ((FieldMappingInfo) info).addJoinTableUnique(unique);
else else
throw new InternalException(); throw new InternalException();
} }
} }
/** /**
@ -735,16 +755,16 @@ public class AnnotationPersistenceMappingParser
} }
void assertDefault(ForeignKey fk) { void assertDefault(ForeignKey fk) {
boolean isDefault = StringUtils.isEmpty(fk.name()) boolean isDefault = StringUtils.isEmpty(fk.name())
&& fk.enabled() && fk.enabled()
&& !fk.deferred() && !fk.deferred()
&& fk.deleteAction() == ForeignKeyAction.RESTRICT && fk.deleteAction() == ForeignKeyAction.RESTRICT
&& fk.updateAction() == ForeignKeyAction.RESTRICT && fk.updateAction() == ForeignKeyAction.RESTRICT
&& fk.columnNames().length == 0 && fk.columnNames().length == 0
&& fk.specified(); && fk.specified();
if (!isDefault) if (!isDefault)
throw new UserException(_loc.get("implicit-non-default-fk", _cls, throw new UserException(_loc.get("implicit-non-default-fk", _cls,
getSourceFile()).getMessage()); getSourceFile()).getMessage());
} }
@ -1449,11 +1469,11 @@ public class AnnotationPersistenceMappingParser
String secondary = null; String secondary = null;
for (int i = 0; i < pcols.length; i++) { for (int i = 0; i < pcols.length; i++) {
if (cols.size() > i) if (cols.size() > i)
setupColumn((Column) cols.get(i), pcols[i]); setupColumn((Column) cols.get(i), pcols[i], _dict);
else { else {
if (cols.isEmpty()) if (cols.isEmpty())
cols = new ArrayList<Column>(pcols.length); cols = new ArrayList<Column>(pcols.length);
cols.add(newColumn(pcols[i])); cols.add(newColumn(pcols[i], _dict));
} }
if (xmlTypeClass != null if (xmlTypeClass != null
@ -1469,7 +1489,9 @@ public class AnnotationPersistenceMappingParser
} }
unique |= (pcols[i].unique()) ? TRUE : FALSE; unique |= (pcols[i].unique()) ? TRUE : FALSE;
secondary = trackSecondaryTable(fm, secondary, pcols[i].table(), i); String secTable = _dict.delimitString(pcols[i].table(),
DBDictionary.DBIdentifiers.SECONDARY_TABLE_NAME);
secondary = trackSecondaryTable(fm, secondary, secTable, i);
} }
if (fm.isElementCollection()) if (fm.isElementCollection())
@ -1483,20 +1505,24 @@ public class AnnotationPersistenceMappingParser
/** /**
* Create a new schema column with information from the given annotation. * Create a new schema column with information from the given annotation.
*/ */
private static Column newColumn(javax.persistence.Column anno) { private static Column newColumn(javax.persistence.Column anno,
DBDictionary dict) {
Column col = new Column(); Column col = new Column();
setupColumn(col, anno); setupColumn(col, anno, dict);
return col; return col;
} }
/** /**
* Setup the given column with information from the given annotation. * Setup the given column with information from the given annotation.
*/ */
private static void setupColumn(Column col, javax.persistence.Column anno) { private static void setupColumn(Column col, javax.persistence.Column anno,
DBDictionary dict) {
if (!StringUtils.isEmpty(anno.name())) if (!StringUtils.isEmpty(anno.name()))
col.setName(anno.name()); col.setName(dict.delimitString(anno.name(),
DBDictionary.DBIdentifiers.COLUMN_NAME));
if (!StringUtils.isEmpty(anno.columnDefinition())) if (!StringUtils.isEmpty(anno.columnDefinition()))
col.setTypeName(anno.columnDefinition()); col.setTypeName(dict.delimitString(anno.columnDefinition(),
DBDictionary.DBIdentifiers.COLUMN_COLUMN_DEFINITION));
if (anno.precision() != 0) if (anno.precision() != 0)
col.setSize(anno.precision()); col.setSize(anno.precision());
else if (anno.length() != 255) else if (anno.length() != 255)

View File

@ -89,9 +89,9 @@ public class PersistenceMappingDefaults
int nColumn = vers.getMappingInfo().getColumns().size(); int nColumn = vers.getMappingInfo().getColumns().size();
switch (nColumn) { switch (nColumn) {
case 0 : return NoneVersionStrategy.getInstance(); case 0 : return NoneVersionStrategy.getInstance();
case 1 : return new NumberVersionStrategy(); case 1 : return new NumberVersionStrategy();
default: return new MultiColumnVersionStrategy(); default: return new MultiColumnVersionStrategy();
} }
} }
@ -135,7 +135,17 @@ public class PersistenceMappingDefaults
ClassMapping clm = fm.getDefiningMapping(); ClassMapping clm = fm.getDefiningMapping();
Table table = getTable(clm); Table table = getTable(clm);
String name = table.getName() + "_"; String name = table.getName();
String delim = dict.getDelimiter();
boolean isDelimited = false;
if (name.startsWith(delim) && name.endsWith(delim)) {
isDelimited = true;
name = name.substring(0,name.length()-1) + "_";
}
else {
name = name + "_";
}
// if this is an assocation table, spec says to suffix with table of // 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 // the related type. spec doesn't cover other cases; we're going to
@ -147,6 +157,11 @@ public class PersistenceMappingDefaults
name += rel.getTable().getName(); name += rel.getTable().getName();
else else
name += fm.getName(); name += fm.getName();
if (isDelimited) {
name += "\"";
}
return name.replace('$', '_'); return name.replace('$', '_');
} }

View File

@ -0,0 +1,199 @@
/*
* 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.delimited.identifiers;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.MapKeyColumn;
import javax.persistence.SecondaryTable;
import javax.persistence.Table;
@Entity
@Table(name="\"primary entityF\"", schema="\"delim id\"")
@SecondaryTable(name="\"secondary entityF\"", schema="\"delim id\"")
//@Table(name="primary_entityF", schema="delim_id")
public class EntityF {
@Id
private int id;
@Column(name="\"f name\"")
private String name;
@Column(name="f_nonDelimName")
private String nonDelimName;
@Column(name="\"secondary name\"", table="\"secondary entityF\"")
private String secName;
@ElementCollection
// CollectionTable with default name generation
@CollectionTable
private Set<String> collectionSet = new HashSet<String>();
@ElementCollection
@CollectionTable(name="\"collectionDelimSet\"", schema="\"delim id\"")
private Set<String> collectionDelimSet = new HashSet<String>();
@ElementCollection
@MapKeyColumn
private Map<String, String> collectionMap = new HashMap<String, String>();
@ElementCollection
@MapKeyColumn(name="\"mapKey\"")
private Map<String, String> delimCollectionMap = new HashMap<String, String>();
public EntityF(String name) {
this.name = name;
}
public EntityF(int id, String name) {
this.id = id;
this.name = name;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the nonDelimName
*/
public String getNonDelimName() {
return nonDelimName;
}
/**
* @param nonDelimName the nonDelimName to set
*/
public void setNonDelimName(String nonDelimName) {
this.nonDelimName = nonDelimName;
}
/**
* @return the secName
*/
public String getSecName() {
return secName;
}
/**
* @param secName the secName to set
*/
public void setSecName(String secName) {
this.secName = secName;
}
/**
* @return the collectionSet
*/
public Set<String> getCollectionSet() {
return collectionSet;
}
/**
* @param collectionSet the collectionSet to set
*/
public void setCollectionSet(Set<String> collectionSet) {
this.collectionSet = collectionSet;
}
public void addCollectionSet(String item) {
collectionSet.add(item);
}
/**
* @return the collectionNamedSet
*/
public Set<String> getCollectionDelimSet() {
return collectionDelimSet;
}
/**
* @param collectionNamedSet the collectionNamedSet to set
*/
public void setCollectionDelimSet(Set<String> collectionDelimSet) {
this.collectionDelimSet = collectionDelimSet;
}
public void addCollectionDelimSet(String item) {
this.collectionDelimSet.add(item);
}
/**
* @return the collectionMap
*/
public Map<String, String> getCollectionMap() {
return collectionMap;
}
/**
* @param collectionMap the collectionMap to set
*/
public void setCollectionMap(Map<String, String> collectionMap) {
this.collectionMap = collectionMap;
}
public void addCollectionMap(String key, String value) {
collectionMap.put(key, value);
}
/**
* @return the delimCollectionMap
*/
public Map<String, String> getDelimCollectionMap() {
return delimCollectionMap;
}
/**
* @param delimCollectionMap the delimCollectionMap to set
*/
public void setDelimCollectionMap(Map<String, String> delimCollectionMap) {
this.delimCollectionMap = delimCollectionMap;
}
public void addDelimCollectionMap(String key, String value) {
delimCollectionMap.put(key, value);
}
}

View File

@ -0,0 +1,115 @@
package org.apache.openjpa.persistence.delimited.identifiers;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
public class TestManualDelimId extends SQLListenerTestCase {
OpenJPAEntityManager em;
int id = 0;
EntityF entityF;
JDBCConfiguration conf;
DBDictionary dict;
public void setUp() throws Exception {
// TODO: retest with DROP to figure out problem
// super.setUp(EntityF2.class,DROP_TABLES);
super.setUp(
org.apache.openjpa.persistence.delimited.identifiers.EntityF.class);
assertNotNull(emf);
em = emf.createEntityManager();
assertNotNull(em);
conf = (JDBCConfiguration) emf.getConfiguration();
dict = conf.getDBDictionaryInstance();
}
public void createEntityF(int id) {
entityF = new EntityF(id, "fName");
entityF.setNonDelimName("fNonDelimName");
entityF.setSecName("sec name");
entityF.addCollectionSet("xxx");
entityF.addCollectionSet("yyy");
entityF.addCollectionDelimSet("aaa");
entityF.addCollectionDelimSet("bbb");
entityF.addCollectionMap("aaa", "xxx");
entityF.addCollectionMap("bbb", "yyy");
entityF.addDelimCollectionMap("www", "xxx");
entityF.addDelimCollectionMap("yyy", "zzz");
}
// TODO: temp - test on multiple DBs
// public void testDBCapability() {
// Connection conn = (Connection)em.getConnection();
// try {
// DatabaseMetaData meta = conn.getMetaData();
// System.out.println("LC - " + meta.storesLowerCaseIdentifiers());
// System.out.println("LCQ - " + meta.storesLowerCaseQuotedIdentifiers());
// System.out.println("MC - " + meta.storesMixedCaseIdentifiers());
// System.out.println("MCQ - " + meta.storesMixedCaseQuotedIdentifiers());
// System.out.println("UC - " + meta.storesUpperCaseIdentifiers());
// System.out.println("UCQ - " + meta.storesUpperCaseQuotedIdentifiers());
// System.out.println("");
// System.out.println("db product name - " + meta.getDatabaseProductName());
// System.out.println("db product version - " + meta.getDatabaseProductVersion());
// System.out.println("driver name - " + meta.getDriverName());
// System.out.println("driver version - " + meta.getDriverVersion());
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
public void testCreateF() {
id++;
createEntityF(id);
em.getTransaction().begin();
em.persist(entityF);
em.getTransaction().commit();
System.out.println(super.toString(sql));
// getColumnInfo("\"primary_entityF\"", "\"f_name\"", "\"delim_id\"");
// getColumnInfo("\"primary entityF\"", null, "\"delim id\"");
// getColumnInfo("\"secondary entityF\"", null, "\"delim id\"");
}
// TODO: change to boolean return and remove assert
// private void getColumnInfo(String tableName, String columnName, String schemaName) {
// Connection conn = (Connection)em.getConnection();
// try {
// DatabaseMetaData meta = conn.getMetaData();
// // tableName = "\"" + tableName + "\"";
// Column[] columns = dict.getColumns(meta, conn.getCatalog(), schemaName, tableName, columnName, conn);
// System.out.println("columns.length - " + columns.length);
//
//// assertEquals(1, columns.length);
//
// for (Column column : columns) {
// System.out.println("column name - " + column.getName());
// System.out.println("column fullName - " + column.getFullName());
// System.out.println("column schemaName - " + column.getSchemaName());
// System.out.println("column tableName - " + column.getTableName());
// System.out.println("column description - " + column.getDescription());
// }
// } catch (SQLException e) {
// e.printStackTrace();
// }
// finally {
// try {
// conn.commit();
// conn.close();
// } catch (SQLException e) {
// e.printStackTrace();
// fail("problem closing connection");
// }
// }
// }
}