org.hibernate.cfg.ComponentPropertyHolder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of beangle-hibernate-core Show documentation
Show all versions of beangle-hibernate-core Show documentation
Hibernate Orm Core Shade,Support Scala Collection
/*
* 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 .
*/
package org.hibernate.cfg;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Converts;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
/**
* PropertyHolder for composites (Embeddable/Embedded).
*
* To facilitate code comments, I'll often refer to this example:
*
* @Embeddable
* @Convert( attributeName="city", ... )
* class Address {
* ...
* @Convert(...)
* public String city;
* }
*
* @Entity
* @Convert( attributeName="homeAddress.city", ... )
* class Person {
* ...
* @Embedded
* @Convert( attributeName="city", ... )
* public Address homeAddress;
* }
*
*
* As you can see, lots of ways to specify the conversion for embeddable attributes :(
*
* @author Steve Ebersole
* @author Emmanuel Bernard
*/
public class ComponentPropertyHolder extends AbstractPropertyHolder {
//TODO introduce an overrideTable() method for columns held by sec table rather than the hack
// joinsPerRealTableName in ClassPropertyHolder
private Component component;
private boolean isOrWithinEmbeddedId;
private boolean isWithinElementCollection;
// private boolean virtual;
private String embeddedAttributeName;
private Map attributeConversionInfoMap;
public ComponentPropertyHolder(
Component component,
String path,
PropertyData inferredData,
PropertyHolder parent,
MetadataBuildingContext context) {
super( path, parent, inferredData.getPropertyClass(), context );
final XProperty embeddedXProperty = inferredData.getProperty();
setCurrentProperty( embeddedXProperty );
this.component = component;
this.isOrWithinEmbeddedId =
parent.isOrWithinEmbeddedId()
|| ( embeddedXProperty != null &&
( embeddedXProperty.isAnnotationPresent( Id.class )
|| embeddedXProperty.isAnnotationPresent( EmbeddedId.class ) ) );
this.isWithinElementCollection = parent.isWithinElementCollection() ||
parent instanceof CollectionPropertyHolder;
if ( embeddedXProperty != null ) {
// this.virtual = false;
this.embeddedAttributeName = embeddedXProperty.getName();
this.attributeConversionInfoMap = processAttributeConversions( embeddedXProperty );
}
else {
// could be either:
// 1) virtual/dynamic component
// 2) collection element/key
// temp
// this.virtual = true;
this.embeddedAttributeName = "";
this.attributeConversionInfoMap = processAttributeConversions( inferredData.getClassOrElement() );
}
}
/**
* This is called from our constructor and handles (in order):
* - @Convert annotation at the Embeddable class level
* - @Converts annotation at the Embeddable class level
* - @Convert annotation at the Embedded attribute level
* - @Converts annotation at the Embedded attribute level
*
*
* The order is important to ensure proper precedence.
*
* {@literal @Convert/@Converts} annotations at the Embeddable attribute level are handled in the calls to
* {@link #startingProperty}. Duplicates are simply ignored there.
*
* @param embeddedXProperty The property that is the composite being described by this ComponentPropertyHolder
*/
private Map processAttributeConversions(XProperty embeddedXProperty) {
final Map infoMap = new HashMap();
final XClass embeddableXClass = embeddedXProperty.getType();
// as a baseline, we want to apply conversions from the Embeddable and then overlay conversions
// from the Embedded
// first apply conversions from the Embeddable...
processAttributeConversions( embeddableXClass, infoMap );
// then we can overlay any conversions from the Embedded attribute
{
// @Convert annotation on the Embedded attribute
final Convert convertAnnotation = embeddedXProperty.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "Convert placed on Embedded attribute must define (sub)attributeName" );
}
infoMap.put( info.getAttributeName(), info );
}
}
{
// @Converts annotation on the Embedded attribute
final Converts convertsAnnotation = embeddedXProperty.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "Convert placed on Embedded attribute must define (sub)attributeName" );
}
infoMap.put( info.getAttributeName(), info );
}
}
}
return infoMap;
}
private void processAttributeConversions(XClass embeddableXClass, Map infoMap) {
{
// @Convert annotation on the Embeddable class level
final Convert convertAnnotation = embeddableXClass.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Convert placed on @Embeddable must define attributeName" );
}
infoMap.put( info.getAttributeName(), info );
}
}
{
// @Converts annotation on the Embeddable class level
final Converts convertsAnnotation = embeddableXClass.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Converts placed on @Embeddable must define attributeName" );
}
infoMap.put( info.getAttributeName(), info );
}
}
}
}
private Map processAttributeConversions(XClass embeddableXClass) {
final Map infoMap = new HashMap();
processAttributeConversions( embeddableXClass, infoMap );
return infoMap;
}
@Override
protected String normalizeCompositePath(String attributeName) {
return embeddedAttributeName + '.' + attributeName;
}
@Override
protected String normalizeCompositePathForLogging(String attributeName) {
return normalizeCompositePath( attributeName );
}
@Override
public void startingProperty(XProperty property) {
if ( property == null ) {
return;
}
// if ( virtual ) {
// return;
// }
// again : the property coming in here *should* be the property on the embeddable (Address#city in the example),
// so we just ignore it if there is already an existing conversion info for that path since they would have
// precedence
// technically we should only do this for properties of "basic type"
final String path = embeddedAttributeName + '.' + property.getName();
if ( attributeConversionInfoMap.containsKey( path ) ) {
return;
}
{
// @Convert annotation on the Embeddable attribute
final Convert convertAnnotation = property.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
attributeConversionInfoMap.put( property.getName(), info );
}
}
{
// @Converts annotation on the Embeddable attribute
final Converts convertsAnnotation = property.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
attributeConversionInfoMap.put( property.getName(), info );
}
}
}
}
@Override
protected AttributeConversionInfo locateAttributeConversionInfo(XProperty property) {
final String propertyName = property.getName();
// conversions on parent would have precedence
AttributeConversionInfo conversion = locateAttributeConversionInfo( propertyName );
if ( conversion != null ) {
return conversion;
}
return null;
}
@Override
protected AttributeConversionInfo locateAttributeConversionInfo(String path) {
final String embeddedPath = StringHelper.qualifyConditionally( embeddedAttributeName, path );
AttributeConversionInfo fromParent = parent.locateAttributeConversionInfo( embeddedPath );
if ( fromParent != null ) {
return fromParent;
}
AttributeConversionInfo fromEmbedded = attributeConversionInfoMap.get( embeddedPath );
if ( fromEmbedded != null ) {
return fromEmbedded;
}
return attributeConversionInfoMap.get( path );
}
public String getEntityName() {
return component.getComponentClassName();
}
public void addProperty(Property prop, Ejb3Column[] columns, XClass declaringClass) {
//Ejb3Column.checkPropertyConsistency( ); //already called earlier
/*
* Check table matches between the component and the columns
* if not, change the component table if no properties are set
* if a property is set already the core cannot support that
*/
if (columns != null) {
Table table = columns[0].getTable();
if ( !table.equals( component.getTable() ) ) {
if ( component.getPropertySpan() == 0 ) {
component.setTable( table );
}
else {
throw new AnnotationException(
"A component cannot hold properties split into 2 different tables: "
+ this.getPath()
);
}
}
}
addProperty( prop, declaringClass );
}
public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
return parent.addJoin( joinTableAnn, noDelayInPkColumnCreation );
}
public String getClassName() {
return component.getComponentClassName();
}
public String getEntityOwnerClassName() {
return component.getOwner().getClassName();
}
public Table getTable() {
return component.getTable();
}
public void addProperty(Property prop, XClass declaringClass) {
component.addProperty( prop );
}
public KeyValue getIdentifier() {
return component.getOwner().getIdentifier();
}
public boolean isOrWithinEmbeddedId() {
return isOrWithinEmbeddedId;
}
public boolean isWithinElementCollection() {
return isWithinElementCollection;
}
public PersistentClass getPersistentClass() {
return component.getOwner();
}
public boolean isComponent() {
return true;
}
public boolean isEntity() {
return false;
}
public void setParentProperty(String parentProperty) {
component.setParentProperty( parentProperty );
}
@Override
public Column[] getOverriddenColumn(String propertyName) {
//FIXME this is yukky
Column[] result = super.getOverriddenColumn( propertyName );
if ( result == null ) {
String userPropertyName = extractUserPropertyName( "id", propertyName );
if ( userPropertyName != null ) result = super.getOverriddenColumn( userPropertyName );
}
if ( result == null ) {
String userPropertyName = extractUserPropertyName( PropertyPath.IDENTIFIER_MAPPER_PROPERTY, propertyName );
if ( userPropertyName != null ) result = super.getOverriddenColumn( userPropertyName );
}
return result;
}
private String extractUserPropertyName(String redundantString, String propertyName) {
String result = null;
String className = component.getOwner().getClassName();
if ( propertyName.startsWith( className )
&& propertyName.length() > className.length() + 2 + redundantString.length() // .id.
&& propertyName.substring(
className.length() + 1, className.length() + 1 + redundantString.length()
).equals( redundantString )
) {
//remove id we might be in a @IdCLass case
result = className + propertyName.substring( className.length() + 1 + redundantString.length() );
}
return result;
}
@Override
public JoinColumn[] getOverriddenJoinColumn(String propertyName) {
return super.getOverriddenJoinColumn( propertyName );
}
@Override
public String toString() {
return super.toString() + "(" + parent.normalizeCompositePathForLogging( embeddedAttributeName ) + ")";
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy