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

(cherry picked from commit c893577efc)

Conflicts:
	hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java
This commit is contained in:
Gail Badner 2016-07-13 20:27:36 -07:00
parent c1f3b54194
commit cc94c57097
7 changed files with 183 additions and 33 deletions

View File

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

View File

@ -319,11 +319,17 @@ public abstract class Collection implements Fetchable, Value, Filterable {
checkColumnDuplication();
}
private void checkColumnDuplication(java.util.Set distinctColumns, Iterator columns)
private void checkColumnDuplication(java.util.Set distinctColumns, Value value)
throws MappingException {
while ( columns.hasNext() ) {
Selectable s = (Selectable) columns.next();
if ( !s.isFormula() ) {
final boolean[] insertability = value.getColumnInsertability();
final boolean[] updatability = value.getColumnUpdateability();
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;
if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException(
@ -334,28 +340,27 @@ public abstract class Collection implements Fetchable, Value, Filterable {
);
}
}
i++;
}
}
private void checkColumnDuplication() throws MappingException {
HashSet cols = new HashSet();
checkColumnDuplication( cols, getKey().getColumnIterator() );
checkColumnDuplication( cols, getKey() );
if ( isIndexed() ) {
checkColumnDuplication(
cols, ( (IndexedCollection) this )
.getIndex()
.getColumnIterator()
cols,
( (IndexedCollection) this ).getIndex()
);
}
if ( isIdentified() ) {
checkColumnDuplication(
cols, ( (IdentifierCollection) this )
.getIdentifier()
.getColumnIterator()
cols,
( (IdentifierCollection) this ).getIdentifier()
);
}
if ( !isOneToMany() ) {
checkColumnDuplication( cols, getElement().getColumnIterator() );
checkColumnDuplication( cols, getElement() );
}
}

View File

@ -58,6 +58,8 @@ public class SimpleValue implements KeyValue {
private final MetadataImplementor metadata;
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 Properties typeParameters;
@ -103,15 +105,32 @@ public class SimpleValue implements KeyValue {
}
public void addColumn(Column column) {
if ( !columns.contains(column) ) {
columns.add(column);
}
column.setValue(this);
column.setTypeIndex( columns.size()-1 );
addColumn( column, true, true );
}
public void addColumn(Column column, boolean isInsertable, boolean isUpdatable) {
int index = columns.indexOf( column );
if ( index == -1 ) {
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.setTypeIndex( columns.size() - 1 );
}
public void addFormula(Formula formula) {
columns.add(formula);
columns.add( formula );
insertability.add( false );
updatability.add( false );
}
@Override
@ -565,18 +584,20 @@ public class SimpleValue implements KeyValue {
}
public boolean[] getColumnInsertability() {
boolean[] result = new boolean[ getColumnSpan() ];
int i = 0;
Iterator iter = getColumnIterator();
while ( iter.hasNext() ) {
Selectable s = (Selectable) iter.next();
result[i++] = !s.isFormula();
}
return result;
return extractBooleansFromList( insertability );
}
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 setJpaAttributeConverterDefinition(AttributeConverterDefinition attributeConverterDefinition) {

View File

@ -151,6 +151,7 @@ public abstract class AbstractCollectionPersister
protected final String[] indexColumnNames;
protected final String[] indexFormulaTemplates;
protected final String[] indexFormulas;
protected final boolean[] indexColumnIsGettable;
protected final boolean[] indexColumnIsSettable;
protected final String[] elementColumnNames;
protected final String[] elementColumnWriters;
@ -375,10 +376,13 @@ public abstract class AbstractCollectionPersister
IndexedCollection indexedCollection = (IndexedCollection) collectionBinding;
indexType = indexedCollection.getIndex().getType();
int indexSpan = indexedCollection.getIndex().getColumnSpan();
boolean[] indexColumnInsertability = indexedCollection.getIndex().getColumnInsertability();
boolean[] indexColumnUpdatability = indexedCollection.getIndex().getColumnUpdateability();
iter = indexedCollection.getIndex().getColumnIterator();
indexColumnNames = new String[indexSpan];
indexFormulaTemplates = new String[indexSpan];
indexFormulas = new String[indexSpan];
indexColumnIsGettable = new boolean[indexSpan];
indexColumnIsSettable = new boolean[indexSpan];
indexColumnAliases = new String[indexSpan];
int i = 0;
@ -395,7 +399,8 @@ public abstract class AbstractCollectionPersister
else {
Column indexCol = (Column) s;
indexColumnNames[i] = indexCol.getQuotedName( dialect );
indexColumnIsSettable[i] = true;
indexColumnIsGettable[i] = true;
indexColumnIsSettable[i] = indexColumnInsertability[i] || indexColumnUpdatability[i];
}
i++;
}
@ -405,6 +410,7 @@ public abstract class AbstractCollectionPersister
}
else {
indexContainsFormula = false;
indexColumnIsGettable = null;
indexColumnIsSettable = null;
indexFormulaTemplates = null;
indexFormulas = null;
@ -1072,8 +1078,8 @@ public abstract class AbstractCollectionPersister
protected void appendIndexColumns(SelectFragment frag, String alias) {
if ( hasIndex ) {
for ( int i = 0; i < indexColumnIsSettable.length; i++ ) {
if ( indexColumnIsSettable[i] ) {
for ( int i = 0; i < indexColumnIsGettable.length; i++ ) {
if ( indexColumnIsGettable[i] ) {
frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] );
}
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 Brett Meyer
* @author Gail Badner
*/
public class PersistentMapTest extends BaseCoreFunctionalTestCase {
@Override
@ -47,7 +48,11 @@ public class PersistentMapTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { User.class, UserData.class };
return new Class<?>[] {
User.class,
UserData.class,
MultilingualString.class
};
}
@Test
@ -196,6 +201,39 @@ public class PersistentMapTest extends BaseCoreFunctionalTestCase {
s.getTransaction().commit();
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
@Table(name = "MyUser")
@ -218,4 +256,5 @@ public class PersistentMapTest extends BaseCoreFunctionalTestCase {
@JoinColumn(name = "userId")
private User user;
}
}