diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java index 6e00d1225..62c2353f8 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java @@ -141,6 +141,15 @@ public class FieldMapping return _fk.getTable(); if (_val.getForeignKey() != null) return _val.getForeignKey().getTable(); + + // if this is a map of bi-directional relation, + // the column of this field should be in the table + // of the entity that is the value of the map + if (_val.getDeclaredTypeCode() == JavaTypes.MAP) { + ClassMapping meta = _elem.getDeclaredTypeMapping(); + if (meta != null) + return meta.getTable(); + } return getDefiningMapping().getTable(); } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java index 9a07efaa8..57e7c5ef2 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java @@ -882,6 +882,11 @@ public class MappingRepository return new RelationMapInverseKeyFieldStrategy(); return new RelationMapTableFieldStrategy(); } + //TODO: in JPA 2.0 if MapKeyClass type is not specified + // an exception is thrown. In OpenJpa 1.x, the map will + // be serialized to a blob (the null value returned by + // this method will lead to a strategy to serialize + // the map). if (!krel && khandler == null) break; if (!vrel && vhandler == null) @@ -901,6 +906,7 @@ public class MappingRepository */ protected FieldStrategy handlerCollectionStrategy(FieldMapping field, ValueHandler ehandler, boolean installHandlers) { + // TODO: JPA 2.0 should ignore this flag and not to serialize if (getConfiguration().getCompatibilityInstance() .getStoreMapCollectionInEntityAsBlob()) return null; @@ -916,6 +922,7 @@ public class MappingRepository protected FieldStrategy handlerMapStrategy(FieldMapping field, ValueHandler khandler, ValueHandler vhandler, boolean krel, boolean vrel, boolean installHandlers) { + // TODO: JPA 2.0 should ignore this flag and not to serialize if (getConfiguration().getCompatibilityInstance() .getStoreMapCollectionInEntityAsBlob()) return null; diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java index a239473d3..21861e450 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java @@ -97,10 +97,7 @@ public class HandlerCollectionTableFieldStrategy throw new MetaDataException(_loc.get("not-coll", field)); assertNotMappedBy(); - // JPA2.0 allows schema for @CollectionTable(used with - // @ElementCollection) - if (!field.isElementCollection()) - field.getValueInfo().assertNoSchemaComponents(field, !adapt); + field.getValueInfo().assertNoSchemaComponents(field, !adapt); field.getKeyMapping().getValueInfo().assertNoSchemaComponents (field.getKey(), !adapt); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java index e7859a2c4..d64c4302a 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java @@ -108,10 +108,15 @@ public class HandlerHandlerMapTableFieldStrategy field.mapJoin(adapt, true); _kio = new ColumnIO(); - DBDictionary dict = field.getMappingRepository().getDBDictionary(); - _kcols = HandlerStrategies.map(key, - dict.getValidColumnName("key", field.getTable()), _kio, adapt); - + List columns = key.getValueInfo().getColumns(); + if (columns != null && columns.size() > 0) { + // MapKeyColumn is used + _kcols = HandlerStrategies.map(key, "key", _kio, adapt); + } else { + DBDictionary dict = field.getMappingRepository().getDBDictionary(); + _kcols = HandlerStrategies.map(key, + dict.getValidColumnName("key", field.getTable()), _kio, adapt); + } _vio = new ColumnIO(); _vcols = HandlerStrategies.map(val, "value", _vio, adapt); field.mapPrimaryKey(adapt); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java index a931b4c0c..5c9307bb6 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java @@ -51,6 +51,10 @@ public class HandlerRelationMapTableFieldStrategy return _kcols; } + public ColumnIO getKeyColumnIO() { + return _kio; + } + public Column[] getValueColumns(ClassMapping cls) { return field.getElementMapping().getColumns(); } @@ -83,8 +87,11 @@ public class HandlerRelationMapTableFieldStrategy sel.select(_kcols); sel.whereForeignKey(field.getJoinForeignKey(), sm.getObjectId(), field.getDefiningMapping(), store); - - Joins joins = joinValueRelation(sel.newJoins(), vals[idx]); + FieldMapping mapped = field.getMappedByMapping(); + Joins joins = null; + if (mapped == null) + joins = joinValueRelation(sel.newJoins(), vals[idx]); + sel.select(vals[idx], field.getElementMapping(). getSelectSubclasses(), store, fetch, eagerMode, joins); @@ -129,23 +136,28 @@ public class HandlerRelationMapTableFieldStrategy ValueMapping val = field.getElementMapping(); if (val.getTypeCode() != JavaTypes.PC || val.isEmbeddedPC()) throw new MetaDataException(_loc.get("not-relation", val)); - assertNotMappedBy(); + FieldMapping mapped = field.getMappedByMapping(); + + if (mapped != null) // map to the owner table + handleMappedBy(adapt); + else { + // map to a separate table + field.mapJoin(adapt, true); + if (val.getTypeMapping().isMapped()) { + ValueMappingInfo vinfo = val.getValueInfo(); + ForeignKey fk = vinfo.getTypeJoin(val, "value", false, adapt); + val.setForeignKey(fk); + val.setColumnIO(vinfo.getColumnIO()); + } else + RelationStrategies.mapRelationToUnmappedPC(val, "value", adapt); - field.mapJoin(adapt, true); + val.mapConstraints("value", adapt); + } _kio = new ColumnIO(); DBDictionary dict = field.getMappingRepository().getDBDictionary(); _kcols = HandlerStrategies.map(key, dict.getValidColumnName("key", field.getTable()), _kio, adapt); - if (val.getTypeMapping().isMapped()) { - ValueMappingInfo vinfo = val.getValueInfo(); - ForeignKey fk = vinfo.getTypeJoin(val, "value", false, adapt); - val.setForeignKey(fk); - val.setColumnIO(vinfo.getColumnIO()); - } else - RelationStrategies.mapRelationToUnmappedPC(val, "value", adapt); - - val.mapConstraints("value", adapt); field.mapPrimaryKey(adapt); } @@ -164,6 +176,8 @@ public class HandlerRelationMapTableFieldStrategy throws SQLException { if (map == null || map.isEmpty()) return; + if (field.getMappedBy() != null) + return; Row row = rm.getSecondaryRow(field.getTable(), Row.ACTION_INSERT); row.setForeignKey(field.getJoinForeignKey(), field.getJoinColumnIO(), diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java index 4a7598ebc..f1411167d 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java @@ -26,6 +26,7 @@ import org.apache.openjpa.jdbc.kernel.JDBCStore; import org.apache.openjpa.jdbc.meta.ClassMapping; import org.apache.openjpa.jdbc.meta.FieldMapping; import org.apache.openjpa.jdbc.meta.FieldStrategy; +import org.apache.openjpa.jdbc.meta.ValueMapping; import org.apache.openjpa.jdbc.schema.ForeignKey; import org.apache.openjpa.jdbc.sql.Joins; import org.apache.openjpa.jdbc.sql.Result; @@ -33,6 +34,7 @@ import org.apache.openjpa.jdbc.sql.Row; import org.apache.openjpa.jdbc.sql.RowManager; import org.apache.openjpa.jdbc.sql.Select; import org.apache.openjpa.kernel.OpenJPAStateManager; +import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.util.MetaDataException; @@ -173,4 +175,78 @@ public abstract class MapTableFieldStrategy protected ClassMapping[] getIndependentElementMappings(boolean traverse) { return ClassMapping.EMPTY_MAPPINGS; } + + protected void handleMappedBy(boolean adapt){ + boolean criteria = field.getValueInfo().getUseClassCriteria(); + // check for named inverse + FieldMapping mapped = field.getMappedByMapping(); + if (mapped != null) { + field.getMappingInfo().assertNoSchemaComponents(field, !adapt); + field.getValueInfo().assertNoSchemaComponents(field, !adapt); + mapped.resolve(mapped.MODE_META | mapped.MODE_MAPPING); + + if (!mapped.isMapped() || mapped.isSerialized()) + throw new MetaDataException(_loc.get("mapped-by-unmapped", + field, mapped)); + + if (mapped.getTypeCode() == JavaTypes.PC) { + if (mapped.getJoinDirection() == mapped.JOIN_FORWARD) { + field.setJoinDirection(field.JOIN_INVERSE); + field.setColumns(mapped.getDefiningMapping(). + getPrimaryKeyColumns()); + } else if (isTypeUnjoinedSubclass(mapped)) + throw new MetaDataException(_loc.get + ("mapped-inverse-unjoined", field.getName(), + field.getDefiningMapping(), mapped)); + ForeignKey fk = mapped.getForeignKey(field.getDefiningMapping()); + field.setForeignKey(fk); + field.setJoinForeignKey(fk); + } else if (mapped.getElement().getTypeCode() == JavaTypes.PC) { + if (isTypeUnjoinedSubclass(mapped.getElementMapping())) + throw new MetaDataException(_loc.get + ("mapped-inverse-unjoined", field.getName(), + field.getDefiningMapping(), mapped)); + + // warn the user about making the collection side the owner + Log log = field.getRepository().getLog(); + if (log.isInfoEnabled()) + log.info(_loc.get("coll-owner", field, mapped)); + ValueMapping elem = mapped.getElementMapping(); + ForeignKey fk = elem.getForeignKey(); + field.setForeignKey(fk); + field.setJoinForeignKey(fk); + } else + throw new MetaDataException(_loc.get("not-inv-relation", + field, mapped)); + + field.setUseClassCriteria(criteria); + return; + } +/* + // this is necessary to support openjpa 3 mappings, which didn't + // differentiate between secondary table joins and relations built + // around an inverse key: check to see if we're mapped as a secondary + // table join but we're in the table of the related type, and if so + // switch our join mapping info to our value mapping info + String tableName = field.getMappingInfo().getTableName(); + Table table = field.getTypeMapping().getTable(); + ValueMappingInfo vinfo = field.getValueInfo(); + if (tableName != null && table != null + && (tableName.equalsIgnoreCase(table.getName()) + || tableName.equalsIgnoreCase(table.getFullName()))) { + vinfo.setJoinDirection(MappingInfo.JOIN_INVERSE); + vinfo.setColumns(field.getMappingInfo().getColumns()); + field.getMappingInfo().setTableName(null); + field.getMappingInfo().setColumns(null); + } +*/ + } + + protected boolean isTypeUnjoinedSubclass(ValueMapping mapped) { + ClassMapping def = field.getDefiningMapping(); + for (; def != null; def = def.getJoinablePCSuperclassMapping()) + if (def == mapped.getTypeMapping()) + return false; + return true; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java index c9098e45b..2c3eb143c 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.openjpa.enhance.PersistenceCapable; import org.apache.openjpa.jdbc.conf.JDBCConfiguration; @@ -33,6 +34,7 @@ import org.apache.openjpa.jdbc.kernel.JDBCStoreManager; import org.apache.openjpa.jdbc.meta.ClassMapping; import org.apache.openjpa.jdbc.meta.Embeddable; import org.apache.openjpa.jdbc.meta.FieldMapping; +import org.apache.openjpa.jdbc.meta.FieldStrategy; import org.apache.openjpa.jdbc.meta.Joinable; import org.apache.openjpa.jdbc.meta.MappingInfo; import org.apache.openjpa.jdbc.meta.ValueMapping; @@ -57,6 +59,7 @@ import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.ClassMetaData; +import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.util.ApplicationIds; import org.apache.openjpa.util.ImplHelper; @@ -213,10 +216,67 @@ public class RelationFieldStrategy updateInverse(sm, rel, store, rm); else { Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT); - if (row != null) + if (row != null) { field.setForeignKey(row, rel); + // this is for bi-directional maps, the key and value of the + // map are stored in the table of the mapped-by entity + setMapKey(sm, rel, store, row); + } } } + + private void setMapKey(OpenJPAStateManager sm, OpenJPAStateManager rel, + JDBCStore store, Row row) throws SQLException { + if (rel == null) + return; + ClassMetaData meta = rel.getMetaData(); + FieldMapping[] fields = ((ClassMapping)meta).getFieldMappings(); + for (int i = 0; i < fields.length; i++) { + FieldMetaData mappedBy = fields[i].getMappedByMetaData(); + if (mappedBy == field) { + if (fields[i].getDeclaredTypeCode() == JavaTypes.MAP) { + Map mapObj = (Map)rel.fetchObjectField( + fields[i].getIndex()); + Object keyObj = getMapKeyObj(mapObj, + sm.getPersistenceCapable()); + ValueMapping key = fields[i].getKeyMapping(); + if (!key.isEmbedded()) { + if (keyObj instanceof PersistenceCapable) { + OpenJPAStateManager keySm = RelationStrategies. + getStateManager(keyObj, store.getContext()); + // key is an entity + ForeignKey fk = fields[i].getKeyMapping(). + getForeignKey(); + ColumnIO io = new ColumnIO(); + row.setForeignKey(fk, io, keySm); + } + } else { + // key is an embeddable or basic type + FieldStrategy strategy = fields[i].getStrategy(); + if (strategy instanceof + HandlerRelationMapTableFieldStrategy) { + HandlerRelationMapTableFieldStrategy strat = + (HandlerRelationMapTableFieldStrategy) strategy; + Column[] kcols = strat.getKeyColumns((ClassMapping)meta); + ColumnIO kio = strat.getKeyColumnIO(); + HandlerStrategies.set(key, keyObj, store, row, kcols, + kio, true); + } + } + break; + } + } + } + } + + private Object getMapKeyObj(Map mapObj, Object value) { + Set keySet = mapObj.keySet(); + for (Object key : keySet) { + if (mapObj.get(key) == value) + return key; + } + return null; + } public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm) throws SQLException { diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java index 60770b6cf..c4c96fe0d 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java @@ -110,7 +110,10 @@ public class RelationRelationMapTableFieldStrategy // order before select in case we're faking union with // multiple selects; order vals used to merge results - Joins joins = joinValueRelation(sel.newJoins(), vals[idx]); + FieldMapping mapped = field.getMappedByMapping(); + Joins joins = null; + if (mapped == null) + joins = joinValueRelation(sel.newJoins(), vals[idx]); sel.orderBy(field.getKeyMapping().getColumns(), true, true); sel.select(vals[idx], field.getElementMapping(). getSelectSubclasses(), store, fetch, eagerMode, joins); @@ -176,11 +179,14 @@ public class RelationRelationMapTableFieldStrategy ValueMapping val = field.getElementMapping(); if (val.getTypeCode() != JavaTypes.PC || val.isEmbeddedPC()) throw new MetaDataException(_loc.get("not-relation", val)); - assertNotMappedBy(); - - field.mapJoin(adapt, true); + FieldMapping mapped = field.getMappedByMapping(); + if (mapped != null) + handleMappedBy(adapt); + else { + field.mapJoin(adapt, true); + mapTypeJoin(val, "value", adapt); + } mapTypeJoin(key, "key", adapt); - mapTypeJoin(val, "value", adapt); field.mapPrimaryKey(adapt); } @@ -212,6 +218,9 @@ public class RelationRelationMapTableFieldStrategy throws SQLException { if (map == null || map.isEmpty()) return; + + if (field.getMappedBy() != null) + return; Row row = rm.getSecondaryRow(field.getTable(), Row.ACTION_INSERT); row.setForeignKey(field.getJoinForeignKey(), field.getJoinColumnIO(), diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java index 51cfe2e2c..ff289c8e3 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java @@ -432,7 +432,11 @@ public class ValueMetaDataImpl _owner.getDefiningMetaData().getEnvClassLoader(), false); if (meta != null) _decCode = JavaTypes.PC; - if (!isEmbedded()) + + if (meta != null && meta.isEmbeddedOnly()) + setEmbedded(true); + + if (!isEmbedded()) _decTypeMeta = meta; } 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 867959a81..54a5d7192 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 @@ -44,6 +44,9 @@ import javax.persistence.Inheritance; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.JoinTable; +import javax.persistence.MapKeyColumn; +import javax.persistence.MapKeyJoinColumn; +import javax.persistence.MapKeyJoinColumns; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.PrimaryKeyJoinColumns; import javax.persistence.SecondaryTable; @@ -141,6 +144,9 @@ public class AnnotationPersistenceMappingParser _tags.put(KeyJoinColumns.class, KEY_JOIN_COLS); _tags.put(KeyNonpolymorphic.class, KEY_NONPOLY); _tags.put(KeyStrategy.class, KEY_STRAT); + _tags.put(MapKeyColumn.class, MAP_KEY_COL); + _tags.put(MapKeyJoinColumn.class, MAP_KEY_JOIN_COL); + _tags.put(MapKeyJoinColumns.class, MAP_KEY_JOIN_COLS); _tags.put(PrimaryKeyJoinColumn.class, PK_JOIN_COL); _tags.put(PrimaryKeyJoinColumns.class, PK_JOIN_COLS); _tags.put(SecondaryTable.class, SECONDARY_TABLE); @@ -1051,6 +1057,16 @@ public class AnnotationPersistenceMappingParser fm.getKeyMapping().getValueInfo() .setStrategy(((KeyStrategy) anno).value()); break; + case MAP_KEY_COL: + parseMapKeyColumn(fm, (MapKeyColumn) anno); + break; + case MAP_KEY_JOIN_COL: + parseMapKeyJoinColumns(fm, (MapKeyJoinColumn) anno); + break; + case MAP_KEY_JOIN_COLS: + parseMapKeyJoinColumns(fm, + ((MapKeyJoinColumns) anno).value()); + break; case PK_JOIN_COL: parsePrimaryKeyJoinColumns(fm, (PrimaryKeyJoinColumn) anno); break; @@ -1699,4 +1715,89 @@ public class AnnotationPersistenceMappingParser col.setFlag (Column.FLAG_UNUPDATABLE, !join.updatable ()); return col; } + + /** + * Parse @MapKeyColumn. + */ + protected void parseMapKeyColumn(FieldMapping fm, MapKeyColumn anno) { + if (!fm.isElementCollection()) + throw new UnsupportedException(_loc.get("unsupported", fm, + anno.toString())); + + int unique = 0; + Column col = new Column(); + setupMapKeyColumn(fm, col, anno); + unique |= (anno.unique()) ? TRUE : FALSE; + setMapKeyColumn(fm, fm.getKeyMapping().getValueInfo(), col, unique); + } + + /** + * Setup the given column with information from the given annotation. + */ + private static void setupMapKeyColumn(FieldMapping fm, Column col, + MapKeyColumn anno) { + if (!StringUtils.isEmpty(anno.name())) + col.setName(anno.name()); + else + col.setName(fm.getName() + "_" + "KEY"); + if (!StringUtils.isEmpty(anno.columnDefinition())) + col.setTypeName(anno.columnDefinition()); + if (anno.precision() != 0) + col.setSize(anno.precision()); + else if (anno.length() != 255) + col.setSize(anno.length()); + col.setNotNull(!anno.nullable()); + col.setDecimalDigits(anno.scale()); + col.setFlag(Column.FLAG_UNINSERTABLE, !anno.insertable()); + col.setFlag(Column.FLAG_UNUPDATABLE, !anno.updatable()); + } + + /** + * Set the given map key column as the map key column for fm. + * + * @param unique bitwise combination of TRUE and FALSE for the + * unique attribute of the column + */ + protected void setMapKeyColumn(FieldMapping fm, MappingInfo info, + Column col, int unique) { + List cols = new ArrayList(); + cols.add(col); + info.setColumns(cols); + if (unique == TRUE) + info.setUnique(new org.apache.openjpa.jdbc.schema.Unique()); + } + + /** + * Parse @MapKeyJoinColumn(s). + */ + private void parseMapKeyJoinColumns(FieldMapping fm, MapKeyJoinColumn... joins) { + if (joins.length == 0) + return; + + List cols = new ArrayList(joins.length); + int unique = 0; + for (int i = 0; i < joins.length; i++) { + cols.add(newColumn(joins[i])); + unique |= (joins[i].unique()) ? TRUE : FALSE; + } + setColumns(fm, fm.getKeyMapping().getValueInfo(), cols, unique); + } + + /** + * Create a new schema column with information from the given annotation. + */ + private static Column newColumn(MapKeyJoinColumn join) { + Column col = new Column(); + if (!StringUtils.isEmpty(join.name())) + col.setName(join.name()); + if (!StringUtils.isEmpty(join.columnDefinition())) + col.setName(join.columnDefinition()); + if (!StringUtils.isEmpty(join.referencedColumnName())) + col.setTarget(join.referencedColumnName()); + col.setNotNull(!join.nullable()); + col.setFlag(Column.FLAG_UNINSERTABLE, !join.insertable()); + col.setFlag(Column.FLAG_UNUPDATABLE, !join.updatable ()); + return col; + } + } diff --git a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java index c2d261969..ca208b7c7 100644 --- a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java +++ b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java @@ -88,6 +88,9 @@ enum MappingTag { KEY_JOIN_COLS, KEY_NONPOLY, KEY_STRAT, + MAP_KEY_COL, + MAP_KEY_JOIN_COL, + MAP_KEY_JOIN_COLS, MAPPING_OVERRIDE, MAPPING_OVERRIDES, NONPOLY, diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Company1.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Company1.java new file mode 100644 index 000000000..c35c97bf8 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Company1.java @@ -0,0 +1,50 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.*; + +@Entity +public class Company1 { + @Id + int id; + + @OneToMany(targetEntity=VicePresident.class) + @MapKeyClass(Division.class) + Map organization = new HashMap(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getOrganization() { + return organization; + } + + public void addToOrganization(Division division, VicePresident vp) { + organization.put(division, vp); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Company2.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Company2.java new file mode 100644 index 000000000..0aad92ec1 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Company2.java @@ -0,0 +1,50 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.*; + +@Entity +public class Company2 { + @Id + int id; + + @OneToMany + Map organization = + new HashMap(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getOrganization() { + return organization; + } + + public void addToOrganization(Division division, VicePresident vp) { + organization.put(division, vp); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department1.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department1.java new file mode 100644 index 000000000..342e3e906 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department1.java @@ -0,0 +1,51 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.*; + +@Entity +public class Department1 { + + int deptId; + + Map empMap = new HashMap(); + + @Id + public int getDeptId() { + return deptId; + } + + public void setDeptId(int deptId) { + this.deptId = deptId; + } + + @OneToMany(mappedBy="department", fetch=FetchType.EAGER) + @MapKey(name="empId") + public Map getEmpMap() { + return empMap; + } + + public void setEmpMap(Map empMap) { + this.empMap = empMap; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department2.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department2.java new file mode 100644 index 000000000..5fedab094 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department2.java @@ -0,0 +1,54 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.*; + +@Entity +public class Department2 { + + int deptId; + Map empMap = new HashMap(); + + @Id + public int getDeptId() { + return deptId; + } + + public void setDeptId(int deptId) { + this.deptId = deptId; + } + + @OneToMany(mappedBy="department") + @MapKey(name="empPK") + public Map getEmpMap() { + return empMap; + } + + public void setEmpMap(Map empMap) { + this.empMap = empMap; + } + + public void addEmployee(Employee2 emp) { + empMap.put(emp.getEmpPK(), emp); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department3.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department3.java new file mode 100644 index 000000000..e76516188 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Department3.java @@ -0,0 +1,55 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.MapKey; +import javax.persistence.OneToMany; + +@Entity +public class Department3 { + + @Id + int deptId; + + @OneToMany(mappedBy="department", fetch=FetchType.EAGER) + @MapKey(name="name") + Map emps = new HashMap(); + + public int getDeptId() { + return deptId; + } + + public void setDeptId(int deptId) { + this.deptId = deptId; + } + + public Map getEmployees() { + return emps; + } + + public void addEmployee(Employee3 emp) { + emps.put(emp.getName(), emp); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Division.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Division.java new file mode 100644 index 000000000..64849c930 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Division.java @@ -0,0 +1,47 @@ +/* + * 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.embed; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Division { + @Id + int id; + + String name; + + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + 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/embed/Employee1.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee1.java new file mode 100644 index 000000000..00d6f1189 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee1.java @@ -0,0 +1,47 @@ +/* + * 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.embed; + +import javax.persistence.*; + +@Entity +public class Employee1 { + @Id + int empId; + + @ManyToOne + @JoinColumn(name="dept_id") + Department1 department; + + public int getEmpId() { + return empId; + } + + public void setEmpId(int empId) { + this.empId = empId; + } + + public Department1 getDepartment() { + return department; + } + + public void setDepartment(Department1 department) { + this.department = department; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee2.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee2.java new file mode 100644 index 000000000..2b014d229 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee2.java @@ -0,0 +1,55 @@ +/* + * 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.embed; + +import java.util.Date; + +import javax.persistence.*; + +@Entity +public class Employee2 { + EmployeePK2 empPK; + + Department2 department; + + public Employee2() {} + + public Employee2(String name, Date bDate) { + this.empPK = new EmployeePK2(name, bDate); + } + + @EmbeddedId + public EmployeePK2 getEmpPK() { + return empPK; + } + + public void setEmpPK(EmployeePK2 empPK) { + this.empPK = empPK; + } + + @ManyToOne + @JoinColumn(name="dept_id") + public Department2 getDepartment() { + return department; + } + + public void setDepartment(Department2 department) { + this.department = department; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee3.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee3.java new file mode 100644 index 000000000..f57b261e1 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Employee3.java @@ -0,0 +1,60 @@ +/* + * 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.embed; + +import javax.persistence.*; + +@Entity +public class Employee3 { + @Id + int empId; + + @ManyToOne + @JoinColumn(name="dept_id") + Department3 department; + + @Embedded + EmployeeName3 name; + + public int getEmpId() { + return empId; + } + + public void setEmpId(int empId) { + this.empId = empId; + } + + public Department3 getDepartment() { + return department; + } + + public void setDepartment(Department3 department) { + this.department = department; + } + + public EmployeeName3 getName() { + return name; + } + + public void setName(EmployeeName3 name) { + this.name = name; + } + + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EmployeeName3.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EmployeeName3.java new file mode 100644 index 000000000..1fb091795 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EmployeeName3.java @@ -0,0 +1,68 @@ +/* + * 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.embed; + +import javax.persistence.Embeddable; + +@Embeddable +public class EmployeeName3 { + + String fName; + String lName; + + public EmployeeName3() {} + + public EmployeeName3(String fName, String lName) { + this.fName = fName; + this.lName = lName; + } + + public String getFName() { + return fName; + } + + public void setFName(String fName) { + this.fName = fName; + } + + public String getLName() { + return lName; + } + + public void setLName(String lName) { + this.lName = lName; + } + + public boolean equals(Object o) { + if (!(o instanceof EmployeeName3)) return false; + EmployeeName3 other = (EmployeeName3) o; + if (fName.equals(other.fName) && + lName.equals(other.lName)) + return true; + return false; + } + + public int hashCode() { + int ret = 0; + ret += lName.hashCode(); + ret = 31 * ret + fName.hashCode(); + return ret; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EmployeePK2.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EmployeePK2.java new file mode 100644 index 000000000..5faef1e4f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EmployeePK2.java @@ -0,0 +1,52 @@ +/* + * 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.embed; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.*; + +@Embeddable +public class EmployeePK2 implements Serializable { + String name; + Date bDay; + + public EmployeePK2() {} + public EmployeePK2(String name, Date bDay) { + this.name = name; + this.bDay = bDay; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EmployeePK2)) return false; + EmployeePK2 pk = (EmployeePK2) o; + if (pk.name.equals(name) && + pk.bDay.equals(bDay)) return true; + return false; + } + + public int hashCode() { + int code = 0; + code = code * 31 + name.hashCode(); + code = code * 31 + bDay.hashCode(); + return code; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item1.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item1.java new file mode 100644 index 000000000..68768fb84 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item1.java @@ -0,0 +1,50 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.*; + +@Entity +public class Item1 { + @Id + int id; + + @ElementCollection(targetClass=String.class) + @MapKeyClass(String.class) + Map images = new HashMap(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getImages() { + return images; + } + + public void addImage(String imageName, String fileName) { + images.put(imageName, fileName); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item2.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item2.java new file mode 100644 index 000000000..2f39bbabb --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item2.java @@ -0,0 +1,49 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.*; + +@Entity +public class Item2 { + @Id + int id; + + @ElementCollection + Map images = new HashMap(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getImages() { + return images; + } + + public void addImage(String imageName, String fileName) { + images.put(imageName, fileName); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item3.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item3.java new file mode 100644 index 000000000..b23b41205 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Item3.java @@ -0,0 +1,52 @@ +/* + * 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.embed; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.*; + +@Entity +public class Item3 { + @Id + int id; + + @ElementCollection + @MapKeyColumn(name="IMAGE_NAME") + @Column(name="IMAGE_FILENAME") + @CollectionTable(name="IMAGE_MAPPING") + Map images = new HashMap(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getImages() { + return images; + } + + public void addImage(String imageName, String fileName) { + images.put(imageName, fileName); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java index c4042a4c3..cd0efbade 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java @@ -19,6 +19,7 @@ package org.apache.openjpa.persistence.embed; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,30 +30,38 @@ import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Query; - +import junit.framework.Assert; import org.apache.openjpa.persistence.test.SingleEMFTestCase; - public class TestEmbeddable extends SingleEMFTestCase { public int numEmbeddables = 1; public int numBasicTypes = 1; - public int ID = 1; public int numProgramManagers = 2; public int numNickNames = 3; - public int numEmployeesPerPhoneNumber = 1; public int numPhoneNumbersPerEmployee = 2; public int numEmployeesPerProgramManager = 2; public int numEmployees = numProgramManagers * numEmployeesPerProgramManager; public int numPhoneNumbers = numEmployees * numPhoneNumbersPerEmployee; - - public Map phones = new HashMap(); - public Map employees = new HashMap(); + public int numDepartments = 2; + public int numEmployeesPerDept = 2; + public int numItems = 2; + public int numImagesPerItem = 3; + public int numCompany = 2; + public int numDivisionsPerCo = 2; + public int ID = 1; + public int itemId = 1; + public int compId = 1; + public int divId = 1; + public int vpId = 1; + public int deptId = 1; public int empId = 1; public int phoneId = 1; public int pmId = 1; public int parkingSpotId = 1; + public Map phones = new HashMap(); + public Map employees = new HashMap(); public void setUp() { setUp(Embed.class, Embed_Coll_Embed.class, Embed_Coll_Integer.class, @@ -65,7 +74,12 @@ public class TestEmbeddable extends SingleEMFTestCase { EntityA_Coll_Embed_Embed.class, ContactInfo.class, Employee.class, JobInfo.class, LocationDetails.class, ParkingSpot.class, PhoneNumber.class, ProgramManager.class, - CLEAR_TABLES); + Department1.class, Employee1.class, Department2.class, + Employee2.class, EmployeePK2.class, Department3.class, + Employee3.class, EmployeeName3.class, Item1.class, Item2.class, + Item3.class, Company1.class, Company2.class, Division.class, + VicePresident.class, + DROP_TABLES); } public void testEntityA_Coll_String() { @@ -128,6 +142,18 @@ public class TestEmbeddable extends SingleEMFTestCase { findEmployeeObj(); } + public void testMapKey() { + createObjMapKey(); + queryObjMapKey(); + findObjMapKey(); + } + + public void testMapKeyClass() { + createObjMapKeyClass(); + queryObjMapKeyClass(); + findObjMapKeyClass(); + } + /* * Create EntityA_Coll_String */ @@ -528,7 +554,7 @@ public class TestEmbeddable extends SingleEMFTestCase { public void queryEmployeeObj() { queryProgramManager(emf); - queryEmployee(emf); + queryEmployeeObj(emf); queryPhoneNumber(emf); queryParkingSpot(emf); } @@ -572,7 +598,7 @@ public class TestEmbeddable extends SingleEMFTestCase { em.close(); } - public void queryEmployee(EntityManagerFactory emf) { + public void queryEmployeeObj(EntityManagerFactory emf) { EntityManager em = emf.createEntityManager(); EntityTransaction tran = em.getTransaction(); tran.begin(); @@ -1068,4 +1094,441 @@ public class TestEmbeddable extends SingleEMFTestCase { tran.commit(); em.close(); } + + public void createObjMapKey() { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + for (int i = 0; i < numDepartments; i++) + createDepartment1(em, deptId++); + for (int i = 0; i < numDepartments; i++) + createDepartment2(em, deptId++); + for (int i = 0; i < numDepartments; i++) + createDepartment3(em, deptId++); + tran.begin(); + em.flush(); + tran.commit(); + em.close(); + } + + public void createObjMapKeyClass() { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + for (int i = 0; i < numItems; i++) + createItem1(em, itemId++); + for (int i = 0; i < numItems; i++) + createItem2(em, itemId++); + for (int i = 0; i < numItems; i++) + createItem3(em, itemId++); + for (int i = 0; i < numCompany; i++) + createCompany1(em, compId++); + for (int i = 0; i < numCompany; i++) + createCompany2(em, compId++); + tran.begin(); + em.flush(); + tran.commit(); + em.close(); + } + + public void createItem1(EntityManager em, int id) { + Item1 item = new Item1(); + item.setId(id); + for (int i = 0; i < numImagesPerItem; i++) { + item.addImage("image" + id + i, "file" + id + i); + } + em.persist(item); + } + + public void createItem2(EntityManager em, int id) { + Item2 item = new Item2(); + item.setId(id); + for (int i = 0; i < numImagesPerItem; i++) { + item.addImage("image" + id + i, "file" + id + i); + } + em.persist(item); + } + + public void createItem3(EntityManager em, int id) { + System.out.println("In CreateItem3, id = " + id); + Item3 item = new Item3(); + item.setId(id); + for (int i = 0; i < numImagesPerItem; i++) { + item.addImage("image" + id + i, "file" + id + i); + } + em.persist(item); + } + + public void createCompany1(EntityManager em, int id) { + Company1 c = new Company1(); + c.setId(id); + for (int i = 0; i < numDivisionsPerCo; i++) { + Division d = createDivision(em, divId++); + VicePresident vp = createVicePresident(em, vpId++); + c.addToOrganization(d, vp); + em.persist(d); + em.persist(vp); + } + em.persist(c); + } + + public void createCompany2(EntityManager em, int id) { + Company2 c = new Company2(); + c.setId(id); + for (int i = 0; i < numDivisionsPerCo; i++) { + Division d = createDivision(em, divId++); + VicePresident vp = createVicePresident(em, vpId++); + c.addToOrganization(d, vp); + em.persist(d); + em.persist(vp); + } + em.persist(c); + } + + public Division createDivision(EntityManager em, int id) { + Division d = new Division(); + d.setId(id); + d.setName("d" + id); + return d; + } + + public VicePresident createVicePresident(EntityManager em, int id) { + VicePresident vp = new VicePresident(); + vp.setId(id); + vp.setName("vp" + id); + return vp; + } + + public void createDepartment1(EntityManager em, int id) { + Department1 d = new Department1(); + d.setDeptId(id); + Map emps = new HashMap(); + for (int i = 0; i < numEmployeesPerDept; i++) { + Employee1 e = createEmployee1(em, empId++); + //d.addEmployee1(e); + emps.put(e.getEmpId(), e); + e.setDepartment(d); + em.persist(e); + } + d.setEmpMap(emps); + em.persist(d); + } + + public Employee1 createEmployee1(EntityManager em, int id) { + Employee1 e = new Employee1(); + e.setEmpId(id); + return e; + } + + public void createDepartment2(EntityManager em, int id) { + Department2 d = new Department2(); + d.setDeptId(id); + for (int i = 0; i < numEmployeesPerDept; i++) { + Employee2 e = createEmployee2(em, empId++); + d.addEmployee(e); + e.setDepartment(d); + em.persist(e); + } + em.persist(d); + } + + public Employee2 createEmployee2(EntityManager em, int id) { + Employee2 e = new Employee2("e" + id, new Date()); + return e; + } + + public void createDepartment3(EntityManager em, int id) { + Department3 d = new Department3(); + d.setDeptId(id); + for (int i = 0; i < numEmployeesPerDept; i++) { + Employee3 e = createEmployee3(em, empId++); + d.addEmployee(e); + e.setDepartment(d); + em.persist(e); + } + em.persist(d); + } + + public Employee3 createEmployee3(EntityManager em, int id) { + Employee3 e = new Employee3(); + EmployeeName3 name = new EmployeeName3("f" + id, "l" + id); + e.setEmpId(id); + e.setName(name); + return e; + } + + public void findObjMapKey() { + EntityManager em = emf.createEntityManager(); + Department1 d1 = em.find(Department1.class, 1); + assertDepartment1(d1); + + Employee1 e1 = em.find(Employee1.class, 1); + assertEmployee1(e1); + + Department2 d2 = em.find(Department2.class, 3); + assertDepartment2(d2); + + Map emps = d2.getEmpMap(); + Set keys = emps.keySet(); + for (EmployeePK2 key : keys) { + Employee2 e2 = em.find(Employee2.class, key); + assertEmployee2(e2); + } + + Department3 d3 = em.find(Department3.class, 5); + assertDepartment3(d3); + + Employee3 e3 = em.find(Employee3.class, 9); + assertEmployee3(e3); + + em.close(); + } + + public void assertDepartment1(Department1 d) { + int id = d.getDeptId(); + Map es = d.getEmpMap(); + Assert.assertEquals(2,es.size()); + Set keys = es.keySet(); + for (Object obj : keys) { + Integer empId = (Integer) obj; + Employee1 e = es.get(empId); + Assert.assertEquals(empId.intValue(), e.getEmpId()); + } + } + + public void assertDepartment2(Department2 d) { + int id = d.getDeptId(); + Map es = d.getEmpMap(); + Assert.assertEquals(2,es.size()); + Set keys = es.keySet(); + for (EmployeePK2 pk : keys) { + Employee2 e = es.get(pk); + Assert.assertEquals(pk, e.getEmpPK()); + } + } + + public void assertDepartment3(Department3 d) { + int id = d.getDeptId(); + Map es = d.getEmployees(); + Assert.assertEquals(2,es.size()); + Set keys = es.keySet(); + for (EmployeeName3 key : keys) { + Employee3 e = es.get(key); + Assert.assertEquals(key, e.getName()); + } + } + + public void assertEmployee1(Employee1 e) { + int id = e.getEmpId(); + Department1 d = e.getDepartment(); + assertDepartment1(d); + } + + public void assertEmployee2(Employee2 e) { + EmployeePK2 pk = e.getEmpPK(); + Department2 d = e.getDepartment(); + assertDepartment2(d); + } + + public void assertEmployee3(Employee3 e) { + int id = e.getEmpId(); + Department3 d = e.getDepartment(); + assertDepartment3(d); + } + + public void queryObjMapKey() { + queryDepartment(emf); + queryEmployee(emf); + } + + public void queryDepartment(EntityManagerFactory emf) { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + tran.begin(); + Query q1 = em.createQuery("select d from Department1 d"); + List ds1 = q1.getResultList(); + for (Department1 d : ds1){ + assertDepartment1(d); + } + + Query q2 = em.createQuery("select d from Department2 d"); + List ds2 = q2.getResultList(); + for (Department2 d : ds2){ + assertDepartment2(d); + } + + Query q3 = em.createQuery("select d from Department3 d"); + List ds3 = q3.getResultList(); + for (Department3 d : ds3){ + assertDepartment3(d); + } + + tran.commit(); + em.close(); + } + + public void queryEmployee(EntityManagerFactory emf) { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + tran.begin(); + Query q1 = em.createQuery("select e from Employee1 e"); + List es1 = q1.getResultList(); + for (Employee1 e : es1){ + assertEmployee1(e); + } + + Query q2 = em.createQuery("select e from Employee2 e"); + List es2 = q2.getResultList(); + for (Employee2 e : es2){ + assertEmployee2(e); + } + + Query q3 = em.createQuery("select e from Employee3 e"); + List es3 = q3.getResultList(); + for (Employee3 e : es3){ + assertEmployee3(e); + } + tran.commit(); + em.close(); + } + + public void findObjMapKeyClass() { + EntityManager em = emf.createEntityManager(); + + Item1 item1 = em.find(Item1.class, 1); + assertItem1(item1); + + Item2 item2 = em.find(Item2.class, 3); + assertItem2(item2); + + Item3 item3 = em.find(Item3.class, 5); + assertItem3(item3); + + Company1 c1 = em.find(Company1.class, 1); + assertCompany1(c1); + + Company2 c2 = em.find(Company2.class, 3); + assertCompany2(c2); + + Division d = em.find(Division.class, 1); + assertDivision(d); + + VicePresident vp = em.find(VicePresident.class, 1); + assertVicePresident(vp); + } + + public void assertItem1(Item1 item) { + int id = item.getId(); + Map images = item.getImages(); + Assert.assertEquals(numImagesPerItem, images.size()); + } + + public void assertItem2(Item2 item) { + int id = item.getId(); + Map images = item.getImages(); + Assert.assertEquals(numImagesPerItem, images.size()); + } + + public void assertItem3(Item3 item) { + int id = item.getId(); + Map images = item.getImages(); + Assert.assertEquals(numImagesPerItem, images.size()); + } + + public void assertCompany1(Company1 c) { + int id = c.getId(); + Map organization = c.getOrganization(); + Assert.assertEquals(2,organization.size()); + } + + public void assertCompany2(Company2 c) { + int id = c.getId(); + Map organization = c.getOrganization(); + Assert.assertEquals(2,organization.size()); + } + + public void assertDivision(Division d) { + int id = d.getId(); + String name = d.getName(); + } + + public void assertVicePresident(VicePresident vp) { + int id = vp.getId(); + String name = vp.getName(); + } + + public void queryObjMapKeyClass() { + queryItem(emf); + queryCompany(emf); + queryDivision(emf); + queryVicePresident(emf); + } + + public void queryItem(EntityManagerFactory emf) { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + tran.begin(); + Query q1 = em.createQuery("select i from Item1 i"); + List is1 = q1.getResultList(); + for (Item1 item : is1){ + assertItem1(item); + } + + Query q2 = em.createQuery("select i from Item2 i"); + List is2 = q2.getResultList(); + for (Item2 item : is2){ + assertItem2(item); + } + + Query q3 = em.createQuery("select i from Item3 i"); + List is3 = q3.getResultList(); + for (Item3 item : is3){ + assertItem3(item); + } + + tran.commit(); + em.close(); + } + + public void queryCompany(EntityManagerFactory emf) { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + tran.begin(); + Query q1 = em.createQuery("select c from Company1 c"); + List cs1 = q1.getResultList(); + for (Company1 c : cs1){ + assertCompany1(c); + } + Query q2 = em.createQuery("select c from Company2 c"); + List cs2 = q2.getResultList(); + for (Company2 c : cs2){ + assertCompany2(c); + } + tran.commit(); + em.close(); + } + + public void queryDivision(EntityManagerFactory emf) { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + tran.begin(); + Query q = em.createQuery("select d from Division d"); + List ds = q.getResultList(); + for (Division d : ds){ + assertDivision(d); + } + tran.commit(); + em.close(); + } + + public void queryVicePresident(EntityManagerFactory emf) { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + tran.begin(); + Query q = em.createQuery("select vp from VicePresident vp"); + List vps = q.getResultList(); + for (VicePresident vp : vps){ + assertVicePresident(vp); + } + tran.commit(); + em.close(); + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/VicePresident.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/VicePresident.java new file mode 100644 index 000000000..692c2e5ab --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/VicePresident.java @@ -0,0 +1,58 @@ +/* + * 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.embed; + +import javax.persistence.*; + +@Entity +public class VicePresident { + @Id + int id; + + String name; + + @ManyToOne + Company1 co; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Company1 getCompany() { + return co; + } + + public void setCompany(Company1 co) { + this.co = co; + } + + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEmbeddableSuperclass.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEmbeddableSuperclass.java index 1f3adc11b..4f13c939e 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEmbeddableSuperclass.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEmbeddableSuperclass.java @@ -24,6 +24,7 @@ import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.meta.ClassMapping; import org.apache.openjpa.jdbc.meta.FieldMapping; import org.apache.openjpa.jdbc.meta.strats.ClobValueHandler; +import org.apache.openjpa.jdbc.meta.strats.EmbedFieldStrategy; import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy; import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedClobFieldStrategy; import org.apache.openjpa.jdbc.meta.strats.NoneClassStrategy; @@ -106,7 +107,7 @@ public class TestEmbeddableSuperclass assertTrue(fm.getStrategy() instanceof RelationFieldStrategy); fm = cls.getFieldMapping("sup"); - assertTrue(fm.getStrategy() instanceof RelationFieldStrategy); + assertTrue(fm.getStrategy() instanceof EmbedFieldStrategy); } public void testPersistAndFind() { diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java index 02adf96ac..f54d491da 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java @@ -59,6 +59,7 @@ import javax.persistence.Lob; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.MapKey; +import javax.persistence.MapKeyClass; import javax.persistence.MappedSuperclass; import javax.persistence.NamedNativeQueries; import javax.persistence.NamedNativeQuery; @@ -143,6 +144,7 @@ public class AnnotationPersistenceMetaDataParser _tags.put(Id.class, ID); _tags.put(IdClass.class, ID_CLASS); _tags.put(MapKey.class, MAP_KEY); + _tags.put(MapKeyClass.class, MAP_KEY_CLASS); _tags.put(NamedNativeQueries.class, NATIVE_QUERIES); _tags.put(NamedNativeQuery.class, NATIVE_QUERY); _tags.put(NamedQueries.class, QUERIES); @@ -1064,6 +1066,10 @@ public class AnnotationPersistenceMetaDataParser if (isMappingOverrideMode()) parseMapKey(fmd, (MapKey) anno); break; + case MAP_KEY_CLASS: + if (isMappingOverrideMode()) + parseMapKeyClass(fmd, (MapKeyClass) anno); + break; case ORDER_BY: parseOrderBy(fmd, (OrderBy) el.getAnnotation(OrderBy.class)); @@ -1399,6 +1405,17 @@ public class AnnotationPersistenceMetaDataParser fmd.getKey().setValueMappedBy(name); } + /** + * Parse @MapKeyClass. + */ + private void parseMapKeyClass(FieldMetaData fmd, MapKeyClass anno) { + if (anno.value() != void.class) + fmd.getKey().setDeclaredType(anno.value()); + else + throw new IllegalArgumentException( + "The value of the MapClassClass cannot be null"); + } + /** * Setup the field as a LOB mapping. */ @@ -1480,6 +1497,8 @@ public class AnnotationPersistenceMetaDataParser */ private void parseElementCollection(FieldMetaData fmd, ElementCollection anno) { + // TODO: throw exception if the runtime env is OpenJpa 1.x + if (fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION && fmd.getDeclaredTypeCode() != JavaTypes.MAP) throw new MetaDataException(_loc.get("bad-meta-anno", fmd, diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java index ed06ea6e2..a91fdb0aa 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java @@ -44,6 +44,7 @@ public enum MetaDataTag { ID_CLASS, LOB, MAP_KEY, + MAP_KEY_CLASS, NATIVE_QUERIES, NATIVE_QUERY, QUERY_STRING,