org.hibernate.cfg.annotations.PropertyBinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-annotations
Show all versions of hibernate-annotations
Annotations metadata for Hibernate
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cfg.annotations;
import java.util.Map;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.OptimisticLock;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.Ejb3Column;
import org.hibernate.cfg.ExtendedMappings;
import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.util.StringHelper;
/**
* @author Emmanuel Bernard
*/
public class PropertyBinder {
private Logger log = LoggerFactory.getLogger( PropertyBinder.class );
private String name;
private String returnedClassName;
private boolean lazy;
private AccessType accessType;
private Ejb3Column[] columns;
private PropertyHolder holder;
private ExtendedMappings mappings;
private Value value;
private boolean insertable = true;
private boolean updatable = true;
private String cascade;
private SimpleValueBinder simpleValueBinder;
private XClass declaringClass;
private boolean declaringClassSet;
private boolean embedded;
private EntityBinder entityBinder;
private boolean isXToMany;
private String referencedEntityName;
public void setReferencedEntityName(String referencedEntityName) {
this.referencedEntityName = referencedEntityName;
}
public void setEmbedded(boolean embedded) {
this.embedded = embedded;
}
public void setEntityBinder(EntityBinder entityBinder) {
this.entityBinder = entityBinder;
}
/*
* property can be null
* prefer propertyName to property.getName() since some are overloaded
*/
private XProperty property;
private XClass returnedClass;
private boolean isId;
private Map inheritanceStatePerClass;
private Property mappingProperty;
public void setInsertable(boolean insertable) {
this.insertable = insertable;
}
public void setUpdatable(boolean updatable) {
this.updatable = updatable;
}
public void setName(String name) {
this.name = name;
}
public void setReturnedClassName(String returnedClassName) {
this.returnedClassName = returnedClassName;
}
public void setLazy(boolean lazy) {
this.lazy = lazy;
}
public void setAccessType(AccessType accessType) {
this.accessType = accessType;
}
public void setColumns(Ejb3Column[] columns) {
insertable = columns[0].isInsertable();
updatable = columns[0].isUpdatable();
//consistency is checked later when we know the property name
this.columns = columns;
}
public void setHolder(PropertyHolder holder) {
this.holder = holder;
}
public void setValue(Value value) {
this.value = value;
}
public void setCascade(String cascadeStrategy) {
this.cascade = cascadeStrategy;
}
public void setMappings(ExtendedMappings mappings) {
this.mappings = mappings;
}
public void setDeclaringClass(XClass declaringClass) {
this.declaringClass = declaringClass;
this.declaringClassSet = true;
}
private void validateBind() {
if ( property.isAnnotationPresent( Immutable.class ) ) {
throw new AnnotationException(
"@Immutable on property not allowed. " +
"Only allowed on entity level or on a collection."
);
}
if ( !declaringClassSet ) {
throw new AssertionFailure( "declaringClass has not been set before a bind" );
}
}
private void validateMake() {
//TODO check necessary params for a make
}
private Property makePropertyAndValue() {
validateBind();
log.debug( "binding property {} with lazy={}", name, lazy );
String containerClassName = holder == null ?
null :
holder.getClassName();
simpleValueBinder = new SimpleValueBinder();
simpleValueBinder.setMappings( mappings );
simpleValueBinder.setPropertyName( name );
simpleValueBinder.setReturnedClassName( returnedClassName );
simpleValueBinder.setColumns( columns );
simpleValueBinder.setPersistentClassName( containerClassName );
simpleValueBinder.setType( property, returnedClass );
simpleValueBinder.setMappings( mappings );
simpleValueBinder.setReferencedEntityName( referencedEntityName );
SimpleValue propertyValue = simpleValueBinder.make();
setValue( propertyValue );
return makeProperty();
}
//used when value is provided
public Property makePropertyAndBind() {
return bind( makeProperty() );
}
//used to build everything from scratch
public Property makePropertyValueAndBind() {
return bind( makePropertyAndValue() );
}
public void setXToMany(boolean xToMany) {
this.isXToMany = xToMany;
}
private Property bind(Property prop) {
if (isId) {
final RootClass rootClass = ( RootClass ) holder.getPersistentClass();
//if an xToMany, it as to be wrapped today.
//FIXME this pose a problem as the PK is the class instead of the associated class which is not really compliant with the spec
if ( isXToMany || entityBinder.wrapIdsInEmbeddedComponents() ) {
Component identifier = (Component) rootClass.getIdentifier();
if (identifier == null) {
identifier = AnnotationBinder.createComponent( holder, new PropertyPreloadedData(null, null, null), true, false );
rootClass.setIdentifier( identifier );
identifier.setNullValue( "undefined" );
rootClass.setEmbeddedIdentifier( true );
}
//FIXME is it good enough?
identifier.addProperty( prop );
}
else {
rootClass.setIdentifier( ( KeyValue ) getValue() );
if (embedded) {
rootClass.setEmbeddedIdentifier( true );
}
else {
rootClass.setIdentifierProperty( prop );
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(
declaringClass,
inheritanceStatePerClass,
mappings
);
if (superclass != null) {
superclass.setDeclaredIdentifierProperty(prop);
}
else {
//we know the property is on the actual entity
rootClass.setDeclaredIdentifierProperty( prop );
}
}
}
}
else {
holder.addProperty( prop, columns, declaringClass );
}
return prop;
}
//used when the value is provided and the binding is done elsewhere
public Property makeProperty() {
validateMake();
log.debug( "Building property " + name );
Property prop = new Property();
prop.setName( name );
prop.setNodeName( name );
prop.setValue( value );
prop.setLazy( lazy );
prop.setCascade( cascade );
prop.setPropertyAccessorName( accessType.getType() );
Generated ann = property != null ?
property.getAnnotation( Generated.class ) :
null;
GenerationTime generated = ann != null ?
ann.value() :
null;
if ( generated != null ) {
if ( !GenerationTime.NEVER.equals( generated ) ) {
if ( property.isAnnotationPresent( javax.persistence.Version.class )
&& GenerationTime.INSERT.equals( generated ) ) {
throw new AnnotationException(
"@Generated(INSERT) on a @Version property not allowed, use ALWAYS: "
+ StringHelper.qualify( holder.getPath(), name )
);
}
insertable = false;
if ( GenerationTime.ALWAYS.equals( generated ) ) {
updatable = false;
}
prop.setGeneration( PropertyGeneration.parse( generated.toString().toLowerCase() ) );
}
}
NaturalId naturalId = property != null ?
property.getAnnotation( NaturalId.class ) :
null;
if ( naturalId != null ) {
if ( !naturalId.mutable() ) {
updatable = false;
}
prop.setNaturalIdentifier( true );
}
prop.setInsertable( insertable );
prop.setUpdateable( updatable );
OptimisticLock lockAnn = property != null ?
property.getAnnotation( OptimisticLock.class ) :
null;
if ( lockAnn != null ) {
prop.setOptimisticLocked( !lockAnn.excluded() );
//TODO this should go to the core as a mapping validation checking
if ( lockAnn.excluded() && (
property.isAnnotationPresent( javax.persistence.Version.class )
|| property.isAnnotationPresent( Id.class )
|| property.isAnnotationPresent( EmbeddedId.class ) ) ) {
throw new AnnotationException(
"@OptimisticLock.exclude=true incompatible with @Id, @EmbeddedId and @Version: "
+ StringHelper.qualify( holder.getPath(), name )
);
}
}
log.trace( "Cascading " + name + " with " + cascade );
this.mappingProperty = prop;
return prop;
}
public void setProperty(XProperty property) {
this.property = property;
}
public void setReturnedClass(XClass returnedClass) {
this.returnedClass = returnedClass;
}
public SimpleValueBinder getSimpleValueBinder() {
return simpleValueBinder;
}
public Value getValue() {
return value;
}
public void setId(boolean id) {
this.isId = id;
}
public void setInheritanceStatePerClass(Map inheritanceStatePerClass) {
this.inheritanceStatePerClass = inheritanceStatePerClass;
}
}