diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/DynamicSchemaFactory.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/DynamicSchemaFactory.java index eb19597b5..10bd2d29b 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/DynamicSchemaFactory.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/DynamicSchemaFactory.java @@ -90,6 +90,13 @@ public class DynamicSchemaFactory Schema schema = getSchema(schemaName); if (schema == null) schema = addSchema(schemaName); + + // Ensure only valid table name(s) are added to the schema + if (tableName.length() > _dict.maxTableNameLength) { + return schema.addTable(tableName, + _dict.getValidTableName(tableName, getSchema(schemaName))); + } + return schema.addTable(tableName); } @@ -104,7 +111,7 @@ public class DynamicSchemaFactory /** * Table type that adds columns when {@link #getColumn} is called. */ - private static class DynamicTable + private class DynamicTable extends Table { public DynamicTable(String name, Schema schema) { @@ -118,6 +125,13 @@ public class DynamicSchemaFactory Column col = super.getColumn(name); if (col != null) return col; + + // Ensure only valid column name(s) are added to the table + if (name.length() > _dict.maxColumnNameLength) { + return addColumn(name, + _dict.getValidColumnName(name, this)); + } + return addColumn(name); } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schema.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schema.java index 490fd4179..6612456cf 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schema.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schema.java @@ -133,6 +133,24 @@ public class Schema return tab; } + /** + * Add a table with a shortened (i.e., validated) name to the schema + */ + public Table addTable(String name, String validName) { + SchemaGroup group = getSchemaGroup(); + Table tab; + if (group != null) { + group.addName(validName, true); + tab = group.newTable(validName, this); + } else + tab = new Table(validName, this); + if (_tableMap == null) + _tableMap = new TreeMap(); + _tableMap.put(name.toUpperCase(), tab); + _tables = null; + return tab; + } + /** * Remove the given table from the schema. * diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java index 1f834b960..252f01169 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java @@ -297,6 +297,26 @@ public class Table return col; } + + /** + * Add a colum with a shortened (i.e., validated) name to the table + */ + public Column addColumn(String name, String validName) { + addName(name, true); + Schema schema = getSchema(); + Column col; + if (schema != null && schema.getSchemaGroup() != null) + col = schema.getSchemaGroup().newColumn(validName, this); + else + col = new Column(validName, this); + if (_colMap == null) + _colMap = new LinkedHashMap(); + _colMap.put(name.toUpperCase(), col); + _cols = null; + return col; + } + + /** * Remove the given column from the table. * diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/dynamicschema/EntityVeryLongNames.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/dynamicschema/EntityVeryLongNames.java new file mode 100644 index 000000000..ce14eeea7 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/dynamicschema/EntityVeryLongNames.java @@ -0,0 +1,149 @@ +/* + * 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.dynamicschema; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + + +/** + * Entity with very long table and column names + * + * @author Tim McConnell + * @since 2.0.0 + */ +@Entity +@Table(name="Very______________________________________________" + + "Long______________________________________________" + + "Table_____________________________________________" + + "Name______________________________________________" ) +public class EntityVeryLongNames implements Serializable { + + @Id + @Column(name="ID________________________________________________" + + "Very______________________________________________" + + "Long______________________________________________" + + "Column____________________________________________" + + "Name______________________________________________" ) + private int id; + + @Column(name="FirstName_________________________________________" + + "Very______________________________________________" + + "Long______________________________________________" + + "Column____________________________________________" + + "Name______________________________________________" ) + private String firstName; + + @Column(name="LastName__________________________________________" + + "Very______________________________________________" + + "Long______________________________________________" + + "Column____________________________________________" + + "Name______________________________________________" ) + private String lastName; + + public EntityVeryLongNames() { + } + + public EntityVeryLongNames(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + + @Override + public String toString() { + return "EntityVeryLongNames: id: " + getId() + + " firstName: " + getFirstName() + + " lastName: " + getLastName(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((getFirstName() == null) ? 0 : getFirstName().hashCode()); + result = prime * result + getId(); + result = prime * result + + ((getLastName() == null) ? 0 : getLastName().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final EntityVeryLongNames other = (EntityVeryLongNames) obj; + if (getId() != other.getId()) { + return false; + } + if (getFirstName() == null) { + if (other.getFirstName() != null) { + return false; + } + } + else if (!getFirstName().equals(other.getFirstName())) { + return false; + } + if (getLastName() == null) { + if (other.getLastName() != null) { + return false; + } + } + else if (!getLastName().equals(other.getLastName())) { + return false; + } + return true; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/dynamicschema/TestDynamicSchemas.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/dynamicschema/TestDynamicSchemas.java new file mode 100644 index 000000000..52aa4df56 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/dynamicschema/TestDynamicSchemas.java @@ -0,0 +1,179 @@ +/* + * 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.dynamicschema; + +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.meta.ClassMapping; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.jdbc.schema.Table; +import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + + +/** + * TestDynamicSchemas is used to create dynamic schemas for the various + * database dictionaries and validate them to ensure they are created + * correctly as specified in their dictionary. The following variables of each + * dictionary are used for validation:

