[OPENJPA-2777] Javax index (#39)

[OPENJPA-2777] javax.persistense.Index can be used on Table annotation
This commit is contained in:
Maxim Solodovnik 2019-03-05 20:44:49 +07:00 committed by GitHub
parent 44aede26df
commit cb20dd6b95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 286 additions and 31 deletions

View File

@ -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);
}
/**

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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"));
}
}