HHH-5393 : MappingException when @MapKeyColumn refers to a column mapped in embeddable map value

This commit is contained in:
Gail Badner 2016-07-13 20:27:36 -07:00
parent 9ac00406d2
commit c893577efc
7 changed files with 179 additions and 29 deletions

View File

@ -366,7 +366,7 @@ public class Ejb3Column {
} }
else { else {
getMappingColumn().setValue( value ); getMappingColumn().setValue( value );
value.addColumn( getMappingColumn() ); value.addColumn( getMappingColumn(), insertable, updatable );
value.getTable().addColumn( getMappingColumn() ); value.getTable().addColumn( getMappingColumn() );
addColumnBinding( value ); addColumnBinding( value );
table = value.getTable(); table = value.getTable();

View File

@ -319,11 +319,17 @@ public abstract class Collection implements Fetchable, Value, Filterable {
checkColumnDuplication(); checkColumnDuplication();
} }
private void checkColumnDuplication(java.util.Set distinctColumns, Iterator columns) private void checkColumnDuplication(java.util.Set distinctColumns, Value value)
throws MappingException { throws MappingException {
while ( columns.hasNext() ) { final boolean[] insertability = value.getColumnInsertability();
Selectable s = (Selectable) columns.next(); final boolean[] updatability = value.getColumnUpdateability();
if ( !s.isFormula() ) { final Iterator<Selectable> iterator = value.getColumnIterator();
int i = 0;
while ( iterator.hasNext() ) {
Selectable s = iterator.next();
// exclude formulas and coluns that are not insertable or updatable
// since these values can be be repeated (HHH-5393)
if ( !s.isFormula() && ( insertability[i] || updatability[i] ) ) {
Column col = (Column) s; Column col = (Column) s;
if ( !distinctColumns.add( col.getName() ) ) { if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException( throw new MappingException(
@ -334,28 +340,27 @@ public abstract class Collection implements Fetchable, Value, Filterable {
); );
} }
} }
i++;
} }
} }
private void checkColumnDuplication() throws MappingException { private void checkColumnDuplication() throws MappingException {
HashSet cols = new HashSet(); HashSet cols = new HashSet();
checkColumnDuplication( cols, getKey().getColumnIterator() ); checkColumnDuplication( cols, getKey() );
if ( isIndexed() ) { if ( isIndexed() ) {
checkColumnDuplication( checkColumnDuplication(
cols, ( (IndexedCollection) this ) cols,
.getIndex() ( (IndexedCollection) this ).getIndex()
.getColumnIterator()
); );
} }
if ( isIdentified() ) { if ( isIdentified() ) {
checkColumnDuplication( checkColumnDuplication(
cols, ( (IdentifierCollection) this ) cols,
.getIdentifier() ( (IdentifierCollection) this ).getIdentifier()
.getColumnIterator()
); );
} }
if ( !isOneToMany() ) { if ( !isOneToMany() ) {
checkColumnDuplication( cols, getElement().getColumnIterator() ); checkColumnDuplication( cols, getElement() );
} }
} }

View File

@ -64,6 +64,8 @@ public class SimpleValue implements KeyValue {
private final MetadataImplementor metadata; private final MetadataImplementor metadata;
private final List<Selectable> columns = new ArrayList<Selectable>(); private final List<Selectable> columns = new ArrayList<Selectable>();
private final List<Boolean> insertability = new ArrayList<Boolean>();
private final List<Boolean> updatability = new ArrayList<Boolean>();
private String typeName; private String typeName;
private Properties typeParameters; private Properties typeParameters;
@ -111,8 +113,23 @@ public class SimpleValue implements KeyValue {
} }
public void addColumn(Column column) { public void addColumn(Column column) {
if ( !columns.contains(column) ) { addColumn( column, true, true );
}
public void addColumn(Column column, boolean isInsertable, boolean isUpdatable) {
int index = columns.indexOf( column );
if ( index == -1 ) {
columns.add(column); columns.add(column);
insertability.add( isInsertable );
updatability.add( isUpdatable );
}
else {
if ( insertability.get( index ) != isInsertable ) {
throw new IllegalStateException( "Same column is added more than once with different values for isInsertable" );
}
if ( updatability.get( index ) != isUpdatable ) {
throw new IllegalStateException( "Same column is added more than once with different values for isUpdatable" );
}
} }
column.setValue( this ); column.setValue( this );
column.setTypeIndex( columns.size() - 1 ); column.setTypeIndex( columns.size() - 1 );
@ -120,6 +137,8 @@ public class SimpleValue implements KeyValue {
public void addFormula(Formula formula) { public void addFormula(Formula formula) {
columns.add( formula ); columns.add( formula );
insertability.add( false );
updatability.add( false );
} }
@Override @Override
@ -608,18 +627,20 @@ public class SimpleValue implements KeyValue {
} }
public boolean[] getColumnInsertability() { public boolean[] getColumnInsertability() {
boolean[] result = new boolean[ getColumnSpan() ]; return extractBooleansFromList( insertability );
int i = 0;
Iterator iter = getColumnIterator();
while ( iter.hasNext() ) {
Selectable s = (Selectable) iter.next();
result[i++] = !s.isFormula();
}
return result;
} }
public boolean[] getColumnUpdateability() { public boolean[] getColumnUpdateability() {
return getColumnInsertability(); return extractBooleansFromList( updatability );
}
private static boolean[] extractBooleansFromList(List<Boolean> list) {
final boolean[] array = new boolean[ list.size() ];
int i = 0;
for ( Boolean value : list ) {
array[ i++ ] = value;
}
return array;
} }
public void setJpaAttributeConverterDescriptor(AttributeConverterDescriptor attributeConverterDescriptor) { public void setJpaAttributeConverterDescriptor(AttributeConverterDescriptor attributeConverterDescriptor) {

View File

@ -151,6 +151,7 @@ public abstract class AbstractCollectionPersister
protected final String[] indexColumnNames; protected final String[] indexColumnNames;
protected final String[] indexFormulaTemplates; protected final String[] indexFormulaTemplates;
protected final String[] indexFormulas; protected final String[] indexFormulas;
protected final boolean[] indexColumnIsGettable;
protected final boolean[] indexColumnIsSettable; protected final boolean[] indexColumnIsSettable;
protected final String[] elementColumnNames; protected final String[] elementColumnNames;
protected final String[] elementColumnWriters; protected final String[] elementColumnWriters;
@ -375,10 +376,13 @@ public abstract class AbstractCollectionPersister
IndexedCollection indexedCollection = (IndexedCollection) collectionBinding; IndexedCollection indexedCollection = (IndexedCollection) collectionBinding;
indexType = indexedCollection.getIndex().getType(); indexType = indexedCollection.getIndex().getType();
int indexSpan = indexedCollection.getIndex().getColumnSpan(); int indexSpan = indexedCollection.getIndex().getColumnSpan();
boolean[] indexColumnInsertability = indexedCollection.getIndex().getColumnInsertability();
boolean[] indexColumnUpdatability = indexedCollection.getIndex().getColumnUpdateability();
iter = indexedCollection.getIndex().getColumnIterator(); iter = indexedCollection.getIndex().getColumnIterator();
indexColumnNames = new String[indexSpan]; indexColumnNames = new String[indexSpan];
indexFormulaTemplates = new String[indexSpan]; indexFormulaTemplates = new String[indexSpan];
indexFormulas = new String[indexSpan]; indexFormulas = new String[indexSpan];
indexColumnIsGettable = new boolean[indexSpan];
indexColumnIsSettable = new boolean[indexSpan]; indexColumnIsSettable = new boolean[indexSpan];
indexColumnAliases = new String[indexSpan]; indexColumnAliases = new String[indexSpan];
int i = 0; int i = 0;
@ -395,7 +399,8 @@ public abstract class AbstractCollectionPersister
else { else {
Column indexCol = (Column) s; Column indexCol = (Column) s;
indexColumnNames[i] = indexCol.getQuotedName( dialect ); indexColumnNames[i] = indexCol.getQuotedName( dialect );
indexColumnIsSettable[i] = true; indexColumnIsGettable[i] = true;
indexColumnIsSettable[i] = indexColumnInsertability[i] || indexColumnUpdatability[i];
} }
i++; i++;
} }
@ -405,6 +410,7 @@ public abstract class AbstractCollectionPersister
} }
else { else {
indexContainsFormula = false; indexContainsFormula = false;
indexColumnIsGettable = null;
indexColumnIsSettable = null; indexColumnIsSettable = null;
indexFormulaTemplates = null; indexFormulaTemplates = null;
indexFormulas = null; indexFormulas = null;
@ -1076,8 +1082,8 @@ public abstract class AbstractCollectionPersister
protected void appendIndexColumns(SelectFragment frag, String alias) { protected void appendIndexColumns(SelectFragment frag, String alias) {
if ( hasIndex ) { if ( hasIndex ) {
for ( int i = 0; i < indexColumnIsSettable.length; i++ ) { for ( int i = 0; i < indexColumnIsGettable.length; i++ ) {
if ( indexColumnIsSettable[i] ) { if ( indexColumnIsGettable[i] ) {
frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] ); frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] );
} }
else { else {

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.collection.map;
import javax.persistence.Embeddable;
@Embeddable
public class LocalizedString {
private String language;
private String text;
public LocalizedString() {
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.collection.map;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
@Entity
@Table(name = "multilingual")
public class MultilingualString {
@Id
@GeneratedValue
private long id;
@ElementCollection
@MapKeyColumn(name = "language", insertable = false, updatable = false)
@CollectionTable(name = "multilingual_string_map", joinColumns = @JoinColumn(name = "string_id"))
private Map<String, LocalizedString> map = new HashMap<String, LocalizedString>();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Map<String, LocalizedString> getMap() {
return map;
}
public void setMap(Map<String, LocalizedString> map) {
this.map = map;
}
}

View File

@ -38,6 +38,7 @@ import org.junit.Test;
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Brett Meyer * @author Brett Meyer
* @author Gail Badner
*/ */
public class PersistentMapTest extends BaseCoreFunctionalTestCase { public class PersistentMapTest extends BaseCoreFunctionalTestCase {
@Override @Override
@ -47,7 +48,11 @@ public class PersistentMapTest extends BaseCoreFunctionalTestCase {
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { User.class, UserData.class }; return new Class<?>[] {
User.class,
UserData.class,
MultilingualString.class
};
} }
@Test @Test
@ -197,6 +202,39 @@ public class PersistentMapTest extends BaseCoreFunctionalTestCase {
s.close(); s.close();
} }
@Test
@TestForIssue( jiraKey = "HHH-5393")
public void testMapKeyColumnInEmbeddableElement() {
Session s = openSession();
s.getTransaction().begin();
MultilingualString m = new MultilingualString();
LocalizedString localizedString = new LocalizedString();
localizedString.setLanguage( "English" );
localizedString.setText( "name" );
m.getMap().put( localizedString.getLanguage(), localizedString );
localizedString = new LocalizedString();
localizedString.setLanguage( "English Pig Latin" );
localizedString.setText( "amenay" );
m.getMap().put( localizedString.getLanguage(), localizedString );
s.persist( m );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
m = s.get( MultilingualString.class, m.getId());
assertEquals( 2, m.getMap().size() );
localizedString = m.getMap().get( "English" );
assertEquals( "English", localizedString.getLanguage() );
assertEquals( "name", localizedString.getText() );
localizedString = m.getMap().get( "English Pig Latin" );
assertEquals( "English Pig Latin", localizedString.getLanguage() );
assertEquals( "amenay", localizedString.getText() );
s.delete( m );
s.getTransaction().commit();
s.close();
}
@Entity @Entity
@Table(name = "MyUser") @Table(name = "MyUser")
private static class User implements Serializable { private static class User implements Serializable {
@ -218,4 +256,5 @@ public class PersistentMapTest extends BaseCoreFunctionalTestCase {
@JoinColumn(name = "userId") @JoinColumn(name = "userId")
private User user; private User user;
} }
} }