From cd3db638aa12bd89d1ea3255539e73388dfd01a9 Mon Sep 17 00:00:00 2001 From: Emmanuel Bernard Date: Tue, 23 Feb 2010 19:12:49 +0000 Subject: [PATCH] HHH-4933 Write documentation on composite identity, partial generated value and derived identity git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18860 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- .../src/main/docbook/en/modules/entity.xml | 947 +++++++++++------- .../src/main/docbook/en/modules/setup.xml | 4 +- .../e5/c/ForeignGeneratorViaMapsIdTest.java | 38 + .../e5/c/MedicalHistory.java | 25 + .../derivedidentities/e5/c/Person.java | 14 + 5 files changed, 651 insertions(+), 377 deletions(-) create mode 100644 annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/ForeignGeneratorViaMapsIdTest.java create mode 100644 annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/MedicalHistory.java create mode 100644 annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/Person.java diff --git a/annotations/src/main/docbook/en/modules/entity.xml b/annotations/src/main/docbook/en/modules/entity.xml index 735a83035f..f1025e11cc 100644 --- a/annotations/src/main/docbook/en/modules/entity.xml +++ b/annotations/src/main/docbook/en/modules/entity.xml @@ -273,9 +273,109 @@ public byte[] getFullCode() {
- + Access type - + By default the access type of a class hierarchy is defined by + the position of the @Id or + @EmbeddedId annotations. If these annotations + are on a field, then only fields are considered for persistence and + the state is accessed via the field. If there annotations are on a + getter, then only the getters are considered for persistence and the + state is accessed via the getter/setter. That works well in practice + and is the recommended approach. + The placement of annotations within a class hierarchy has to + be consistent (either field or on property) to be able to + determine the default access type. It is recommended to stick to + one single annotation placement strategy throughout your whole + application. + + + However in some situations, you need to: + + + + force the access type of the entity hierarchy + + + + override the access type of a specific entity in the class + hierarchy + + + + override the access type of an embeddable type + + + + The best use case is an embeddable class used by several + entities that might not use the same access type. In this case it is + better to force the access type at the embeddable class level. + + To force the access type on a given class, use the + @Access annotation as showed below: + + @Entity +public class Order { + @Id private Long id; + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + @Embedded private Address address; + public Address getAddress() { return address; } + public void setAddress() { this.address = address; } +} + +@Entity +public class User { + private Long id; + @Id public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + private Address address; + @Embedded public Address getAddress() { return address; } + public void setAddress() { this.address = address; } +} + +@Embeddable +@Access(AcessType.PROPERTY) +public class Address { + private String street1; + public String getStreet1() { return street1; } + public void setStreet1() { this.street1 = street1; } + + private hashCode; //not persistent +} + + You can also override the access type of a single property while + keeping the other properties standard. + + @Entity +public class Order { + @Id private Long id; + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + @Transient private String userId; + @Transient private String orderId; + + @Access(AccessType.PROPERTY) + public String getOrderNumber() { return userId + ":" + orderId; } + public void setOrderNumber() { this.userId = ...; this.orderId = ...; } +} + + In this example, the default access type is + FIELD except for the + orderNumber property. Note that the corresponding + field, if any must be marked as @Transient or + transient. + + + @org.hibernate.annotations.AccessType + + The annotation + @org.hibernate.annotations.AccessType should + be considered deprecated for FIELD and PROPERTY access. It is still + useful however if you need to use a custom access type. +
@@ -307,12 +407,6 @@ public byte[] getFullCode() { annotated with @Temporal - - - annotated with - @org.hibernate.annotations.CollectionOfElements - (for Hibernate only) - @@ -435,8 +529,7 @@ public String getName() { ... } @Embedded and @AttributeOverride annotation in the associated property: - -@Entity + @Entity public class Person implements Serializable { // Persistent component using defaults @@ -449,19 +542,15 @@ public class Person implements Serializable { } ) Country bornIn; ... -} - +} - -@Embeddable + @Embeddable public class Address implements Serializable { String city; Country nationality; //no overriding here -} - +} - -@Embeddable + @Embeddable public class Country implements Serializable { private String iso2; @Column(name="countryName") private String name; @@ -473,13 +562,11 @@ public class Country implements Serializable { public String getName() { return name; } public void setName(String name) { this.name = name; } ... -} - +} An embeddable object inherits the access type of its owning - entity (note that you can override that using - @Access or the Hibernate specific - @AccessType annotation. + entity (note that you can override that using the + @Access annotation). The Person entity bean has two component properties, homeAddress and @@ -492,10 +579,9 @@ public class Country implements Serializable { annotations for each mapped attribute of Country. As you can see, Country is also a nested component of Address, - again using auto-detection by Hibernate and EJB3 defaults. Overriding - columns of embedded objects of embedded objects is currently not - supported in the EJB3 spec, however, Hibernate Annotations supports it - through dotted expressions. + again using auto-detection by Hibernate and JPA defaults. Overriding + columns of embedded objects of embedded objects is through dotted + expressions. @Embedded @AttributeOverrides( { @@ -504,27 +590,28 @@ public class Country implements Serializable { @AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") ) //nationality columns in homeAddress are overridden } ) - Address homeAddress;Hibernate Annotations supports one - more feature that is not explicitly supported by the EJB3 - specification. You can annotate a embedded object with the + Address homeAddress;Hibernate Annotations supports + something that is not explicitly supported by the JPA specification. + You can annotate a embedded object with the @MappedSuperclass annotation to make the superclass properties persistent (see @MappedSuperclass for more informations). - While not supported by the EJB3 specification, Hibernate - Annotations allows you to use association annotations in an embeddable - object (ie @*ToOne nor - @*ToMany). To override the association columns you - can use @AssociationOverride. + You can also use association annotations in an embeddable object + (ie @OneToOne, @ManyToOne, + @OneToMany or @ManyToMany). + To override the association columns you can use + @AssociationOverride. If you want to have the same embeddable object type twice in the - same entity, the column name defaulting will not work: at least one of - the columns will have to be explicit. Hibernate goes beyond the EJB3 - spec and allows you to enhance the defaulting mechanism through the - NamingStrategy. - DefaultComponentSafeNamingStrategy is a small - improvement over the default EJB3NamingStrategy that allows embedded - objects to be defaulted even if used twice in the same entity. + same entity, the column name defaulting will not work as several + embedded objects would share the same set of columns. In plain JPA, + you need to override at least one set of columns. Hibernate, however, + allows you to enhance the default naming mechanism through the + NamingStrategy interface. You can write a + strategy that prevent name clashing in such a situation. + DefaultComponentSafeNamingStrategy is an + example of this.
@@ -545,15 +632,18 @@ public class Country implements Serializable { - Otherwise, if the type of the property is Serializable, it - is mapped as @Basic in a column holding the object in its - serialized version + Otherwise, if the type of the property is + Serializable, it is mapped as + @Basic in a column holding the object in + its serialized version - Otherwise, if the type of the property is java.sql.Clob or - java.sql.Blob, it is mapped as @Lob with the appropriate - LobType + Otherwise, if the type of the property is + java.sql.Clob or + java.sql.Blob, it is mapped as + @Lob with the appropriate + LobType
@@ -567,58 +657,65 @@ public class Country implements Serializable { property is the identifier of your entity bean. This property can be set by the application itself or be generated by Hibernate (preferred). You can define the identifier generation strategy thanks to the - @GeneratedValue annotation: + @GeneratedValue annotation. - - - AUTO - either identity column, sequence or table depending on - the underlying DB - +
+ Generating the identifier property - - TABLE - table holding the id - + JPA defines five types of identifier generation + strategies: - - IDENTITY - identity column - + + + AUTO - either identity column, sequence or table depending + on the underlying DB + - - SEQUENCE - sequence - - + + TABLE - table holding the id + - Hibernate provides more id generators than the basic EJB3 ones. - Check for more informations. + + IDENTITY - identity column + - The following example shows a sequence generator using the - SEQ_STORE configuration (see below) + + SEQUENCE - sequence + - -@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") -public Integer getId() { ... } - + + identity copy - the identity is copied from another + entity + + - The next example uses the identity generator: + Hibernate provides more id generators than the basic JPA ones. + Check for more informations. - -@Id @GeneratedValue(strategy=GenerationType.IDENTITY) -public Long getId() { ... } - + The following example shows a sequence generator using the + SEQ_STORE configuration (see below) - The AUTO generator is the preferred type for - portable applications (across several DB vendors). The identifier - generation configuration can be shared for several - @Id mappings with the generator attribute. There are - several configurations available through - @SequenceGenerator and - @TableGenerator. The scope of a generator can be the - application or the class. Class-defined generators are not visible - outside the class and can override application level generators. - Application level generators are defined at XML level (see ): + @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") +public Integer getId() { ... } - <table-generator name="EMP_GEN" + The next example uses the identity generator: + + @Id @GeneratedValue(strategy=GenerationType.IDENTITY) +public Long getId() { ... } + + The AUTO generator is the preferred type for + portable applications (across several DB vendors). The identifier + generation configuration can be shared for several + @Id mappings with the generator attribute. There + are several configurations available through + @SequenceGenerator and + @TableGenerator. The scope of a generator can be + the application or the class. Class-defined generators are not visible + outside the class and can override application level generators. + Application level generators are defined at XML level (see ): + + <table-generator name="EMP_GEN" table="GENERATOR_TABLE" pk-column-name="key" value-column-name="hi" @@ -649,37 +746,47 @@ public Long getId() { ... } ) - If JPA XML (like META-INF/orm.xml) is used to - define thegenerators, EMP_GEN and - SEQ_GEN are application level generators. - EMP_GEN defines a table based id generator using the - hilo algorithm with a max_lo of 20. The hi value is - kept in a table "GENERATOR_TABLE". - The information is kept in a row where pkColumnName - "key" is equals to pkColumnValue - "EMP" and column valueColumnName - "hi" contains the the next high value used. + If JPA XML (like META-INF/orm.xml) is used + to define thegenerators, EMP_GEN and + SEQ_GEN are application level generators. + EMP_GEN defines a table based id generator using + the hilo algorithm with a max_lo of 20. The hi + value is kept in a table + "GENERATOR_TABLE". The information is kept in a row + where pkColumnName "key" is equals to + pkColumnValue "EMP" and column + valueColumnName "hi" contains + the the next high value used. - SEQ_GEN defines a sequence generator using a - sequence named my_sequence. The allocation size used - for this sequence based hilo algorithm is 20. Note that this version of - Hibernate Annotations does not handle initialValue in - the sequence generator. The default allocation size is 50, so if you - want to use a sequence and pickup the value each time, you must set the - allocation size to 1. + SEQ_GEN defines a sequence generator using a + sequence named my_sequence. The allocation size + used for this sequence based hilo algorithm is 20. Note that this + version of Hibernate Annotations does not handle + initialValue in the sequence generator. The default + allocation size is 50, so if you want to use a sequence and pickup the + value each time, you must set the allocation size to 1. - - Package level definition is no longer supported by the EJB 3.0 - specification. However, you can use the - @GenericGenerator at the package level (see ). - + + We recommend all new projects to use + hibernate.id.new_generator_mappings=true as the new + generators are more efficient and closer to the JPA 2 specification + semantic. However they are not backward compatible with existing + databases (if a sequence or a table is used for id generation). See + for more information on how + to activate them. + - The next example shows the definition of a sequence generator in a - class scope: + + Package level definition is not supported by the JPA + specification. However, you can use the + @GenericGenerator at the package level (see ). + - -@Entity + The next example shows the definition of a sequence generator in + a class scope: + + @Entity @javax.persistence.SequenceGenerator( name="SEQ_STORE", sequenceName="my_sequence" @@ -689,120 +796,378 @@ public class Store implements Serializable { @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") public Long getId() { return id; } +} + + This class will use a sequence named my_sequence and the + SEQ_STORE generator is not visible in other classes. Note that you can + check the Hibernate Annotations tests in the + org.hibernate.test.annotations.id package for more + examples. + + Finally, you can ask Hibernate to copy the identifier from + another associated entity. In the Hibernate jargon, it is known as a + foreign generator but the JPA mapping reads better and is + encouraged. + + @Entity +class MedicalHistory implements Serializable { + @Id @OneToOne + @JoinColumn(name = "person_id") + Person patient; } - - This class will use a sequence named my_sequence and the SEQ_STORE - generator is not visible in other classes. Note that you can check the - Hibernate Annotations tests in the - org.hibernate.test.annotations.id package for more - examples. +@Entity +public class Person implements Serializable { + @Id @GeneratedValue Integer id; +} - You can define a composite primary key through several - syntaxes: + Or alternatively - - - annotate the component property as @Id and make the component - class @Embeddable - + @Entity +class MedicalHistory implements Serializable { + @Id Integer id; - - annotate the component property as @EmbeddedId - + @MapsId @OneToOne + @JoinColumn(name = "patient_id") + Person patient; +} - - annotate the class as @IdClass and annotate each property of - the entity involved in the primary key with @Id - - +@Entity +class Person { + @Id @GeneratedValue Integer id; +} - While quite common to the EJB2 developer, - @IdClass is likely new for Hibernate users. The - composite primary key class corresponds to multiple fields or properties - of the entity class, and the names of primary key fields or properties - in the primary key class and those of the entity class must match and - their types must be the same. Let's look at an example: + But an identifier does not have to be a single property, it ca + be composed of several properties. +
- @Entity -@IdClass(FootballerPk.class) -public class Footballer { - //part of the id key - @Id public String getFirstname() { - return firstname; - } +
+ Composite identifier - public void setFirstname(String firstname) { - this.firstname = firstname; - } + You can define a composite primary key through several + syntaxes: - //part of the id key - @Id public String getLastname() { - return lastname; - } + + + use a component type to represent the identifier and map it + as a property in the entity: you then annotated the property as + @EmbeddedId. The component type has to be + Serializable. + - public void setLastname(String lastname) { - this.lastname = lastname; - } + + map multiple properties as @Id + properties: the identifier type is then the entity class itself + and needs to be Serializable. This approach + is unfortunately not standard and only supported by + Hibernate. + - public String getClub() { - return club; - } + + map multiple properties as @Id + properties and declare an external class to be the identifier + type. This class, which needs to be + Serializable, is declared on the entity via + the @IdClass annotation. The identifier + type must contain the same properties as the identifier properties + of the entity: each property name must be the same, its type must + be the same as well if the entity property is of a basic type, its + type must be the type of the primary key of the associated entity + if the entity property is an association (either a + @OneToOne or a + @ManyToOne). + + - public void setClub(String club) { - this.club = club; - } + As you can see the last case is far from obvious. It has been + inherited from the dark ages of EJB 2 for backward compatibilities and + we recommend you not to use it (for simplicity sake). - //appropriate equals() and hashCode() implementation + Let's explore all three cases using examples. + +
+ @EmbeddedId property + + Here is a simple example of + @EmbeddedId. + + @Entity +class User { + @EmbeddedId + @AttributeOverride(name="firstName", column=@Column(name="fld_firstname") + UserId id; + + Integer age; } @Embeddable -public class FootballerPk implements Serializable { - //same name and type as in Footballer - public String getFirstname() { - return firstname; - } +class UserId implements Serializable { + String firstName; + String lastName; +} - public void setFirstname(String firstname) { - this.firstname = firstname; - } + You can notice that the UserId class is + serializable. To override the column mapping, use + @AttributeOverride. - //same name and type as in Footballer - public String getLastname() { - return lastname; - } + An embedded id can itself contains the primary key of an + associated entity. - public void setLastname(String lastname) { - this.lastname = lastname; - } + @Entity +class Customer { + @EmbeddedId CustomerId id; + boolean preferredCustomer; - //appropriate equals() and hashCode() implementation -} - - - As you may have seen, @IdClass points to the - corresponding primary key class. - - While not supported by the EJB3 specification, Hibernate allows - you to define associations inside a composite identifier. Simply use the - regular annotations for that - - @Entity -@AssociationOverride( name="id.channel", joinColumns = @JoinColumn(name="chan_id") ) -public class TvMagazin { - @EmbeddedId public TvMagazinPk id; - @Temporal(TemporalType.TIME) Date time; + @MapsId("userId") + @JoinColumns({ + @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"), + @JoinColumn(name="userlastname_fk", referencedColumnName="lastName") + }) + @OneToOne User user; } @Embeddable -public class TvMagazinPk implements Serializable { - @ManyToOne - public Channel channel; - public String name; - @ManyToOne - public Presenter presenter; +class CustomerId implements Serializable { + UserId userId; + String customerNumber; } - + +@Entity +class User { + @EmbeddedId UserId id; + Integer age; +} + +@Embeddable +class UserId implements Serializable { + String firstName; + String lastName; +} + + In the embedded id object, the association is represented as + the identifier of the associated entity. But you can link its value + to a regular association in the entity via the + @MapsId annotation. The + @MapsId value correspond to the property name + of the embedded id object containing the associated entity's + identifier. In the database, it means that the + Customer.user and the + CustomerId.userId properties share the same + underlying column (user_fk in this case). + + In practice, your code only sets the + Customer.user property and the user id value is + copied by Hibernate into the CustomerId.userId + property. + + + The id value can be copied as late as flush time, don't rely + on it until after flush time. + + + While not supported in JPA, Hibernate lets you place your + association directly in the embedded id component (instead of having + to use the @MapsId annotation). + + @Entity +class Customer { + @EmbeddedId CustomerId id; + boolean preferredCustomer; +} + +@Embeddable +class CustomerId implements Serializable { + @OneToOne + @JoinColumns({ + @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"), + @JoinColumn(name="userlastname_fk", referencedColumnName="lastName") + }) + User user; + String customerNumber; +} + +@Entity +class User { + @EmbeddedId UserId id; + Integer age; +} + +@Embeddable +class UserId implements Serializable { + String firstName; + String lastName; +} +
+ +
+ Multiple @Id properties + + Another arguably more natural) approach is to place + @Id on multiple properties of my entity. This + approach is only supported by Hibernate but does not require an + extra embeddable component. + + @Entity +class Customer implements Serializable { + @Id @OneToOne + @JoinColumns({ + @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"), + @JoinColumn(name="userlastname_fk", referencedColumnName="lastName") + }) + User user; + + @Id String customerNumber; + + boolean preferredCustomer; +} + +@Entity +class User { + @EmbeddedId UserId id; + Integer age; +} + +@Embeddable +class UserId implements Serializable { + String firstName; + String lastName; +} + + In this case Customer being it's own + identifier representation, it must implement + Serializable. +
+ +
+ @IdClass + + @IdClass on an entity points to the + class (component) representing the identifier of the class. The + properties marked @Id on the entity must have + their corresponding property on the @IdClass. + The return type of search twin property must be either identical for + basic properties or must correspond to the identifier class of the + associated entity for an association. + + + This approach is inherited from the EJB 2 days and we + recommend against its use. But, after all it's your application + and Hibernate supports it. + + + @Entity +class Customer { + @Id @OneToOne + @JoinColumns({ + @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"), + @JoinColumn(name="userlastname_fk", referencedColumnName="lastName") + }) + User user; + + @Id String customerNumber; + + boolean preferredCustomer; +} + +class CustomerId implements Serializable { + UserId user; + String customerNumber; +} + +@Entity +class User { + @EmbeddedId UserId id; + Integer age; +} + +@Embeddable +class UserId implements Serializable { + String firstName; + String lastName; +} + + Customer and + CustomerId do have the same properties + customerNumber as well as + user. + + While not JPA standard, Hibernate let's you declare the + vanilla associated property in the + @IdClass. + + @Entity +class Customer { + @Id @OneToOne + @JoinColumns({ + @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"), + @JoinColumn(name="userlastname_fk", referencedColumnName="lastName") + }) + User user; + + @Id String customerNumber; + + boolean preferredCustomer; +} + +class CustomerId implements Serializable { + @OneToOne User user; + String customerNumber; +} + +@Entity +class User { + @EmbeddedId UserId id; + Integer age; +} + +@Embeddable +class UserId implements Serializable { + String firstName; + String lastName; +} +
+ +
+ Partial identifier generation + + Hibernate supports the automatic generation of some of the + identifier properties. Simply use the + @GeneratedValue annotation on one or several + id properties. + + + The Hibernate team has always felt such a construct as + fundamentally wrong. Try hard to fix your data model before using + this feature. + + + @Entity +public class CustomerInventory implements Serializable { + @Id + @TableGenerator(name = "inventory", + table = "U_SEQUENCES", + pkColumnName = "S_ID", + valueColumnName = "S_NEXTNUM", + pkColumnValue = "inventory", + allocationSize = 1000) + @GeneratedValue(strategy = GenerationType.TABLE, generator = "inventory") + Integer id; + + + @Id @ManyToOne(cascade = CascadeType.MERGE) + Customer customer; +} + +@Entity +public class Customer implements Serializable { + @Id + private int id; +} + + You can also generate properties inside an + @EmbeddedId class. +
+
@@ -965,8 +1330,7 @@ public class BaseEntity { The default access type (field or methods) is used, unless you - use the @Access or - @AccessType + use the @Access annotation. @@ -1830,8 +2194,7 @@ public class RegionalArticlePk implements Serializable { ... } @Embeddable inherit the access type of its - owning entity unless @Access or the Hibernate - specific annotation @AccessType is used. Composite + owning entity unless @Access is used. Composite foreign keys (if not using the default sensitive values) are defined on associations using the @JoinColumns element, which is basically an array of @JoinColumn. It is considered a @@ -2562,172 +2925,6 @@ List results = s.createCriteria( Citizen.class )
Property -
- Access type - - The default access type is determined from the position of the - @Id or @EmbeddedId annotation in - the entity hierarchy. - - - The placement of annotations within a class hierarchy has to - be consistent (either field or on property) to be able to determine - the default access type. It is recommended to stick to one single - annotation placement strategy throughout your whole - application. - - - To finetune the access strategy JPA 2 introduces the @Access - annotation. With its help you can define the access type on: - - - - an entity - - - - a superclass - - - - an embeddable object - - - - a property - - - - Prior to JPA 2 Hibernate used the Hibernate specific annotation - @org.hibernate.annotations.AccessType to change - specific access types. @AccessType still exists to support legacy - systems, but the usage of @Access is recommended for new applications. - The behaviour of @Access and @AccessType are similar, but there are - differences. For both annotations applies: - - - - The access type is overridden for the annotated element, if - overridden on a class, all the properties of the given class - inherit the access type. - - - - If an entity is marked as - @Access(AccessType.PROPERTY) or - @AccessType("property") respectively, the - getters are scanned for annotations, if the enitiy is marked as - @Access(AccessType.FIELD) or - @AccessType("field") respectively, the fields - are scanned for annotations. - - - - In the case where you want to override the access type for a - property of an entity which already defines an explicit access type - the annotation placement between @Access and @AccessType - differs: - - @AccessType("property") // set access type for all properties in Country -public class Country implements Serializable { - private String iso2; - private String name; - - public String getIso2() { - return iso2; - } - - public void setIso2(String iso2) { - this.iso2 = iso2; - } - - @Column(name = "countryName") - @AccessType("field") // set the access type for name to field - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} - - - @Access(AccessType.PROPERTY) // set access type for all properties in Country -public class Country implements Serializable { - private String iso2; - - @Access(AccessType.FIELD) // set the access type for name to field - private String name; - - public String getIso2() { - return iso2; - } - - public void setIso2(String iso2) { - this.iso2 = iso2; - } - - @Column(name = "countryName") - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} - - - - Watch out for the different annotation placement strategy. In - the case of @Access the field has to be annotated - with the overriding access strategy whereas with - @AccessType the property gets annotated. - - - If a superclass or an embeddable object is not annotated, the - default entity access type is used. - - @Entity -public class Person implements Serializable { - @Id @GeneratedValue //access type field - Integer id; - - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "iso2", column = @Column(name = "bornIso2")), - @AttributeOverride(name = "name", column = @Column(name = "bornCountryName")) - }) - Country bornIn; -} - -@Embeddable -@AccessType("property") //override access type for all properties in Country -public class Country implements Serializable { - private String iso2; - private String name; - - public String getIso2() { - return iso2; - } - - public void setIso2(String iso2) { - this.iso2 = iso2; - } - - @Column(name = "countryName") - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} - -
-
Formula diff --git a/annotations/src/main/docbook/en/modules/setup.xml b/annotations/src/main/docbook/en/modules/setup.xml index 8f89dce815..3f05fff044 100644 --- a/annotations/src/main/docbook/en/modules/setup.xml +++ b/annotations/src/main/docbook/en/modules/setup.xml @@ -204,7 +204,7 @@ private static final SessionFactory sessionFactory; annotations, except for this startup routine change or in the configuration file. You can use your favorite configuration method for other properties ( hibernate.properties, - hibernate.cfg.xml, programmatic APIs, etc). + hibernate.cfg.xml, programmatic APIs, etc). You can mix annotated persistent classes and classic @@ -225,7 +225,7 @@ private static final SessionFactory sessionFactory; conflict occurs.
-
+
Properties On top of the Hibernate Core properties, Hibernate Annotations diff --git a/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/ForeignGeneratorViaMapsIdTest.java b/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/ForeignGeneratorViaMapsIdTest.java new file mode 100644 index 0000000000..b7b9d12225 --- /dev/null +++ b/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/ForeignGeneratorViaMapsIdTest.java @@ -0,0 +1,38 @@ +package org.hibernate.test.annotations.derivedidentities.e5.c; + +import org.hibernate.Session; +import org.hibernate.test.annotations.TestCase; +import org.hibernate.test.util.SchemaUtil; + +/** + * @author Emmanuel Bernard + */ +public class ForeignGeneratorViaMapsIdTest extends TestCase { + + public void testForeignGenerator() throws Exception { + assertTrue( SchemaUtil.isColumnPresent( "MedicalHistory", "patient_id", getCfg() ) ); + Person e = new Person(); + Session s = openSession( ); + s.getTransaction().begin(); + s.persist( e ); + MedicalHistory d = new MedicalHistory(); + d.patient = e; + s.persist( d ); + s.flush(); + s.clear(); + d = (MedicalHistory) s.get( MedicalHistory.class, e.id); + assertEquals( e.id, d.id ); + s.delete( d ); + s.delete( d.patient ); + s.getTransaction().rollback(); + s.close(); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + MedicalHistory.class, + Person.class + }; + } +} diff --git a/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/MedicalHistory.java b/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/MedicalHistory.java new file mode 100644 index 0000000000..64a505656c --- /dev/null +++ b/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/MedicalHistory.java @@ -0,0 +1,25 @@ +package org.hibernate.test.annotations.derivedidentities.e5.c; + +import java.io.Serializable; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.MapsId; +import javax.persistence.OneToOne; + + +/** + * @author Emmanuel Bernard + */ +@Entity +public class MedicalHistory implements Serializable { + @Id + Integer id; + + @MapsId + @JoinColumn(name = "patient_id") + @OneToOne + Person patient; +} \ No newline at end of file diff --git a/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/Person.java b/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/Person.java new file mode 100644 index 0000000000..62ec0cf22d --- /dev/null +++ b/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e5/c/Person.java @@ -0,0 +1,14 @@ +package org.hibernate.test.annotations.derivedidentities.e5.c; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Emmanuel Bernard + */ +@Entity +public class Person { + @Id @GeneratedValue + Integer id; +} \ No newline at end of file