Author: epbernard Date: 2006-04-27 19:11:39 -0400 (Thu, 27 Apr 2006) New Revision: 9822 Added: trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/Generated.java trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/GenerationTime.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/Antenna.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/GeneratedTest.java Modified: trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/PropertyBinder.java Log: ANN-89 support Generated properties Modified: trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml =================================================================== --- trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml 2006-04-27 23:11:39 UTC (rev 9822) @@ -2472,6 +2472,35 @@ person == person.address.owner</programlisting> </sect3> + + <sect3> + <title>Generated properties</title> + + <para>Some properties are generated at insert or update time by your + database. Hibernate can deal with such properties and triggers a + subsequent select to read these properties.</para> + + <programlisting>@Entity +public class Antenna { + @Id public Integer id; + @Generated(GenerationTime.ALWAYS) @Column(insertable = false, updatable = false) + public String longitude; + + @Generated(GenerationTime.INSERT) @Column(insertable = false) + public String latitude; +}</programlisting> + + <para>Annotate your property as <literal>@Generated</literal> You have + to make sure your insertability or updatability does not conflict with + the generation strategy you have chosen. When GenerationTime.INSERT is + chosen, the property must not contains insertable columns, when + GenerationTime.ALWAYS is chosen, the property must not contains + insertable nor updatable columns.</para> + + <para><literal>@Version</literal> properties cannot be + <literal>@Generated(INSERT)</literal> by design, it has to be either + <literal>NEVER</literal> or <literal>ALWAYS</literal>.</para> + </sect3> </sect2> <sect2> Added: trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/Generated.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/Generated.java 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/Generated.java 2006-04-27 23:11:39 UTC (rev 9822) @@ -0,0 +1,18 @@ +//$Id: $ +package org.hibernate.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; + +/** + * The annotated property is generated by the database + * + * @author Emmanuel Bernard + */ +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Generated { + GenerationTime value(); +} Added: trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/GenerationTime.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/GenerationTime.java 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/annotations/GenerationTime.java 2006-04-27 23:11:39 UTC (rev 9822) @@ -0,0 +1,13 @@ +//$Id: $ +package org.hibernate.annotations; + +/** + * When should the generation occurs + * + * @author Emmanuel Bernard + */ +public enum GenerationTime { + NEVER, + INSERT, + ALWAYS +} Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java 2006-04-27 23:11:39 UTC (rev 9822) @@ -1179,12 +1179,10 @@ joinColumns, ann.optional(), getFetchMode( ann.fetch() ), - ignoreNotFound, onDeleteCascade, inferredData.getPropertyName(), - inferredData.getClassOrElementName(), + ignoreNotFound, onDeleteCascade, mappings.getReflectionManager().toXClass( ann.targetEntity() ), - inferredData.getDefaultAccess(), propertyHolder, - false, isIdentifierMapper, mappings + inferredData, false, isIdentifierMapper, mappings ); } else if ( property.isAnnotationPresent( OneToOne.class ) ) { @@ -1202,12 +1200,10 @@ joinColumns, ann.optional(), getFetchMode( ann.fetch() ), - ignoreNotFound, onDeleteCascade, inferredData.getPropertyName(), - inferredData.getClassOrElementName(), + ignoreNotFound, onDeleteCascade, mappings.getReflectionManager().toXClass( ann.targetEntity() ), - inferredData.getDefaultAccess(), propertyHolder, - ann.mappedBy(), trueOneToOne, isIdentifierMapper, mappings + inferredData, ann.mappedBy(), trueOneToOne, isIdentifierMapper, mappings ); } else if ( property.isAnnotationPresent( OneToMany.class ) @@ -1504,6 +1500,7 @@ PropertyBinder binder = new PropertyBinder(); binder.setName( inferredData.getPropertyName() ); binder.setValue( comp ); + binder.setProperty( inferredData.getProperty() ); binder.setPropertyAccessorName( inferredData.getDefaultAccess() ); Property prop = binder.make(); propertyHolder.addProperty( prop ); @@ -1658,6 +1655,7 @@ binder.setName( inferredData.getPropertyName() ); binder.setValue( id ); binder.setPropertyAccessorName( inferredData.getDefaultAccess() ); + binder.setProperty( inferredData.getProperty() ); Property prop = binder.make(); rootClass.setIdentifierProperty( prop ); } @@ -1665,14 +1663,14 @@ private static void bindManyToOne( String cascadeStrategy, Ejb3JoinColumn[] columns, boolean optional, FetchMode fetchMode, - boolean ignoreNotFound, boolean cascadeOnDelete, String propertyName, - String returnedClassName, XClass targetEntity, String propertyAccessorName, PropertyHolder propertyHolder, - boolean unique, boolean isIdentifierMapper, ExtendedMappings mappings + boolean ignoreNotFound, boolean cascadeOnDelete, + XClass targetEntity, PropertyHolder propertyHolder, + PropertyData inferredData, boolean unique, boolean isIdentifierMapper, ExtendedMappings mappings ) { //All FK columns should be in the same table org.hibernate.mapping.ManyToOne value = new org.hibernate.mapping.ManyToOne( columns[0].getTable() ); if ( isDefault( targetEntity, mappings ) ) { - value.setReferencedEntityName( returnedClassName ); + value.setReferencedEntityName( inferredData.getClassOrElementName() ); } else { value.setReferencedEntityName( targetEntity.getName() ); @@ -1686,7 +1684,8 @@ column.setNullable( false ); } } - value.setTypeName( returnedClassName ); + value.setTypeName( inferredData.getClassOrElementName() ); + final String propertyName = inferredData.getPropertyName(); value.setTypeUsingReflection( propertyHolder.getClassName(), propertyName ); //value.createForeignKey(); @@ -1711,7 +1710,7 @@ binder.setInsertable( columns[0].isInsertable() ); binder.setUpdatable( columns[0].isUpdatable() ); } - binder.setPropertyAccessorName( propertyAccessorName ); + binder.setPropertyAccessorName( inferredData.getDefaultAccess() ); binder.setCascade( cascadeStrategy ); Property prop = binder.make(); //composite FK columns are in the same table so its OK @@ -1725,16 +1724,14 @@ FetchMode fetchMode, boolean ignoreNotFound, boolean cascadeOnDelete, - String propertyName, - String returnedClassName, XClass targetEntity, - String propertyAccessorName, PropertyHolder propertyHolder, - String mappedBy, + PropertyData inferredData, String mappedBy, boolean trueOneToOne, boolean isIdentifierMapper, ExtendedMappings mappings ) { //column.getTable() => persistentClass.getTable() + final String propertyName = inferredData.getPropertyName(); log.debug( "Fetching " + propertyName + " with " + fetchMode ); boolean mapToPK = true; if ( ! trueOneToOne ) { @@ -1761,7 +1758,7 @@ ); value.setPropertyName( propertyName ); if ( isDefault( targetEntity, mappings ) ) { - value.setReferencedEntityName( returnedClassName ); + value.setReferencedEntityName( inferredData.getClassOrElementName() ); } else { value.setReferencedEntityName( targetEntity.getName() ); @@ -1803,7 +1800,7 @@ binder.setName( propertyName ); binder.setValue( value ); binder.setCascade( cascadeStrategy ); - binder.setPropertyAccessorName( propertyAccessorName ); + binder.setPropertyAccessorName( inferredData.getDefaultAccess() ); Property prop = binder.make(); prop.setCascade( cascadeStrategy ); //no column associated since its a one to one @@ -1813,8 +1810,8 @@ //has a FK on the table bindManyToOne( cascadeStrategy, columns, optional, fetchMode, ignoreNotFound, cascadeOnDelete, - propertyName, returnedClassName, targetEntity, - propertyAccessorName, propertyHolder, true, isIdentifierMapper, mappings + targetEntity, + propertyHolder, inferredData, true, isIdentifierMapper, mappings ); } } Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java 2006-04-27 23:11:39 UTC (rev 9822) @@ -363,6 +363,7 @@ collection.setOrphanDelete( true ); } binder.setPropertyAccessorName( propertyAccessorName ); + binder.setProperty( property ); binder.setInsertable( insertable ); binder.setUpdatable( updatable ); Property prop = binder.make(); Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/PropertyBinder.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/PropertyBinder.java 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/PropertyBinder.java 2006-04-27 23:11:39 UTC (rev 9822) @@ -3,14 +3,19 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; import org.hibernate.cfg.Ejb3Column; import org.hibernate.cfg.ExtendedMappings; import org.hibernate.cfg.PropertyHolder; import org.hibernate.mapping.Property; import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.Value; +import org.hibernate.mapping.PropertyGeneration; import org.hibernate.reflection.XClass; import org.hibernate.reflection.XProperty; +import org.hibernate.AnnotationException; +import org.hibernate.util.StringHelper; /** * @author Emmanuel Bernard @@ -28,6 +33,10 @@ private boolean insertable = true; private boolean updatable = true; private String cascade; + /* + * property can be null + * prefer propertyName to property.getName() since some are overloaded + */ private XProperty property; private XClass returnedClass; @@ -119,6 +128,32 @@ prop.setLazy( lazy ); prop.setCascade( cascade ); prop.setPropertyAccessorName( propertyAccessorName ); + 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 ) ); + } + if ( prop.isInsertable() ) { + throw new AnnotationException("Cannot have @Generated property and insertable columns: " + + StringHelper.qualify( holder.getPath(), name ) ); + } + if ( GenerationTime.ALWAYS.equals( generated ) && prop.isUpdateable() ) { + throw new AnnotationException("Cannot have @Generated(ALWAYS) property and updatable columns: " + + StringHelper.qualify( holder.getPath(), name ) ); + } + prop.setInsertable( false ); + prop.setUpdateable( false ); + prop.setGeneration( PropertyGeneration.parse( generated.toString().toLowerCase() ) ); + } + } log.debug( "Cascading " + name + " with " + cascade ); return prop; } Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/Antenna.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/Antenna.java 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/Antenna.java 2006-04-27 23:11:39 UTC (rev 9822) @@ -0,0 +1,25 @@ +//$Id: $ +package org.hibernate.test.annotations.various; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Column; + +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; + +/** + * @author Emmanuel Bernard + */ +@Entity +public class Antenna { + @Id public Integer id; + @Generated(GenerationTime.ALWAYS) @Column(insertable = false, updatable = false) + public String longitude; + + @Generated(GenerationTime.INSERT) @Column(insertable = false) + public String latitude; + + @Generated(GenerationTime.NEVER) + public Double power; +} Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/GeneratedTest.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/GeneratedTest.java 2006-04-27 21:12:40 UTC (rev 9821) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/various/GeneratedTest.java 2006-04-27 23:11:39 UTC (rev 9822) @@ -0,0 +1,30 @@ +//$Id: $ +package org.hibernate.test.annotations.various; + +import org.hibernate.test.annotations.TestCase; +import org.hibernate.Session; +import org.hibernate.Transaction; + +/** + * @author Emmanuel Bernard + */ +public class GeneratedTest extends TestCase { + + public void testGenerated() throws Exception { + Session s = openSession(); + Transaction tx = s.beginTransaction(); + Antenna antenna = new Antenna(); + antenna.id = new Integer(1); + s.persist( antenna ); + assertNull( antenna.latitude ); + assertNull( antenna.longitude ); + tx.rollback(); + s.close(); + } + + protected Class[] getMappings() { + return new Class[] { + Antenna.class + }; + } +} |