ANN-857 implement @MapKeyJoinColumn and @MapKeyJoinColumns + _KEY default column name for key. Also keep legacy support for old naming solution
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@17187 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
577024e7aa
commit
e093ee46c4
|
@ -74,6 +74,8 @@ import javax.persistence.ElementCollection;
|
|||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.MapKeyJoinColumns;
|
||||
import javax.persistence.MapKeyJoinColumn;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
|
@ -135,6 +137,7 @@ import org.hibernate.cfg.annotations.SimpleValueBinder;
|
|||
import org.hibernate.cfg.annotations.TableBinder;
|
||||
import org.hibernate.cfg.annotations.MapKeyColumnDelegator;
|
||||
import org.hibernate.cfg.annotations.CustomizableColumns;
|
||||
import org.hibernate.cfg.annotations.MapKeyJoinColumnDelegator;
|
||||
import org.hibernate.engine.FilterDefinition;
|
||||
import org.hibernate.engine.Versioning;
|
||||
import org.hibernate.id.MultipleHiLoPerTableGenerator;
|
||||
|
@ -1540,39 +1543,88 @@ public final class AnnotationBinder {
|
|||
mappings
|
||||
);
|
||||
}
|
||||
{
|
||||
Column[] keyColumns = null;
|
||||
//JPA 2 has priority and has different default column values, differenciate legacy from JPA 2
|
||||
Boolean isJPA2 = null;
|
||||
if ( property.isAnnotationPresent( MapKeyColumn.class ) ) {
|
||||
isJPA2 = Boolean.TRUE;
|
||||
keyColumns = new Column[] { new MapKeyColumnDelegator( property.getAnnotation( MapKeyColumn.class ) ) };
|
||||
}
|
||||
else if ( property.isAnnotationPresent( org.hibernate.annotations.MapKey.class ) ) {
|
||||
if ( isJPA2 == null) {
|
||||
isJPA2 = Boolean.FALSE;
|
||||
}
|
||||
keyColumns = property.getAnnotation( org.hibernate.annotations.MapKey.class ).columns();
|
||||
}
|
||||
|
||||
Column[] keyColumns = null;
|
||||
//JPA 2 has priority
|
||||
if ( property.isAnnotationPresent( MapKeyColumn.class ) ) {
|
||||
keyColumns = new Column[] { new MapKeyColumnDelegator( property.getAnnotation( MapKeyColumn.class ) ) };
|
||||
//not explicitly legacy
|
||||
if ( isJPA2 == null) {
|
||||
isJPA2 = Boolean.TRUE;
|
||||
}
|
||||
|
||||
//nullify empty array
|
||||
keyColumns = keyColumns != null && keyColumns.length > 0 ? keyColumns : null;
|
||||
|
||||
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
|
||||
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||
keyColumns,
|
||||
null,
|
||||
Nullability.FORCED_NOT_NULL,
|
||||
propertyHolder,
|
||||
isJPA2 ? inferredData : mapKeyVirtualProperty,
|
||||
isJPA2 ? "_KEY" : null,
|
||||
entityBinder.getSecondaryTables(),
|
||||
mappings
|
||||
);
|
||||
collectionBinder.setMapKeyColumns( mapColumns );
|
||||
}
|
||||
else if ( property.isAnnotationPresent( org.hibernate.annotations.MapKey.class ) ) {
|
||||
keyColumns = property.getAnnotation( org.hibernate.annotations.MapKey.class ).columns();
|
||||
{
|
||||
JoinColumn[] joinKeyColumns = null;
|
||||
//JPA 2 has priority and has different default column values, differenciate legacy from JPA 2
|
||||
Boolean isJPA2 = null;
|
||||
if ( property.isAnnotationPresent( MapKeyJoinColumns.class ) ) {
|
||||
isJPA2 = Boolean.TRUE;
|
||||
final MapKeyJoinColumn[] mapKeyJoinColumns = property.getAnnotation( MapKeyJoinColumns.class ).value();
|
||||
joinKeyColumns = new JoinColumn[mapKeyJoinColumns.length];
|
||||
int index = 0;
|
||||
for ( MapKeyJoinColumn joinColumn : mapKeyJoinColumns ) {
|
||||
joinKeyColumns[index] = new MapKeyJoinColumnDelegator( joinColumn );
|
||||
index++;
|
||||
}
|
||||
if ( joinKeyColumns != null ) {
|
||||
throw new AnnotationException( "@MapKeyJoinColumn and @MapKeyJoinColumns used on the same property: "
|
||||
+ StringHelper.qualify( propertyHolder.getClassName(), property.getName() ) );
|
||||
}
|
||||
}
|
||||
else if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
|
||||
isJPA2 = Boolean.TRUE;
|
||||
joinKeyColumns = new JoinColumn[] { new MapKeyJoinColumnDelegator( property.getAnnotation( MapKeyJoinColumn.class ) ) };
|
||||
}
|
||||
else if ( property.isAnnotationPresent( org.hibernate.annotations.MapKeyManyToMany.class ) ) {
|
||||
if ( isJPA2 == null) {
|
||||
isJPA2 = Boolean.FALSE;
|
||||
}
|
||||
joinKeyColumns = property.getAnnotation( org.hibernate.annotations.MapKeyManyToMany.class ).joinColumns();
|
||||
}
|
||||
|
||||
//not explicitly legacy
|
||||
if ( isJPA2 == null) {
|
||||
isJPA2 = Boolean.TRUE;
|
||||
}
|
||||
|
||||
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
|
||||
Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumnsWithDefaultColumnSuffix(
|
||||
joinKeyColumns,
|
||||
null,
|
||||
entityBinder.getSecondaryTables(),
|
||||
propertyHolder,
|
||||
isJPA2 ? inferredData.getPropertyName() : mapKeyVirtualProperty.getPropertyName(),
|
||||
isJPA2 ? "_KEY" : null,
|
||||
mappings
|
||||
);
|
||||
collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
|
||||
}
|
||||
//nullify empty array
|
||||
keyColumns = keyColumns != null && keyColumns.length > 0 ? keyColumns : null;
|
||||
|
||||
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
|
||||
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
|
||||
keyColumns,
|
||||
null,
|
||||
Nullability.FORCED_NOT_NULL,
|
||||
propertyHolder,
|
||||
mapKeyVirtualProperty,
|
||||
entityBinder.getSecondaryTables(),
|
||||
mappings
|
||||
);
|
||||
collectionBinder.setMapKeyColumns( mapColumns );
|
||||
|
||||
MapKeyManyToMany mapKeyManyToMany = property.getAnnotation( MapKeyManyToMany.class );
|
||||
Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumns(
|
||||
mapKeyManyToMany != null ?
|
||||
mapKeyManyToMany.joinColumns() :
|
||||
null,
|
||||
null, entityBinder.getSecondaryTables(),
|
||||
propertyHolder, mapKeyVirtualProperty.getPropertyName(), mappings
|
||||
);
|
||||
collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
|
||||
|
||||
//potential element
|
||||
collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
|
||||
|
|
|
@ -336,6 +336,18 @@ public class Ejb3Column {
|
|||
PropertyData inferredData,
|
||||
Map<String, Join> secondaryTables,
|
||||
ExtendedMappings mappings
|
||||
){
|
||||
return buildColumnFromAnnotation(
|
||||
anns,
|
||||
formulaAnn, nullability, propertyHolder, inferredData, null, secondaryTables, mappings);
|
||||
}
|
||||
public static Ejb3Column[] buildColumnFromAnnotation(
|
||||
javax.persistence.Column[] anns,
|
||||
org.hibernate.annotations.Formula formulaAnn, Nullability nullability, PropertyHolder propertyHolder,
|
||||
PropertyData inferredData,
|
||||
String suffixForDefaultColumnName,
|
||||
Map<String, Join> secondaryTables,
|
||||
ExtendedMappings mappings
|
||||
) {
|
||||
Ejb3Column[] columns;
|
||||
if ( formulaAnn != null ) {
|
||||
|
@ -361,7 +373,12 @@ public class Ejb3Column {
|
|||
log.debug( "Column(s) overridden for property {}", inferredData.getPropertyName() );
|
||||
}
|
||||
if ( actualCols == null ) {
|
||||
columns = buildImplicitColumn( inferredData, secondaryTables, propertyHolder, nullability, mappings );
|
||||
columns = buildImplicitColumn( inferredData,
|
||||
suffixForDefaultColumnName,
|
||||
secondaryTables,
|
||||
propertyHolder,
|
||||
nullability,
|
||||
mappings );
|
||||
}
|
||||
else {
|
||||
final int length = actualCols.length;
|
||||
|
@ -376,6 +393,12 @@ public class Ejb3Column {
|
|||
column.setPrecision( col.precision() );
|
||||
column.setScale( col.scale() );
|
||||
column.setLogicalColumnName( col.name() );
|
||||
//support for explicit property name + suffix
|
||||
if ( StringHelper.isEmpty( column.getLogicalColumnName() )
|
||||
&& ! StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
|
||||
column.setLogicalColumnName( inferredData.getPropertyName() + suffixForDefaultColumnName );
|
||||
}
|
||||
|
||||
column.setPropertyName(
|
||||
BinderHelper.getRelativePath( propertyHolder, inferredData.getPropertyName() )
|
||||
);
|
||||
|
@ -398,8 +421,12 @@ public class Ejb3Column {
|
|||
}
|
||||
|
||||
private static Ejb3Column[] buildImplicitColumn(
|
||||
PropertyData inferredData, Map<String, Join> secondaryTables, PropertyHolder propertyHolder,
|
||||
Nullability nullability, ExtendedMappings mappings
|
||||
PropertyData inferredData,
|
||||
String suffixForDefaultColumnName,
|
||||
Map<String, Join> secondaryTables,
|
||||
PropertyHolder propertyHolder,
|
||||
Nullability nullability,
|
||||
ExtendedMappings mappings
|
||||
) {
|
||||
Ejb3Column[] columns;
|
||||
columns = new Ejb3Column[1];
|
||||
|
@ -412,12 +439,22 @@ public class Ejb3Column {
|
|||
column.setNullable( false );
|
||||
}
|
||||
column.setLength( DEFAULT_COLUMN_LENGTH );
|
||||
final String propertyName = inferredData.getPropertyName();
|
||||
column.setPropertyName(
|
||||
BinderHelper.getRelativePath( propertyHolder, inferredData.getPropertyName() )
|
||||
BinderHelper.getRelativePath( propertyHolder, propertyName )
|
||||
);
|
||||
column.setPropertyHolder( propertyHolder );
|
||||
column.setJoins( secondaryTables );
|
||||
column.setMappings( mappings );
|
||||
|
||||
// property name + suffix is an "explicit" column name
|
||||
if ( !StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
|
||||
column.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
|
||||
column.setImplicit( false );
|
||||
}
|
||||
else {
|
||||
column.setImplicit( true );
|
||||
}
|
||||
column.bind();
|
||||
columns[0] = column;
|
||||
return columns;
|
||||
|
|
|
@ -133,6 +133,17 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
PropertyHolder propertyHolder,
|
||||
String propertyName,
|
||||
ExtendedMappings mappings
|
||||
) {
|
||||
return buildJoinColumnsWithDefaultColumnSuffix(anns, mappedBy, joins, propertyHolder, propertyName, "", mappings);
|
||||
}
|
||||
|
||||
public static Ejb3JoinColumn[] buildJoinColumnsWithDefaultColumnSuffix(
|
||||
JoinColumn[] anns,
|
||||
String mappedBy, Map<String, Join> joins,
|
||||
PropertyHolder propertyHolder,
|
||||
String propertyName,
|
||||
String suffixForDefaultColumnName,
|
||||
ExtendedMappings mappings
|
||||
) {
|
||||
JoinColumn[] actualColumns = propertyHolder.getOverriddenJoinColumn(
|
||||
StringHelper.qualify( propertyHolder.getPath(), propertyName )
|
||||
|
@ -140,7 +151,14 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
if ( actualColumns == null ) actualColumns = anns;
|
||||
if ( actualColumns == null || actualColumns.length == 0 ) {
|
||||
return new Ejb3JoinColumn[] {
|
||||
buildJoinColumn( (JoinColumn) null, mappedBy, joins, propertyHolder, propertyName, mappings )
|
||||
buildJoinColumn(
|
||||
(JoinColumn) null,
|
||||
mappedBy,
|
||||
joins,
|
||||
propertyHolder,
|
||||
propertyName,
|
||||
suffixForDefaultColumnName,
|
||||
mappings )
|
||||
};
|
||||
}
|
||||
else {
|
||||
|
@ -153,6 +171,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
joins,
|
||||
propertyHolder,
|
||||
propertyName,
|
||||
suffixForDefaultColumnName,
|
||||
mappings
|
||||
);
|
||||
}
|
||||
|
@ -168,6 +187,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
String mappedBy, Map<String, Join> joins,
|
||||
PropertyHolder propertyHolder,
|
||||
String propertyName,
|
||||
String suffixForDefaultColumnName,
|
||||
ExtendedMappings mappings
|
||||
) {
|
||||
if ( ann != null ) {
|
||||
|
@ -179,6 +199,10 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
}
|
||||
Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
|
||||
joinColumn.setJoinAnnotation( ann, null );
|
||||
if ( StringHelper.isEmpty( joinColumn.getLogicalColumnName() )
|
||||
&& ! StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
|
||||
joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
|
||||
}
|
||||
joinColumn.setJoins( joins );
|
||||
joinColumn.setPropertyHolder( propertyHolder );
|
||||
joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
|
||||
|
@ -192,8 +216,17 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
joinColumn.setMappedBy( mappedBy );
|
||||
joinColumn.setJoins( joins );
|
||||
joinColumn.setPropertyHolder( propertyHolder );
|
||||
joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
|
||||
joinColumn.setImplicit( true );
|
||||
joinColumn.setPropertyName(
|
||||
BinderHelper.getRelativePath( propertyHolder, propertyName )
|
||||
);
|
||||
// property name + suffix is an "explicit" column name
|
||||
if ( !StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
|
||||
joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
|
||||
joinColumn.setImplicit( false );
|
||||
}
|
||||
else {
|
||||
joinColumn.setImplicit( true );
|
||||
}
|
||||
joinColumn.setMappings( mappings );
|
||||
joinColumn.bind();
|
||||
return joinColumn;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.hibernate.cfg.annotations;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MapKeyJoinColumn;
|
||||
import javax.persistence.JoinColumn;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@SuppressWarnings({ "ClassExplicitlyAnnotation" })
|
||||
public class MapKeyJoinColumnDelegator implements JoinColumn {
|
||||
private final MapKeyJoinColumn column;
|
||||
|
||||
public MapKeyJoinColumnDelegator(MapKeyJoinColumn column) {
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return column.name();
|
||||
}
|
||||
|
||||
public String referencedColumnName() {
|
||||
return column.referencedColumnName();
|
||||
}
|
||||
|
||||
public boolean unique() {
|
||||
return column.unique();
|
||||
}
|
||||
|
||||
public boolean nullable() {
|
||||
return column.nullable();
|
||||
}
|
||||
|
||||
public boolean insertable() {
|
||||
return column.insertable();
|
||||
}
|
||||
|
||||
public boolean updatable() {
|
||||
return column.updatable();
|
||||
}
|
||||
|
||||
public String columnDefinition() {
|
||||
return column.columnDefinition();
|
||||
}
|
||||
|
||||
public String table() {
|
||||
return column.table();
|
||||
}
|
||||
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return Column.class;
|
||||
}
|
||||
}
|
|
@ -12,10 +12,10 @@ import javax.persistence.Column;
|
|||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.MapKeyJoinColumn;
|
||||
|
||||
import org.hibernate.annotations.MapKey;
|
||||
import org.hibernate.annotations.CollectionOfElements;
|
||||
import org.hibernate.annotations.MapKeyManyToMany;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
|
@ -31,13 +31,31 @@ public class Atmosphere {
|
|||
public Map<String, Gas> gases = new HashMap<String, Gas>();
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@MapKeyManyToMany(joinColumns = @JoinColumn(name="gas_id") )
|
||||
@MapKeyJoinColumn(name="gas_id" )
|
||||
@JoinTable(name = "Gas_per_key")
|
||||
public Map<GasKey, Gas> gasesPerKey = new HashMap<GasKey, Gas>();
|
||||
|
||||
@CollectionOfElements //TODO migrate to @ElementCollection ; @MapKeyManyToMany ??
|
||||
@Column(name="composition_rate")
|
||||
@MapKeyManyToMany(joinColumns = @JoinColumn(name="gas_id"))
|
||||
@MapKeyJoinColumn(name="gas_id" )
|
||||
@JoinTable(name = "Composition", joinColumns = @JoinColumn(name = "atmosphere_id"))
|
||||
public Map<Gas, Double> composition = new HashMap<Gas, Double>();
|
||||
|
||||
//use default JPA 2 column name for map key
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@MapKeyColumn
|
||||
@JoinTable(name="Atm_Gas_Def")
|
||||
public Map<String, Gas> gasesDef = new HashMap<String, Gas>();
|
||||
|
||||
//use default HAN legacy column name for map key
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@MapKey
|
||||
@JoinTable(name="Atm_Gas_DefLeg")
|
||||
public Map<String, Gas> gasesDefLeg = new HashMap<String, Gas>();
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@MapKeyJoinColumn
|
||||
@JoinTable(name = "Gas_p_key_def")
|
||||
public Map<GasKey, Gas> gasesPerKeyDef = new HashMap<GasKey, Gas>();
|
||||
|
||||
}
|
||||
|
|
|
@ -5,10 +5,14 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.hibernate.test.annotations.RequiresDialect;
|
||||
import org.hibernate.test.annotations.TestCase;
|
||||
|
@ -19,6 +23,35 @@ import org.hibernate.test.annotations.TestCase;
|
|||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class IndexedCollectionTest extends TestCase {
|
||||
|
||||
public void testJPA2DefaultMapColumns() throws Exception {
|
||||
isDefaultKeyColumnPresent( "gasesDef" );
|
||||
isDefaultKeyColumnPresent( "gasesPerKeyDef" );
|
||||
isNotDefaultKeyColumnPresent( "gasesDefLeg" );
|
||||
}
|
||||
|
||||
private void isDefaultKeyColumnPresent(String propertyName) {
|
||||
final Collection collection = getCfg().getCollectionMapping( Atmosphere.class.getName() + "." + propertyName );
|
||||
final Iterator columnIterator = collection.getCollectionTable().getColumnIterator();
|
||||
boolean hasDefault = false;
|
||||
while ( columnIterator.hasNext() ) {
|
||||
Column column = (Column) columnIterator.next();
|
||||
if ( (propertyName + "_KEY").equals( column.getName() ) ) hasDefault = true;
|
||||
}
|
||||
assertTrue( "Could not find " + propertyName + "_KEY", hasDefault);
|
||||
}
|
||||
|
||||
private void isNotDefaultKeyColumnPresent(String propertyName) {
|
||||
final Collection collection = getCfg().getCollectionMapping( Atmosphere.class.getName() + "." + propertyName );
|
||||
final Iterator columnIterator = collection.getCollectionTable().getColumnIterator();
|
||||
boolean hasDefault = false;
|
||||
while ( columnIterator.hasNext() ) {
|
||||
Column column = (Column) columnIterator.next();
|
||||
if ( (propertyName + "_KEY").equals( column.getName() ) ) hasDefault = true;
|
||||
}
|
||||
assertFalse( "Could not find " + propertyName + "_KEY", hasDefault);
|
||||
}
|
||||
|
||||
public void testFkList() throws Exception {
|
||||
Wardrobe w = new Wardrobe();
|
||||
Drawer d1 = new Drawer();
|
||||
|
|
|
@ -9,6 +9,7 @@ import javax.persistence.GeneratedValue;
|
|||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.MapKeyClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
|
||||
import org.hibernate.annotations.MapKey;
|
||||
import org.hibernate.annotations.MapKeyManyToMany;
|
||||
|
@ -27,8 +28,8 @@ public class Brand {
|
|||
private Map<Size, Luggage> luggagesBySize = new HashMap<Size, Luggage>();
|
||||
|
||||
@ElementCollection(targetClass = SizeImpl.class)
|
||||
@MapKeyManyToMany(targetEntity = LuggageImpl.class)
|
||||
//TODO @MapKeyClass(LuggageImpl.class)
|
||||
@MapKeyClass(LuggageImpl.class)
|
||||
@MapKeyManyToMany //legacy column name: was never officially supported BTW
|
||||
private Map<Luggage, Size> sizePerLuggage = new HashMap<Luggage, Size>();
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue