You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by il...@apache.org on 2020/07/21 12:43:50 UTC
[openjpa] branch master updated: OPENJPA-2818 DBDictionary >
delimitIdentifiers = true does not work (#69)
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openjpa.git
The following commit(s) were added to refs/heads/master by this push:
new e3bb1f1 OPENJPA-2818 DBDictionary > delimitIdentifiers = true does not work (#69)
e3bb1f1 is described below
commit e3bb1f16575c53f35969968b259a20d16056747c
Author: Enrico Olivelli <eo...@gmail.com>
AuthorDate: Tue Jul 21 14:43:40 2020 +0200
OPENJPA-2818 DBDictionary > delimitIdentifiers = true does not work (#69)
---
openjpa-jdbc/nbproject/project.properties | 0
.../openjpa/jdbc/identifier/DBIdentifier.java | 42 +-
.../org/apache/openjpa/jdbc/schema/SchemaTool.java | 7 +-
.../java/org/apache/openjpa/jdbc/schema/Table.java | 38 +-
.../openjpa/jdbc/sql/DBDictionaryFactory.java | 5 +-
.../apache/openjpa/jdbc/sql/HerdDBDictionary.java | 2 +
.../org/apache/openjpa/jdbc/sql/SelectImpl.java | 6 +-
.../openjpa/jdbc/sql/DBDictionaryFactoryTest.java | 61 +++
.../apache/openjpa/jdbc/sql/TestSelectImpl.java | 92 ++++
.../openjpa/jdbc/sql/TestDelimitIdentifiers.java | 536 +++++++++++++++++++++
10 files changed, 759 insertions(+), 30 deletions(-)
diff --git a/openjpa-jdbc/nbproject/project.properties b/openjpa-jdbc/nbproject/project.properties
new file mode 100644
index 0000000..e69de29
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java
index 9b8b467..aa1f888 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java
@@ -30,7 +30,7 @@ import org.apache.openjpa.lib.util.StringUtil;
*/
public class DBIdentifier extends IdentifierImpl implements Cloneable, Identifier, Serializable {
-
+
private static final long serialVersionUID = 1L;
/**
@@ -754,16 +754,17 @@ public class DBIdentifier extends IdentifierImpl implements Cloneable, Identifie
if (DBIdentifier.isNull(name)) {
return name;
}
- DBIdentifier sName = name.clone();
- if (sName.getNameInternal() == null) {
- return sName;
+ if (name.getNameInternal() == null) {
+ return name;
}
// Do not convert delimited names to upper case. They may have
// been delimited to preserve case.
- if (force || !Normalizer.isDelimited(sName.getNameInternal())) {
+ if (force || !Normalizer.isDelimited(name.getNameInternal())) {
+ DBIdentifier sName = name.clone();
sName.setNameInternal(sName.getNameInternal().toUpperCase());
+ return sName;
}
- return sName;
+ return name;
}
/**
@@ -861,17 +862,36 @@ public class DBIdentifier extends IdentifierImpl implements Cloneable, Identifie
if (DBIdentifier.isNull(name)) {
return name;
}
- DBIdentifier sName = name.clone();
- if (isEmpty(sName)) {
- return sName;
+ if (!name.isDelimited()) {
+ return name;
}
- String strName = sName.getNameInternal();
- strName = Normalizer.removeDelimiters(strName);
+ String strName = Normalizer.removeDelimiters(name.getNameInternal());
+ DBIdentifier sName = name.clone();
sName.setNameInternal(strName);
return sName;
}
/**
+ * Combine {@link #removeDelimiters(org.apache.openjpa.jdbc.identifier.DBIdentifier) }
+ * with {@link #toUpper(org.apache.openjpa.jdbc.identifier.DBIdentifier, boolean) }
+ * in order to save allocations and CPU cycles.
+ * @param name
+ * @return
+ */
+ public static DBIdentifier removeDelimitersAndMakeUpper(DBIdentifier name) {
+ if (DBIdentifier.isNull(name) || name.getNameInternal() == null) {
+ return name;
+ }
+ if (!name.isDelimited()) {
+ return toUpper(name, true);
+ }
+ String strName = Normalizer.removeDelimiters(name.getNameInternal());
+ DBIdentifier sName = name.clone();
+ sName.setNameInternal(strName.toUpperCase());
+ return sName;
+ }
+
+ /**
* Returns a new delimiter with leading and trailing spaces removed.
* @param name
*/
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
index a3c22be..f7d528f 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
@@ -40,6 +40,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.sql.DataSource;
import org.apache.openjpa.conf.OpenJPAConfiguration;
@@ -1030,10 +1032,7 @@ public class SchemaTool {
continue;
if (dropColumn(cols[k])) {
- if (dbTable != null)
- dbTable.removeColumn(col);
- else
- _log.warn(_loc.get("drop-col", cols[k], tabs[j]));
+ dbTable.removeColumn(col);
}
}
}
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 ab00073..34f4327 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
@@ -38,6 +38,7 @@ import org.apache.openjpa.lib.meta.SourceTracker;
* @author Abe White
* @author Stephen Kim
*/
+
public class Table
extends NameSet
implements Comparable<Object>, SourceTracker {
@@ -45,6 +46,7 @@ public class Table
private static final long serialVersionUID = 1L;
private DBIdentifier _name = DBIdentifier.NULL;
private DBIdentifier _schemaName = DBIdentifier.NULL;
+ // all keys must be normalized with normalizeColumnKey
private Map<DBIdentifier, Column> _colMap = null;
private Map<DBIdentifier, Index> _idxMap = null;
private Collection<ForeignKey> _fkList = null;
@@ -312,12 +314,20 @@ public class Table
return _rels;
}
+ /**
+ * Return the list of column names, used only for informative (error) messages.
+ * @return
+ */
public String[] getColumnNames() {
if (_colMap == null) {
return new String[0];
}
- DBIdentifier[] sNames = _colMap.keySet().toArray(new DBIdentifier[_colMap.size()]);
- return DBIdentifier.toStringArray(sNames);
+ return _colMap
+ .values()
+ .stream()
+ .map(Column::getIdentifier)
+ .map(DBIdentifier::getName)
+ .toArray(String[]::new);
}
/**
@@ -329,10 +339,19 @@ public class Table
return getColumn(DBIdentifier.newIdentifier(name, DBIdentifierType.COLUMN, true));
}
- public Column getColumn(DBIdentifier name) {
+ private Column internalGetColumn(DBIdentifier name) {
if (DBIdentifier.isNull(name) || _colMap == null)
return null;
- return _colMap.get(DBIdentifier.toUpper(name));
+ DBIdentifier key = normalizeColumnKey(name);
+ return _colMap.get(key);
+ }
+
+ public Column getColumn(DBIdentifier name) {
+ return internalGetColumn(name);
+ }
+
+ private static DBIdentifier normalizeColumnKey(DBIdentifier name) {
+ return DBIdentifier.removeDelimitersAndMakeUpper(name);
}
public Column getColumn(DBIdentifier name, boolean create) {
@@ -375,8 +394,7 @@ public class Table
if (DBIdentifier.isNull(name) || _colMap == null) {
return false;
}
- DBIdentifier sName = DBIdentifier.toUpper(name);
- return _colMap.containsKey(sName);
+ return _colMap.containsKey(normalizeColumnKey(name));
}
public boolean containsColumn(Column col) {
@@ -414,8 +432,7 @@ public class Table
}
if (_colMap == null)
_colMap = new LinkedHashMap<>();
- DBIdentifier sName = DBIdentifier.toUpper(name);
- _colMap.put(sName, col);
+ _colMap.put(normalizeColumnKey(name), col);
_cols = null;
return col;
}
@@ -440,8 +457,7 @@ public class Table
col = new Column(validName, this);
if (_colMap == null)
_colMap = new LinkedHashMap<>();
- DBIdentifier sName = DBIdentifier.toUpper(name);
- _colMap.put(sName, col);
+ _colMap.put(normalizeColumnKey(name), col);
_cols = null;
return col;
}
@@ -469,7 +485,7 @@ public class Table
if (col == null || _colMap == null)
return false;
- DBIdentifier sName = DBIdentifier.toUpper(col.getIdentifier());
+ DBIdentifier sName = normalizeColumnKey(col.getIdentifier());
Column cur = _colMap.get(sName);
if (!col.equals(cur))
return false;
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java
index bdc302d..cd24841 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java
@@ -217,7 +217,7 @@ public class DBDictionaryFactory {
/**
* Guess the dictionary class name to use based on the product string.
*/
- private static String dictionaryClassForString(String prod, JDBCConfiguration conf) {
+ static String dictionaryClassForString(String prod, JDBCConfiguration conf) {
if (StringUtil.isEmpty(prod))
return null;
prod = prod.toLowerCase(Locale.ENGLISH);
@@ -270,6 +270,9 @@ public class DBDictionaryFactory {
if (prod.indexOf("sapdb") != -1) {
return dbdictionaryPlugin.unalias("maxdb");
}
+ if (prod.indexOf("herddb") != -1) {
+ return dbdictionaryPlugin.unalias("herddb");
+ }
// test h2 in a special way, because there's a decent chance the string
// h2 could appear in the URL of another database
if (prod.indexOf("jdbc:h2:") != -1)
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java
index 31986e8..63bfa18 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java
@@ -32,6 +32,8 @@ public class HerdDBDictionary
supportsForeignKeys = false;
supportsUniqueConstraints = false;
supportsCascadeDeleteAction = false;
+ schemaCase = SCHEMA_CASE_LOWER;
+ delimitedCase = SCHEMA_CASE_PRESERVE;
// make OpenJPA escape everything, because Apache Calcite has a lot of reserved words, like 'User', 'Value'...
setDelimitIdentifiers(true);
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
index 611e730..1c10959 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
@@ -2154,7 +2154,7 @@ public class SelectImpl
* Return the alias for the given table under the given joins.
* NOTE: WE RELY ON THESE INDEXES BEING MONOTONICALLY INCREASING FROM 0
*/
- private int getTableIndex(Table table, PathJoins pj, boolean create) {
+ int getTableIndex(Table table, PathJoins pj, boolean create) {
// if we have a from select, then there are no table aliases
if (_from != null)
return -1;
@@ -2699,7 +2699,7 @@ public class SelectImpl
* Return the alias used to key on the column data, considering the
* given joins.
*/
- private String getColumnAlias(Column col, PathJoins pj) {
+ String getColumnAlias(Column col, PathJoins pj) {
String alias;
if (_sel._from != null) {
alias = SelectImpl.toAlias(_sel._from.getTableIndex
@@ -2711,7 +2711,7 @@ public class SelectImpl
return alias + "_" + col;
}
alias = SelectImpl.toAlias(_sel.getTableIndex(col.getTable(), pj, false));
- return (alias == null) ? null : alias + "." + col;
+ return (alias == null) ? null : alias + "." + _sel._dict.getNamingUtil().toDBName(col.toString());
}
////////////////////////////
diff --git a/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactoryTest.java b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactoryTest.java
new file mode 100644
index 0000000..29782bf
--- /dev/null
+++ b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactoryTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 Apache Software Foundation.
+ *
+ * Licensed 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.jdbc.sql;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
+
+public class DBDictionaryFactoryTest {
+
+ @Test
+ public void testDictionaryClassForString() {
+ JDBCConfiguration conf = new JDBCConfigurationImpl();
+
+ String[] aliases = new String[]{
+ "access", org.apache.openjpa.jdbc.sql.AccessDictionary.class.getName(),
+ "db2", org.apache.openjpa.jdbc.sql.DB2Dictionary.class.getName(),
+ "derby", org.apache.openjpa.jdbc.sql.DerbyDictionary.class.getName(),
+ "empress", org.apache.openjpa.jdbc.sql.EmpressDictionary.class.getName(),
+ "foxpro", org.apache.openjpa.jdbc.sql.FoxProDictionary.class.getName(),
+ "h2", org.apache.openjpa.jdbc.sql.H2Dictionary.class.getName(),
+ "hsql", org.apache.openjpa.jdbc.sql.HSQLDictionary.class.getName(),
+ "informix", org.apache.openjpa.jdbc.sql.InformixDictionary.class.getName(),
+ "ingres", org.apache.openjpa.jdbc.sql.IngresDictionary.class.getName(),
+ "jdatastore", org.apache.openjpa.jdbc.sql.JDataStoreDictionary.class.getName(),
+ "mariadb", org.apache.openjpa.jdbc.sql.MariaDBDictionary.class.getName(),
+ "mysql", org.apache.openjpa.jdbc.sql.MySQLDictionary.class.getName(),
+ "herddb", org.apache.openjpa.jdbc.sql.HerdDBDictionary.class.getName(),
+ "oracle", org.apache.openjpa.jdbc.sql.OracleDictionary.class.getName(),
+ "pointbase", org.apache.openjpa.jdbc.sql.PointbaseDictionary.class.getName(),
+ "postgres", org.apache.openjpa.jdbc.sql.PostgresDictionary.class.getName(),
+ "soliddb", org.apache.openjpa.jdbc.sql.SolidDBDictionary.class.getName(),
+ "sqlserver", org.apache.openjpa.jdbc.sql.SQLServerDictionary.class.getName(),
+ "sybase", org.apache.openjpa.jdbc.sql.SybaseDictionary.class.getName(),
+ "maxdb", MaxDBDictionary.class.getName(),
+ "jdbc:h2:", H2Dictionary.class.getName(),
+ "h2 database", H2Dictionary.class.getName()
+ };
+
+ for (int i = 0; i < aliases.length; i++) {
+ String key = aliases[i++];
+ String expected = aliases[i];
+ assertEquals(expected, DBDictionaryFactory.dictionaryClassForString(key, conf));
+ }
+ }
+
+}
diff --git a/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestSelectImpl.java b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestSelectImpl.java
new file mode 100644
index 0000000..21571c8
--- /dev/null
+++ b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestSelectImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.jdbc.sql;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
+import org.apache.openjpa.jdbc.identifier.DBIdentifier;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.Table;
+import static org.apache.openjpa.jdbc.sql.Select.FROM_SELECT_ALIAS;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Unit tests about SelectImpl.
+ */
+public class TestSelectImpl {
+
+ @Test
+ public void testSelectResultGetColumnAlias() {
+ verifySelectResultGetColumnAlias(true, false, false, -1, "Col", 56, "t56.\"Col\"");
+ verifySelectResultGetColumnAlias(false, false, false, -1, "Col", 18, "t18.Col");
+
+ // tests with additional select in from
+ verifySelectResultGetColumnAlias(false, true /* fromSelect */, false, -1, "Col", 18, null);
+ verifySelectResultGetColumnAlias(false, true, false, 92, "Col", 18, "t92_Col");
+ verifySelectResultGetColumnAlias(false, true, true /* requiresAliasForSubselect */, 92, "Col", 18, FROM_SELECT_ALIAS + ".t92_Col");
+
+ verifySelectResultGetColumnAlias(true, true, false, -1, "Col", 18, null);
+ verifySelectResultGetColumnAlias(true, true, false, 92, "Col", 18, "t92_Col");
+ verifySelectResultGetColumnAlias(true, true, true /* requiresAliasForSubselect */, 92, "Col", 18, FROM_SELECT_ALIAS + ".t92_Col");
+ }
+
+ private void verifySelectResultGetColumnAlias(boolean delimitIdentifiers, boolean fromSelect, boolean requiresAliasForSubselect,
+ int fromSelectTableIndex, String colName, int tableIndex, String expected) {
+ DBDictionary dict = new DBDictionary();
+ dict.setDelimitIdentifiers(delimitIdentifiers);
+ dict.requiresAliasForSubselect = requiresAliasForSubselect;
+ dict.setSupportsDelimitedIdentifiers(delimitIdentifiers);
+ dict.configureNamingRules();
+ DBIdentifier columnName = DBIdentifier.newColumn(colName, false);
+
+ JDBCConfiguration conf = new JDBCConfigurationImpl();
+ dict.setConfiguration(conf);
+ conf.setDBDictionary(dict);
+ SelectImpl selectImpl = new SelectImpl(conf) {
+ @Override
+ int getTableIndex(Table table, PathJoins pj, boolean create) {
+ return tableIndex;
+ }
+ };
+ Connection conn = null;
+ Statement stmnt = null;
+ ResultSet rs = null;
+ SelectImpl.SelectResult result = new SelectImpl.SelectResult(conn, stmnt, rs, dict);
+ result.setSelect(selectImpl);
+ PathJoins pj = null;
+ Column column = new Column(columnName, null);
+ if (fromSelect) {
+ SelectImpl fromSelectImpl = new SelectImpl(conf) {
+ @Override
+ int getTableIndex(Table table, PathJoins pj, boolean create) {
+ return fromSelectTableIndex;
+ }
+ };
+ selectImpl.setFromSelect(fromSelectImpl);
+ } else {
+ assertEquals(-1, fromSelectTableIndex);
+ }
+ String res = result.getColumnAlias(column, pj);
+ assertEquals(expected, res);
+ }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestDelimitIdentifiers.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestDelimitIdentifiers.java
new file mode 100644
index 0000000..505c8f0
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestDelimitIdentifiers.java
@@ -0,0 +1,536 @@
+/*
+ * 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.jdbc.sql;
+
+import static junit.framework.TestCase.assertEquals;
+import org.apache.commons.dbcp2.BasicDataSource;
+import org.apache.derby.jdbc.EmbeddedDriver;
+import org.apache.openjpa.persistence.PersistenceProviderImpl;
+import org.apache.openjpa.persistence.PersistenceUnitInfoImpl;
+import org.junit.Test;
+
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.assertNotNull;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import org.apache.openjpa.persistence.PersistentCollection;
+
+public class TestDelimitIdentifiers {
+
+ public static class LowercaseSchemaDerbyDBDictionary extends DerbyDictionary {
+
+ public LowercaseSchemaDerbyDBDictionary() {
+ super();
+ schemaCase = SCHEMA_CASE_LOWER;
+ delimitedCase = SCHEMA_CASE_PRESERVE;
+ setDelimitIdentifiers(true);
+ setSupportsDelimitedIdentifiers(true);
+ }
+ }
+
+ @Test
+ public void testDelimitIdentifiers() throws SQLException {
+ final PersistenceUnitInfoImpl persistenceUnitInfo = new PersistenceUnitInfoImpl();
+ persistenceUnitInfo.setExcludeUnlistedClasses(true);
+ persistenceUnitInfo.addManagedClassName(AllFieldTypes.class.getName());
+ final BasicDataSource ds = new BasicDataSource();
+ ds.setDriver(new EmbeddedDriver());
+ ds.setUrl("jdbc:derby:memory:TestDelimitIdentifiers;create=true");
+ persistenceUnitInfo.setNonJtaDataSource(ds);
+ // reproducer for OPENJPA-2818 delimitIdentifiers=true,delimitedCase=lower,schemaCase=lower
+ persistenceUnitInfo.setProperty("openjpa.jdbc.DBDictionary", LowercaseSchemaDerbyDBDictionary.class.getName());
+ new PersistenceProviderImpl().generateSchema(persistenceUnitInfo, new HashMap<>());
+ // try rebuild the schema
+ new PersistenceProviderImpl().generateSchema(persistenceUnitInfo, new HashMap<>());
+
+ final Map<String, Collection<String>> columns = new HashMap<>();
+ final Collection<String> createdTables = new HashSet<>();
+ try (final Connection connection = ds.getConnection()) {
+ try (final ResultSet tables = connection.getMetaData()
+ .getTables(null, null, "TestDelimitIdentifiers$AllFieldTypes%", null)) {
+ while (tables.next()) {
+ final String table = tables.getString(3);
+ createdTables.add(table);
+ }
+ }
+ for (final String table : createdTables) {
+ try (final Statement statement = connection.createStatement()) {
+ try (final ResultSet rs = statement.executeQuery("select * from \"" + table + "\"")) {
+ final ResultSetMetaData metaData = rs.getMetaData();
+ final Set<String> columnNames = new HashSet<>();
+ columns.put(table, columnNames);
+ for (int i = 1; i <= metaData.getColumnCount(); i++) {
+ columnNames.add(metaData.getColumnName(i));
+ }
+ }
+ }
+ }
+ }
+
+ final EntityManagerFactory entityManagerFactory = new PersistenceProviderImpl()
+ .createContainerEntityManagerFactory(persistenceUnitInfo, new HashMap());
+ try {
+ final AllFieldTypes entity = new AllFieldTypes();
+ final AllFieldTypes entity2 = new AllFieldTypes();
+ {
+ final EntityManager em = entityManagerFactory.createEntityManager();
+ em.getTransaction().begin();
+ try {
+ em.persist(entity2);
+ entity.setArrayOfStrings(new String[]{"a", "b"});
+ entity.setStringField("foo");
+ entity.setIntField(10);
+ entity.setSelfOneOne(entity2);
+ em.persist(entity);
+ em.getTransaction().commit();
+ } catch (final RuntimeException re) {
+ if (em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ throw re;
+ } finally {
+ em.close();
+ }
+ }
+ {
+ final EntityManager em = entityManagerFactory.createEntityManager();
+ try {
+ assertEquals(2, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x").
+ getResultList().size());
+ assertEquals(1, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x where x.stringField = 'foo'").
+ getResultList().size());
+ assertEquals(0, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x where x.stringField = 'bar'").
+ getResultList().size());
+ assertEquals(1, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x where x.intField >= 10").
+ getResultList().size());
+ } finally {
+ em.close();
+ }
+ }
+ } finally {
+ entityManagerFactory.close();
+ }
+ ds.close();
+ }
+
+ @Entity
+ public static class AllFieldTypes {
+
+ public enum EnumType {
+ Value1, Value2
+ };
+
+ // @Basic types
+ private short shortField;
+ private int intField;
+ private boolean booleanField;
+ private long longField;
+ private float floatField;
+ private char charField;
+ private double doubleField;
+ private byte byteField;
+ private Short wShortField;
+ private Integer wIntegerField;
+ private Boolean wBooleanField;
+ private Long wLongField;
+ private Float wFloatField;
+ private Character wCharacterField;
+ private Double wDoubleField;
+ private Byte wByteField;
+ private BigInteger bigIntegerField;
+ private BigDecimal bigDecimalField;
+ private String stringField;
+ private Date dateField;
+ private Calendar calendarField;
+ private java.sql.Date sqlDateField;
+ private java.sql.Time sqlTimeField;
+ private java.sql.Timestamp sqlTimestampField;
+ private byte[] byteLob;
+ private Byte[] wByteLob;
+ private char[] charLob;
+ private Character[] wCharacterLob;
+ private EnumType enumField;
+ private Serializable serializableField;
+
+ // Additional types
+ private Set<String> setOfStrings = new HashSet<>();
+ private String[] arrayOfStrings;
+
+ @PersistentCollection
+ private int[] arrayOfInts;
+
+ // one-to-one and one-to-many relations to self
+ @OneToOne
+ private AllFieldTypes selfOneOne;
+ @OneToMany
+ private List<AllFieldTypes> selfOneMany = new ArrayList<>();
+
+ // Java8 DateTime types which are required by the JPA-2.2 spec
+ private LocalDate localDateField;
+ private LocalTime localTimeField;
+ private LocalDateTime localDateTimeField;
+ private OffsetTime offsetTimeField;
+ private OffsetDateTime offsetDateTimeField;
+
+ public void setShortField(short shortField) {
+ this.shortField = shortField;
+ }
+
+ public short getShortField() {
+ return this.shortField;
+ }
+
+ public void setIntField(int intField) {
+ this.intField = intField;
+ }
+
+ public int getIntField() {
+ return this.intField;
+ }
+
+ public void setBooleanField(boolean booleanField) {
+ this.booleanField = booleanField;
+ }
+
+ public boolean getBooleanField() {
+ return this.booleanField;
+ }
+
+ public void setLongField(long longField) {
+ this.longField = longField;
+ }
+
+ public long getLongField() {
+ return this.longField;
+ }
+
+ public void setFloatField(float floatField) {
+ this.floatField = floatField;
+ }
+
+ public float getFloatField() {
+ return this.floatField;
+ }
+
+ public void setCharField(char charField) {
+ this.charField = charField;
+ }
+
+ public char getCharField() {
+ return this.charField;
+ }
+
+ public void setDoubleField(double doubleField) {
+ this.doubleField = doubleField;
+ }
+
+ public double getDoubleField() {
+ return this.doubleField;
+ }
+
+ public void setByteField(byte byteField) {
+ this.byteField = byteField;
+ }
+
+ public byte getByteField() {
+ return this.byteField;
+ }
+
+ public void setStringField(String stringField) {
+ this.stringField = stringField;
+ }
+
+ public String getStringField() {
+ return this.stringField;
+ }
+
+ public void setDateField(Date dateField) {
+ this.dateField = dateField;
+ }
+
+ public Date getDateField() {
+ return this.dateField;
+ }
+
+ public void setSetOfStrings(Set<String> setOfStrings) {
+ this.setOfStrings = setOfStrings;
+ }
+
+ public Set<String> getSetOfStrings() {
+ return this.setOfStrings;
+ }
+
+ public void setArrayOfStrings(String[] arrayOfStrings) {
+ this.arrayOfStrings = arrayOfStrings;
+ }
+
+ public String[] getArrayOfStrings() {
+ return this.arrayOfStrings;
+ }
+
+ public void setArrayOfInts(int[] arrayOfInts) {
+ this.arrayOfInts = arrayOfInts;
+ }
+
+ public int[] getArrayOfInts() {
+ return arrayOfInts;
+ }
+
+ public BigDecimal getBigDecimalField() {
+ return bigDecimalField;
+ }
+
+ public void setBigDecimalField(BigDecimal bigDecimalField) {
+ this.bigDecimalField = bigDecimalField;
+ }
+
+ public BigInteger getBigIntegerField() {
+ return bigIntegerField;
+ }
+
+ public void setBigIntegerField(BigInteger bigIntegerField) {
+ this.bigIntegerField = bigIntegerField;
+ }
+
+ public byte[] getByteLob() {
+ return byteLob;
+ }
+
+ public void setByteLob(byte[] byteLob) {
+ this.byteLob = byteLob;
+ }
+
+ public Calendar getCalendarField() {
+ return calendarField;
+ }
+
+ public void setCalendarField(Calendar calendarField) {
+ this.calendarField = calendarField;
+ }
+
+ public char[] getCharLob() {
+ return charLob;
+ }
+
+ public void setCharLob(char[] charLob) {
+ this.charLob = charLob;
+ }
+
+ public EnumType getEnumField() {
+ return enumField;
+ }
+
+ public void setEnumField(EnumType enumField) {
+ this.enumField = enumField;
+ }
+
+ public Serializable getSerializableField() {
+ return serializableField;
+ }
+
+ public void setSerializableField(Serializable serializableField) {
+ this.serializableField = serializableField;
+ }
+
+ public java.sql.Date getSqlDateField() {
+ return sqlDateField;
+ }
+
+ public void setSqlDateField(java.sql.Date sqlDateField) {
+ this.sqlDateField = sqlDateField;
+ }
+
+ public java.sql.Time getSqlTimeField() {
+ return sqlTimeField;
+ }
+
+ public void setSqlTimeField(java.sql.Time sqlTimeField) {
+ this.sqlTimeField = sqlTimeField;
+ }
+
+ public java.sql.Timestamp getSqlTimestampField() {
+ return sqlTimestampField;
+ }
+
+ public void setSqlTimestampField(java.sql.Timestamp sqlTimestampField) {
+ this.sqlTimestampField = sqlTimestampField;
+ }
+
+ public Boolean getWBooleanField() {
+ return wBooleanField;
+ }
+
+ public void setWBooleanField(Boolean booleanField) {
+ wBooleanField = booleanField;
+ }
+
+ public Byte getWByteField() {
+ return wByteField;
+ }
+
+ public void setWByteField(Byte byteField) {
+ wByteField = byteField;
+ }
+
+ public Byte[] getWByteLob() {
+ return wByteLob;
+ }
+
+ public void setWByteLob(Byte[] byteLob) {
+ wByteLob = byteLob;
+ }
+
+ public Character getWCharacterField() {
+ return wCharacterField;
+ }
+
+ public void setWCharacterField(Character characterField) {
+ wCharacterField = characterField;
+ }
+
+ public Character[] getWCharacterLob() {
+ return wCharacterLob;
+ }
+
+ public void setWCharacterLob(Character[] characterLob) {
+ wCharacterLob = characterLob;
+ }
+
+ public Double getWDoubleField() {
+ return wDoubleField;
+ }
+
+ public void setWDoubleField(Double doubleField) {
+ wDoubleField = doubleField;
+ }
+
+ public Float getWFloatField() {
+ return wFloatField;
+ }
+
+ public void setWFloatField(Float floatField) {
+ wFloatField = floatField;
+ }
+
+ public Integer getWIntegerField() {
+ return wIntegerField;
+ }
+
+ public void setWIntegerField(Integer integerField) {
+ wIntegerField = integerField;
+ }
+
+ public Long getWLongField() {
+ return wLongField;
+ }
+
+ public void setWLongField(Long longField) {
+ wLongField = longField;
+ }
+
+ public Short getWShortField() {
+ return wShortField;
+ }
+
+ public void setWShortField(Short shortField) {
+ wShortField = shortField;
+ }
+
+ public AllFieldTypes getSelfOneOne() {
+ return selfOneOne;
+ }
+
+ public void setSelfOneOne(AllFieldTypes selfOneOne) {
+ this.selfOneOne = selfOneOne;
+ }
+
+ public List<AllFieldTypes> getSelfOneMany() {
+ return selfOneMany;
+ }
+
+ public void setSelfOneMany(List<AllFieldTypes> selfOneMany) {
+ this.selfOneMany = selfOneMany;
+ }
+
+ public LocalDate getLocalDateField() {
+ return localDateField;
+ }
+
+ public void setLocalDateField(LocalDate localDateField) {
+ this.localDateField = localDateField;
+ }
+
+ public LocalTime getLocalTimeField() {
+ return localTimeField;
+ }
+
+ public void setLocalTimeField(LocalTime localTimeField) {
+ this.localTimeField = localTimeField;
+ }
+
+ public LocalDateTime getLocalDateTimeField() {
+ return localDateTimeField;
+ }
+
+ public void setLocalDateTimeField(LocalDateTime localDateTimeField) {
+ this.localDateTimeField = localDateTimeField;
+ }
+
+ public OffsetTime getOffsetTimeField() {
+ return offsetTimeField;
+ }
+
+ public void setOffsetTimeField(OffsetTime offsetTimeField) {
+ this.offsetTimeField = offsetTimeField;
+ }
+
+ public OffsetDateTime getOffsetDateTimeField() {
+ return offsetDateTimeField;
+ }
+
+ public void setOffsetDateTimeField(OffsetDateTime offsetDateTimeField) {
+ this.offsetDateTimeField = offsetDateTimeField;
+ }
+ }
+}