+ * + *

    + *
  1. maxTableNameLength + *
  2. maxColumnNameLength + *
+ * + * Note(s): + * + * + * @author Tim McConnell + * @since 2.0.0 + */ +public class TestDynamicSchemas extends SingleEMFTestCase { + + public void setUp() { + } + + + public void testDerbyDynamicSchema() { + OpenJPAEntityManagerFactorySPI derbyEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:derby:net://host:1527/databaseName", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( derbyEMF ); + } + + + public void testDB2DynamicSchema() { + OpenJPAEntityManagerFactorySPI db2EMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:db2://localhost:5000/db2", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( db2EMF ); + } + + + public void testOracleDynamicSchema() { + OpenJPAEntityManagerFactorySPI oracleEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:oracle:thin:@host:1234:database_sid", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( oracleEMF ); + } + + + public void testAccessDynamicSchema() { + OpenJPAEntityManagerFactorySPI accessEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:odbc:Driver=Microsoft Access Driver (*.mdb);DBQ=c:", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( accessEMF ); + } + + + public void testSQLServerDynamicSchema() { + OpenJPAEntityManagerFactorySPI sqlserverEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:microsoft:sqlserver:", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( sqlserverEMF ); + } + + + public void testMySQLDynamicSchema() { + OpenJPAEntityManagerFactorySPI mysqlEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:mysql://host1:1,host2:2/database?p1=v1&p2=v2", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( mysqlEMF ); + } + + + public void testPostgresDynamicSchema() { + OpenJPAEntityManagerFactorySPI postgresEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:postgresql:database", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( postgresEMF ); + } + + + public void testInformixDynamicSchema() { + OpenJPAEntityManagerFactorySPI informixEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:informix-sqli:", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( informixEMF ); + } + + + public void testSybaseDynamicSchema() { + OpenJPAEntityManagerFactorySPI sybaseEMF = + createEMF(EntityVeryLongNames.class, + "openjpa.ConnectionURL", + "jdbc:sybase:Tds:host:1234?ServiceName=db", + "openjpa.jdbc.SynchronizeMappings", "export", + "openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA); + validateTableName( sybaseEMF ); + } + + + private void validateTableName(OpenJPAEntityManagerFactorySPI emf) { + JDBCConfiguration conf = (JDBCConfiguration) emf.getConfiguration(); + DBDictionary dict = conf.getDBDictionaryInstance(); + ClassMapping mapping = (ClassMapping)conf. + getMetaDataRepositoryInstance(). + getMetaData(EntityVeryLongNames.class,getClass(). + getClassLoader(), true); + Table table = mapping.getTable(); + assertTrue(table.getName().length() > 0); + assertTrue(table.getName().length() <= dict.maxTableNameLength); + validateColumnNames(table, dict); + } + + + private void validateColumnNames(Table table, DBDictionary dict) { + Column[] columns = table.getColumns(); + for (Column column : columns) { + assertTrue(column.getName().length() > 0); + assertTrue(column.getName().length() <= dict.maxColumnNameLength); + } + } +}