HHH-9834 - Fix primary key generation for Map-based ElementCollections that use Lob.
This commit is contained in:
parent
4cbb53c17a
commit
cc56c9672b
|
@ -47,6 +47,7 @@ import org.hibernate.envers.internal.entities.mapper.relation.SortedSetCollectio
|
|||
import org.hibernate.envers.internal.entities.mapper.relation.ToOneIdMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleDummyComponentMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleEmbeddableComponentMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleMapElementNotKeyComponentMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleMapKeyIdComponentMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleMapKeyPropertyComponentMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleRelatedComponentMapper;
|
||||
|
@ -78,6 +79,8 @@ import org.hibernate.type.ComponentType;
|
|||
import org.hibernate.type.ListType;
|
||||
import org.hibernate.type.ManyToOneType;
|
||||
import org.hibernate.type.MapType;
|
||||
import org.hibernate.type.MaterializedClobType;
|
||||
import org.hibernate.type.MaterializedNClobType;
|
||||
import org.hibernate.type.SetType;
|
||||
import org.hibernate.type.SortedMapType;
|
||||
import org.hibernate.type.SortedSetType;
|
||||
|
@ -467,7 +470,8 @@ public final class CollectionMetadataGenerator {
|
|||
middleEntityXml,
|
||||
queryGeneratorBuilder,
|
||||
referencedPrefix,
|
||||
propertyAuditingData.getJoinTable().inverseJoinColumns()
|
||||
propertyAuditingData.getJoinTable().inverseJoinColumns(),
|
||||
isCollectionElementKeyProperty()
|
||||
);
|
||||
|
||||
// ******
|
||||
|
@ -510,7 +514,8 @@ public final class CollectionMetadataGenerator {
|
|||
middleEntityXml,
|
||||
queryGeneratorBuilder,
|
||||
"mapkey",
|
||||
null
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -562,7 +567,8 @@ public final class CollectionMetadataGenerator {
|
|||
Element xmlMapping,
|
||||
QueryGeneratorBuilder queryGeneratorBuilder,
|
||||
String prefix,
|
||||
JoinColumn[] joinColumns) {
|
||||
JoinColumn[] joinColumns,
|
||||
boolean key) {
|
||||
final Type type = value.getType();
|
||||
if ( type instanceof ManyToOneType ) {
|
||||
final String prefixRelated = prefix + "_";
|
||||
|
@ -673,7 +679,7 @@ public final class CollectionMetadataGenerator {
|
|||
else {
|
||||
// Last but one parameter: collection components are always insertable
|
||||
final boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(
|
||||
xmlMapping,
|
||||
key ? xmlMapping : xmlMapping.getParent(),
|
||||
new PropertyAuditingData(
|
||||
prefix,
|
||||
"field",
|
||||
|
@ -686,16 +692,23 @@ public final class CollectionMetadataGenerator {
|
|||
value,
|
||||
null,
|
||||
true,
|
||||
true
|
||||
key
|
||||
);
|
||||
|
||||
if ( mapped ) {
|
||||
if ( mapped && key ) {
|
||||
// Simple values are always stored in the first item of the array returned by the query generator.
|
||||
return new MiddleComponentData(
|
||||
new MiddleSimpleComponentMapper( mainGenerator.getVerEntCfg(), prefix ),
|
||||
0
|
||||
);
|
||||
}
|
||||
else if ( mapped && !key ) {
|
||||
// when mapped but not part of the key, its stored as a dummy mapper??
|
||||
return new MiddleComponentData(
|
||||
new MiddleMapElementNotKeyComponentMapper( mainGenerator.getVerEntCfg(), prefix ),
|
||||
0
|
||||
);
|
||||
}
|
||||
else {
|
||||
mainGenerator.throwUnsupportedTypeException( type, referencingEntityName, propertyName );
|
||||
// Impossible to get here.
|
||||
|
@ -1008,4 +1021,22 @@ public final class CollectionMetadataGenerator {
|
|||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the collection element should participate in the primary key association. This
|
||||
* is only applicable for non-component, non-associative collection element types.
|
||||
*
|
||||
* @return {@code true} if should be part of the primary key, {@code false} if not.
|
||||
*/
|
||||
private boolean isCollectionElementKeyProperty() {
|
||||
// When List or Set are used, the element will always be part of the primary key.
|
||||
// When using a Map, the element isn't required as the Map Key will suffice.
|
||||
if ( propertyValue instanceof org.hibernate.mapping.Map ) {
|
||||
final Type type = propertyValue.getElement().getType();
|
||||
if ( !type.isComponentType() && !type.isAssociationType() ) {
|
||||
return !( type instanceof MaterializedClobType || type instanceof MaterializedNClobType );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,14 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.MapKey;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.annotations.common.reflection.ClassLoadingException;
|
||||
import org.hibernate.annotations.common.reflection.ReflectionManager;
|
||||
|
@ -35,6 +38,7 @@ import org.hibernate.envers.NotAudited;
|
|||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
|
||||
import org.hibernate.envers.configuration.internal.metadata.MetadataTools;
|
||||
import org.hibernate.envers.internal.EnversMessageLogger;
|
||||
import org.hibernate.envers.internal.tools.MappingTools;
|
||||
import org.hibernate.envers.internal.tools.ReflectionTools;
|
||||
import org.hibernate.envers.internal.tools.StringTools;
|
||||
|
@ -42,6 +46,7 @@ import org.hibernate.loader.PropertyPath;
|
|||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.envers.internal.tools.Tools.newHashMap;
|
||||
import static org.hibernate.envers.internal.tools.Tools.newHashSet;
|
||||
|
@ -58,6 +63,11 @@ import static org.hibernate.envers.internal.tools.Tools.newHashSet;
|
|||
* @author Lukasz Zuchowski (author at zuchos dot com)
|
||||
*/
|
||||
public class AuditedPropertiesReader {
|
||||
private static final EnversMessageLogger LOG = Logger.getMessageLogger(
|
||||
EnversMessageLogger.class,
|
||||
AuditedPropertiesReader.class.getName()
|
||||
);
|
||||
|
||||
protected final ModificationStore defaultStore;
|
||||
private final PersistentPropertiesSource persistentPropertiesSource;
|
||||
private final AuditedPropertiesHolder auditedPropertiesHolder;
|
||||
|
@ -502,6 +512,8 @@ public class AuditedPropertiesReader {
|
|||
}
|
||||
}
|
||||
|
||||
validateLobMappingSupport( property );
|
||||
|
||||
final String propertyName = propertyNamePrefix + property.getName();
|
||||
if ( !this.checkAudited( property, propertyData,propertyName, allClassAudited, globalCfg.getModifiedFlagSuffix() ) ) {
|
||||
return false;
|
||||
|
@ -524,6 +536,30 @@ public class AuditedPropertiesReader {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void validateLobMappingSupport(XProperty property) {
|
||||
// HHH-9834 - Sanity check
|
||||
try {
|
||||
if ( property.isAnnotationPresent( ElementCollection.class ) ) {
|
||||
if ( property.isAnnotationPresent( Lob.class ) ) {
|
||||
if ( !property.getCollectionClass().isAssignableFrom( Map.class ) ) {
|
||||
throw new MappingException(
|
||||
"@ElementCollection combined with @Lob is only supported for Map collection types."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( MappingException e ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Invalid mapping in [%s] for property [%s]",
|
||||
property.getDeclaringClass().getName(),
|
||||
property.getName()
|
||||
),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean checkAudited(
|
||||
XProperty property,
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.envers.internal.entities.mapper.relation.component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
|
||||
import org.hibernate.envers.internal.entities.EntityInstantiator;
|
||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||
|
||||
/**
|
||||
* A middle table component mapper which assigns a Map-type's element as part
|
||||
* of the data-portion of the mapping rather than the identifier.
|
||||
* <p>
|
||||
* This is useful for mappings where the database does not support CLOB or NCLOB
|
||||
* data types as part of the primary key for the table.
|
||||
* </p>
|
||||
* An example:
|
||||
* <pre>
|
||||
* @ElementCollection
|
||||
* @Lob
|
||||
* private Map<String, String> values;
|
||||
* </pre>
|
||||
*
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class MiddleMapElementNotKeyComponentMapper implements MiddleComponentMapper {
|
||||
private final String propertyName;
|
||||
private final AuditEntitiesConfiguration verEntCfg;
|
||||
|
||||
public MiddleMapElementNotKeyComponentMapper(AuditEntitiesConfiguration verEntCfg, String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
this.verEntCfg = verEntCfg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public Object mapToObjectFromFullMap(
|
||||
EntityInstantiator entityInstantiator,
|
||||
Map<String, Object> data,
|
||||
Object dataObject,
|
||||
Number revision) {
|
||||
return data.get( propertyName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapToMapFromObject(
|
||||
SessionImplementor session,
|
||||
Map<String, Object> idData,
|
||||
Map<String, Object> data,
|
||||
Object obj) {
|
||||
data.put( propertyName, obj );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMiddleEqualToQuery(
|
||||
Parameters parameters,
|
||||
String idPrefix1,
|
||||
String prefix1,
|
||||
String idPrefix2,
|
||||
String prefix2) {
|
||||
parameters.addWhere( prefix1 + "." + propertyName, false, "=", prefix2 + "." + propertyName, false );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue