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:
parent
c1f3b54194
commit
cc94c57097
|
@ -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();
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue