This is an automated email from the ASF dual-hosted git repository. solomax 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 cb20dd6 [OPENJPA-2777] Javax index (#39) cb20dd6 is described below commit cb20dd6b95a07fe87bf46a2c33580322d0b11cd0 Author: Maxim Solodovnik <solomax...@gmail.com> AuthorDate: Tue Mar 5 20:44:49 2019 +0700 [OPENJPA-2777] Javax index (#39) [OPENJPA-2777] javax.persistense.Index can be used on Table annotation --- .../org/apache/openjpa/jdbc/meta/ClassMapping.java | 1 + .../apache/openjpa/jdbc/meta/ClassMappingInfo.java | 126 ++++++++++++++++----- .../apache/openjpa/jdbc/meta/localizer.properties | 5 + .../jdbc/AnnotationPersistenceMappingParser.java | 41 ++++++- .../openjpa/persistence/jdbc/localizer.properties | 4 + .../jdbc/annotations/EntityWithIndices.java | 76 +++++++++++++ .../persistence/jdbc/annotations/TestIndices.java | 64 +++++++++++ 7 files changed, 286 insertions(+), 31 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java index a1a387f..1e35054 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java @@ -903,6 +903,7 @@ public class ClassMapping // once columns are resolved, resolve unique constraints as they need // the columns be resolved _info.getUniques(this, true); + _info.getIndices(this, true); } /** diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java index 0cad85d..60cc2a0 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java @@ -34,6 +34,7 @@ import org.apache.openjpa.jdbc.identifier.QualifiedDBIdentifier; import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy; import org.apache.openjpa.jdbc.schema.Column; import org.apache.openjpa.jdbc.schema.ForeignKey; +import org.apache.openjpa.jdbc.schema.Index; import org.apache.openjpa.jdbc.schema.Schema; import org.apache.openjpa.jdbc.schema.SchemaGroup; import org.apache.openjpa.jdbc.schema.Table; @@ -77,6 +78,7 @@ public class ClassMappingInfo // Unique constraints indexed by primary or secondary table name private Map<DBIdentifier,List<Unique>> _uniques; + private Map<DBIdentifier,List<Index>> _indices = new HashMap<>(); /** * The described class name. */ @@ -452,13 +454,21 @@ public class ClassMappingInfo } } if (cinfo._uniques != null) { - if (_uniques == null) - _uniques = new HashMap<>(); - for (Entry<DBIdentifier, List<Unique>> entry : cinfo._uniques.entrySet()) - if (!_uniques.containsKey(entry.getKey())) - _uniques.put(entry.getKey(), entry.getValue()); + if (_uniques == null) { + _uniques = new HashMap<>(); + } + for (Entry<DBIdentifier, List<Unique>> entry : cinfo._uniques.entrySet()) { + if (!_uniques.containsKey(entry.getKey())) { + _uniques.put(entry.getKey(), entry.getValue()); + } + } + } + _indices.clear(); + for (Entry<DBIdentifier, List<Index>> entry : cinfo._indices.entrySet()) { + if (!_indices.containsKey(entry.getKey())) { + _indices.put(entry.getKey(), entry.getValue()); + } } - } /** @@ -480,24 +490,50 @@ public class ClassMappingInfo * @param unique the unique constraint. null means no-op. */ public void addUnique(DBIdentifier table, Unique unique) { - if (!DBIdentifier.equal(_tableName, table) && - (_seconds == null || !_seconds.containsKey(table))) { + if (!DBIdentifier.equal(_tableName, table) && + (_seconds == null || !_seconds.containsKey(table))) { throw new UserException(_loc.get("unique-no-table", new Object[]{table, _className, _tableName, ((_seconds == null) ? "" : _seconds.keySet())})); - } - if (unique == null) - return; + } + if (unique == null) + return; if (_uniques == null) _uniques = new HashMap<>(); unique.setTableIdentifier(table); List<Unique> uniques = _uniques.get(table); if (uniques == null) { - uniques = new ArrayList<>(); - uniques.add(unique); - _uniques.put(table, uniques); + uniques = new ArrayList<>(); + uniques.add(unique); + _uniques.put(table, uniques); } else { - uniques.add(unique); + uniques.add(unique); + } + } + + /** + * Add index for the given table. + * @param table must be primary table or secondary table name added a + * priori to this receiver. + * @param idx the index. null means no-op. + */ + public void addIndex(DBIdentifier table, Index idx) { + if (!DBIdentifier.equal(_tableName, table) && + (_seconds == null || !_seconds.containsKey(table))) { + throw new UserException(_loc.get("index-no-table", + new Object[]{table, _className, _tableName, + ((_seconds == null) ? "" : _seconds.keySet())})); + } + if (idx == null) + return; + idx.setTableIdentifier(table); + List<Index> indices = _indices.get(table); + if (indices == null) { + indices = new ArrayList<>(); + indices.add(idx); + _indices.put(table, indices); + } else { + indices.add(idx); } } @@ -531,31 +567,65 @@ public class ClassMappingInfo return new Unique[0]; List<Unique> result = new ArrayList<>(); for (DBIdentifier tableName : _uniques.keySet()) { - List<Unique> uniqueConstraints = _uniques.get(tableName); - for (Unique template : uniqueConstraints) { - Column[] templateColumns = template.getColumns(); + List<Unique> uniqueConstraints = _uniques.get(tableName); + for (Unique template : uniqueConstraints) { + Column[] templateColumns = template.getColumns(); Column[] uniqueColumns = new Column[templateColumns.length]; Table table = getTable((ClassMapping)cm, tableName, adapt); - for (int i=0; i<uniqueColumns.length; i++) { + for (int i=0; i<uniqueColumns.length; i++) { DBIdentifier columnName = templateColumns[i].getIdentifier(); - if (!table.containsColumn(columnName)) { + if (!table.containsColumn(columnName)) { throw new UserException(_loc.get( "unique-missing-column", new Object[]{cm, columnName, tableName, Arrays.toString(table.getColumnNames())})); - } + } Column uniqueColumn = table.getColumn(columnName); - uniqueColumns[i] = uniqueColumn; - } - Unique unique = createUnique(cm, "unique", template, - uniqueColumns, adapt); - if (unique != null) - result.add(unique); - } + uniqueColumns[i] = uniqueColumn; + } + Unique unique = createUnique(cm, "unique", template, + uniqueColumns, adapt); + if (unique != null) + result.add(unique); + } } return result.toArray(new Unique[result.size()]); } + /** + * Get all indices associated with both the primary and/or + * secondary tables. + * + */ + public Index[] getIndices(MetaDataContext cm, boolean adapt) { + if (_indices.isEmpty()) + return new Index[0]; + List<Index> result = new ArrayList<>(); + for (DBIdentifier tableName : _indices.keySet()) { + List<Index> indices = _indices.get(tableName); + for (Index template : indices) { + Column[] templateColumns = template.getColumns(); + Column[] columns = new Column[templateColumns.length]; + Table table = getTable((ClassMapping)cm, tableName, adapt); + for (int i = 0; i < columns.length; i++) { + DBIdentifier columnName = templateColumns[i].getIdentifier(); + if (!table.containsColumn(columnName)) { + throw new UserException(_loc.get( + "index-missing-column", + new Object[]{cm, columnName, tableName, + Arrays.toString(table.getColumnNames())})); + } + Column column = table.getColumn(columnName); + columns[i] = column; + } + Index idx = createIndex(cm, "index", template, columns, adapt); + if (idx != null) + result.add(idx); + } + } + return result.toArray(new Index[result.size()]); + } + @Override public File getSourceFile() { return _file; diff --git a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties index 36079b2..f267615 100644 --- a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties +++ b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties @@ -416,6 +416,11 @@ unique-missing-column: The column "{1}" in a unique constraint in "{0}" on \ unique-no-table: A unique constraint on table "{0}" can not be added to \ mapping of class "{1}" because the table does neither match its primary \ table "{2}" nor any of its secondary table(s) "{3}". +index-no-table: Index on table "{0}" can not be added to \ + mapping of class "{1}" because the table does neither match its primary \ + table "{2}" nor any of its secondary table(s) "{3}". +index-missing-column: The column "{1}" in a index in "{0}" on \ + table "{2}" can not be found in the list of available columns "{3}". bad-version-column-table: One of the version column "{0}" has been associated \ with table "{1}", but no primary or secondary table of such name exists. version-type-unsupported: Version field "{0}" of {1} is not supported. diff --git a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java index 592328a..d22431b 100644 --- a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java +++ b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java @@ -615,6 +615,7 @@ public class AnnotationPersistenceMappingParser } addUniqueConstraints(tName.getName(), cm, cm.getMappingInfo(), table.uniqueConstraints()); + addIndices(tName.getName(), cm, cm.getMappingInfo(), table.indexes()); } Unique createUniqueConstraint(MetaDataContext ctx, UniqueConstraint anno) { @@ -643,7 +644,7 @@ public class AnnotationPersistenceMappingParser Unique unique = createUniqueConstraint(ctx, anno); unique.setTableIdentifier(DBIdentifier.newTable(table, delimit())); if (info instanceof ClassMappingInfo) - ((ClassMappingInfo) info).addUnique(table, unique); + ((ClassMappingInfo) info).addUnique(DBIdentifier.newTable(table), unique); else if (info instanceof FieldMappingInfo) ((FieldMappingInfo) info).addJoinTableUnique(unique); else @@ -651,6 +652,40 @@ public class AnnotationPersistenceMappingParser } } + + org.apache.openjpa.jdbc.schema.Index createIndex(MetaDataContext ctx, javax.persistence.Index anno) { + String columnNames = anno.columnList(); + if (StringUtil.isEmpty(columnNames)) + throw new UserException(_loc.get("index-no-column", ctx)); + DBIdentifier[] sColNames = DBIdentifier.toArray(columnNames.split(","), DBIdentifierType.COLUMN, delimit()); + org.apache.openjpa.jdbc.schema.Index indx = new org.apache.openjpa.jdbc.schema.Index(); + for (int i = 0; i < sColNames.length; i++) { + if (DBIdentifier.isEmpty(sColNames[i])) + throw new UserException(_loc.get("index-empty-column", + Arrays.toString(sColNames), ctx)); + Column column = new Column(); + column.setIdentifier(sColNames[i]); + indx.addColumn(column); + } + indx.setUnique(anno.unique()); + if (!StringUtil.isEmpty(anno.name())) { + indx.setIdentifier(DBIdentifier.newConstraint(anno.name(), delimit())); + } + return indx; + } + + void addIndices(String table, MetaDataContext ctx, + MappingInfo info, javax.persistence.Index... indices) { + for (javax.persistence.Index anno : indices) { + org.apache.openjpa.jdbc.schema.Index idx = createIndex(ctx, anno); + idx.setTableIdentifier(DBIdentifier.newTable(table, delimit())); + if (info instanceof ClassMappingInfo) + ((ClassMappingInfo) info).addIndex(DBIdentifier.newTable(table), idx); + else + throw new InternalException(); + } + } + /** * Form a qualified table name from a schema and table name. */ @@ -1644,7 +1679,7 @@ public class AnnotationPersistenceMappingParser // cache the JAXB XmlRootElement class if it is present so we do not // have a hard-wired dependency on JAXB here - Class xmlRootElementClass = null; + Class<?> xmlRootElementClass = null; try { xmlRootElementClass = Class.forName("javax.xml.bind.annotation.XmlRootElement"); } catch (Exception e) { @@ -1669,7 +1704,7 @@ public class AnnotationPersistenceMappingParser .getDBDictionary(); if (dict.supportsXMLColumn) // column maps to xml type - ((Column) cols.get(i)).setTypeIdentifier(DBIdentifier.newColumnDefinition(dict.xmlTypeName)); + cols.get(i).setTypeIdentifier(DBIdentifier.newColumnDefinition(dict.xmlTypeName)); } unique |= (pcols[i].unique()) ? TRUE : FALSE; diff --git a/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties b/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties index f31c6de..b4ab97c 100644 --- a/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties +++ b/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties @@ -53,6 +53,10 @@ unique-empty-column: A unique constraint "{0}" specified in mapping of class \ unique-many-on-seq-unsupported: More than one unique constraints is specified \ on sequence generator "{1}" in "{0}". But multiple unique constraint on \ sequence generator is currently not supported. +index-no-column: An index specified in mapping of "{0}" specified \ + no column. +index-empty-column: An index "{0}" specified in mapping of class \ + "{1}" includes an empty column. discriminator-on-abstract-class: A discriminator value has been specified for \ the abstract class "{0}". The discriminator will never be used and may be \ safely removed. diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EntityWithIndices.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EntityWithIndices.java new file mode 100644 index 0000000..cc073d1 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EntityWithIndices.java @@ -0,0 +1,76 @@ +/* + * 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.annotations; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; + +@Entity +@Table(name = "INDICES1" + , indexes = {@Index(name = "idx_index1", columnList = "index1") + , @Index(name = "idx_long", columnList = "LONG_NAME", unique = true)}) +public class EntityWithIndices { + @Id + @Column(name = "PK") + private Long pk; + + @Column(name = "INDEX1") + private String index1; + + @Column(name = "LONG_NAME") + private String longName; + + @Column(name = "NAME") + private String name; + + public Long getPk() { + return pk; + } + + public void setPk(Long pk) { + this.pk = pk; + } + + public String getIndex1() { + return index1; + } + + public void setIndex1(String index1) { + this.index1 = index1; + } + + public String getLongName() { + return longName; + } + + public void setLongName(String longName) { + this.longName = longName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestIndices.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestIndices.java new file mode 100644 index 0000000..f105d64 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestIndices.java @@ -0,0 +1,64 @@ +/* + * 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.annotations; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.identifier.DBIdentifier; +import org.apache.openjpa.jdbc.meta.ClassMapping; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.jdbc.schema.Index; +import org.apache.openjpa.jdbc.schema.Table; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.junit.Test; + +public class TestIndices extends SingleEMFTestCase { + @Override + public void setUp() { + setUp(EntityWithIndices.class, CLEAR_TABLES +// ,"openjpa.Log","SQL=trace" + ); + } + + @Test + public void testIndicesCreated() { + JDBCConfiguration conf = (JDBCConfiguration) emf.getConfiguration(); + ClassMapping cls = conf.getMappingRepositoryInstance().getMapping(EntityWithIndices.class, null, true); + Table table = cls.getTable(); + Index idx1 = table.getIndex(DBIdentifier.newIndex("idx_index1")); + assertNotNull("Defined index should exist", idx1); + assertFalse(idx1.isUnique()); + + Index idx2 = table.getIndex(DBIdentifier.newIndex("idx_long")); + assertNotNull("Defined index should exist", idx2); + assertTrue(idx2.isUnique()); + + Set<String> indexedCols = new HashSet<>(); + for (Index idx : table.getIndexes()) { + for (Column col : idx.getColumns()) { + indexedCols.add(col.getIdentifier().getName()); + } + } + assertTrue(indexedCols.contains("INDEX1")); + assertTrue(indexedCols.contains("LONG_NAME")); + assertFalse(indexedCols.contains("NAME")); + } +}