diff --git a/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml b/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml index 20ae700f9f..3fd1eda171 100644 --- a/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml +++ b/documentation/manual/src/main/docbook/en-US/content/basic_mapping.xml @@ -429,13 +429,6 @@ public class Flight implements Serializable { @org.hibernate.annotations.Entity: - - mutable (defaults to true): Immutable - classes, mutable=false, cannot be updated or - deleted by the application. This allows Hibernate to make some minor - performance optimizations. - - dynamicInsert / dynamicUpdate (defaults to false): specifies that @@ -535,6 +528,11 @@ public class Flight implements Serializable { accident. + Some entities are not mutable. They cannot be updated or deleted + by the application. This allows Hibernate to make some minor performance + optimizations.. Use the @Immutable + annotation. + You can also alter how Hibernate deals with lazy initialization for this class. On @Proxy, use lazy=false to disable lazy fetching (not @@ -548,7 +546,7 @@ public class Flight implements Serializable { @BatchSize specifies a "batch size" for fetching instances of this class by identifier. Not yet loaded instances - are loaded batch-size at a time (default 1). + are loaded batch-size at a time (default 1). You can specific an arbitrary SQL WHERE condition to be used when retrieving objects of this class. Use @Where for @@ -828,13 +826,24 @@ public class Summary {
- id + identifiers Mapped classes must declare the primary key column of the database table. Most classes will also have a - JavaBeans-style property holding the unique identifier of an instance. - The <id> element defines the mapping from that - property to the primary key column. + JavaBeans-style property holding the unique identifier of an + instance. + + Mark the identifier property with + @Id. + + @Entity +public class Person { + @Id Integer getId() { ... } + ... +} + + In hbm.xml, use the <id> element which + defines the mapping from that property to the primary key column. @@ -896,11 +905,446 @@ public class Summary { that the class has no identifier property. The unsaved-value attribute is almost never - needed in Hibernate3. + needed in Hibernate3 and indeed has no corresponding element in + annotations. - There is an alternative <composite-id> - declaration that allows access to legacy data with composite keys. Its - use is strongly discouraged for anything else. + You can also declare the identifier as a composite identifier. + This allows access to legacy data with composite keys. Its use is + strongly discouraged for anything else. + +
+ Composite identifier + + You can define a composite primary key through several + syntaxes: + + + + 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. + + + + 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. + + + + 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). + + + + 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). + + Let's explore all three cases using examples. + +
+ Property using a component type + + Here is a simple example of + @EmbeddedId. + + @Entity +class User { + @EmbeddedId + @AttributeOverride(name="firstName", column=@Column(name="fld_firstname") + UserId id; + + Integer age; +} + +@Embeddable +class UserId implements Serializable { + String firstName; + String lastName; +} + + You can notice that the UserId class is + serializable. To override the column mapping, use + @AttributeOverride. + + An embedded id can itself contains the primary key of an + associated entity. + + @Entity +class Customer { + @EmbeddedId CustomerId id; + boolean preferredCustomer; + + @MapsId("userId") + @JoinColumns({ + @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"), + @JoinColumn(name="userlastname_fk", referencedColumnName="lastName") + }) + @OneToOne User user; +} + +@Embeddable +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; +} + + Let's now rewrite these examples using the hbm.xml syntax. + + + <composite-id + name="propertyName" + class="ClassName" + mapped="true|false" + access="field|property|ClassName"> + node="element-name|." + + <key-property name="propertyName" type="typename" column="column_name"/> + <key-many-to-one name="propertyName class="ClassName" column="column_name"/> + ...... +</composite-id> + + First a simple example: + + <class name="User"> + <composite-id name="id" class="UserId"> + <key-property name="firstName" column="fld_firstname"/> + <key-property name="lastName"/> + </composite-id> +</class> + + Then an example showing how an association can be + mapped. + + <class name="Customer"> + <composite-id name="id" class="CustomerId"> + <key-property name="firstName" column="userfirstname_fk"/> + <key-property name="lastName" column="userfirstname_fk"/> + <key-property name="customerNumber"/> + </composite-id> + + <property name="preferredCustomer"/> + + <many-to-one name="user"> + <column name="userfirstname_fk" updatable="false" insertable="false"/> + <column name="userlastname_fk" updatable="false" insertable="false"/> + </many-to-one> +</class> + +<class name="User"> + <composite-id name="id" class="UserId"> + <key-property name="firstName"/> + <key-property name="lastName"/> + </composite-id> + + <property name="age"/> +</class> + + Notice a few things in the previous example: + + + + the order of the properties (and column) matters. It must + be the same between the association and the primary key of the + associated entity + + + + the many to one uses the same columns as the primary key + and thus must be marked as read only + (insertable and updatable + to false). + + + + unlike with @MapsId, the id value + of the associated entity is not transparently copied, check the + foreign id generator for more + information. + + + + The last example shows how to map association directly in the + embedded id component. + + <class name="Customer"> + <composite-id name="id" class="CustomerId"> + <key-many-to-one name="user"> + <column name="userfirstname_fk"/> + <column name="userlastname_fk"/> + </key-many-to-one> + <key-property name="customerNumber"/> + </composite-id> + + <property name="preferredCustomer"/> + + <many-to-one name="user"> + <column name="userfirstname_fk" updatable="false" insertable="false"/> + <column name="userlastname_fk" updatable="false" insertable="false"/> + </many-to-one> +</class> + +<class name="User"> + <composite-id name="id" class="UserId"> + <key-property name="firstName"/> + <key-property name="lastName"/> + </composite-id> + + <property name="age"/> +</class> +
+ +
+ Multiple @Id properties + + XXXXXXXXXXXXXXXXXXXXXXXXXXXXX + + 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. +
+
Generator