From hibernate-commits at lists.jboss.org Mon Mar 1 08:26:57 2010 Content-Type: multipart/mixed; boundary="===============0683741323612846987==" MIME-Version: 1.0 From: hibernate-commits at lists.jboss.org To: hibernate-commits at lists.jboss.org Subject: [hibernate-commits] Hibernate SVN: r18911 - in core/trunk/annotations/src/main: java/org/hibernate/annotations and 1 other directory. Date: Mon, 01 Mar 2010 08:26:57 -0500 Message-ID: <201003011326.o21DQv5w023073@svn01.web.mwc.hst.phx2.redhat.com> --===============0683741323612846987== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Author: epbernard Date: 2010-03-01 08:26:56 -0500 (Mon, 01 Mar 2010) New Revision: 18911 Modified: core/trunk/annotations/src/main/docbook/en/modules/entity.xml core/trunk/annotations/src/main/java/org/hibernate/annotations/MapKey.ja= va core/trunk/annotations/src/main/java/org/hibernate/annotations/MapKeyMan= yToMany.java Log: HHH-4933 Doc on Map and List support Modified: core/trunk/annotations/src/main/docbook/en/modules/entity.xml =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- core/trunk/annotations/src/main/docbook/en/modules/entity.xml 2010-03-0= 1 13:23:33 UTC (rev 18910) +++ core/trunk/annotations/src/main/docbook/en/modules/entity.xml 2010-03-0= 1 13:26:56 UTC (rev 18911) @@ -24,7 +24,7 @@ - Entity Beans + Mapping Entities =
Intro @@ -957,7 +957,7 @@ 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). + underlying column (user_fk in this case). = In practice, your code only sets the Customer.user property and the user id value = is @@ -966,7 +966,7 @@ = The id value can be copied as late as flush time, don't = rely - on it until after flush time. + on it until after flush time. = While not supported in JPA, Hibernate lets you place your @@ -1399,7 +1399,7 @@
=
- Mapping entity bean associations/relationships + Mapping entity associations/relationships =
One-to-one @@ -1493,7 +1493,7 @@ passport and the column id of Passport is id. = - The third possibility (using an association table) is very + The third possibility (using an association table) is quite exotic. = @@ -1536,8 +1536,7 @@ Many-to-one associations are declared at the property level = with the annotation @ManyToOne: = - -(a)Entity() + @Entity() public class Flight implements Serializable { @ManyToOne( cascade =3D {CascadeTyp= e.PERSIST, CascadeType.MERGE} ) @JoinColumn(name=3D"COMP_ID") @@ -1545,8 +1544,7 @@ return company; } ... -} - +} = The @JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the n= ame @@ -1563,8 +1561,7 @@ almost all cases. However this is useful when you want to use interfaces as the return type instead of the regular entity. = - -(a)Entity() + @Entity public class Flight implements Serializable { @ManyToOne( cascade =3D {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=3DCompanyImpl.class ) @@ -1577,9 +1574,9 @@ = public interface Company { ... - +} = - You can alse map a many to one association through an + You can also map a many-to-one association through an association table. This association table described by the @JoinTable annotation will contains a foreign k= ey referencing back the entity table (through @@ -1587,8 +1584,7 @@ referencing the target entity table (through @JoinTable.inverseJoinColumns). = - -(a)Entity() + @Entity public class Flight implements Serializable { @ManyToOne( cascade =3D {CascadeType.PERSIST, CascadeType.MERGE} ) @JoinTable(name=3D"Flight_Company", @@ -1599,8 +1595,7 @@ return company; } ... -} - +}
=
@@ -1611,37 +1606,267 @@ Overview = You can map Collection, - List (ie ordered lists, not indexed lists), - Map and Set. The EJB3 - specification describes how to map an ordered list (ie a list - ordered at load time) using - @javax.persistence.OrderBy annotation: this - annotation takes into parameter a list of comma separated (target - entity) properties to order the collection by (eg firstname - asc, age desc), if the string is empty, the collection wi= ll - be ordered by id. For true indexed collections, please refer to = the - . EJB3 allows you to map Maps= using - as a key one of the target entity property using - @MapKey(name=3D"myProperty") (myProperty is a - property name in the target entity). When using - @MapKey (without property name), the target - entity primary key is used. The map key uses the same column as = the - property pointed out: there is no additional column defined to h= old - the map key, and it does make sense since the map key actually - represent a target property. Be aware that once loaded, the key = is - no longer kept in sync with the property, in other words, if you - change the property value, the key will not change automatically= in - your Java model (for true map support please refers to ). Many people confuse - <map> capabilities and - @MapKey ones. These are two different feature= s. - @MapKey still has some limitations, please ch= eck - the forum or the JIRA tracking system for more informations. + List, Map and + Set pointing to associated entities as + one-to-many or many-to-many associations using the + @OneToMany or + @ManyToMany annotation respectively. If t= he + collection is of a basic type or of an embeddable type, use + @ElementCollection. We will describe that= in + more detail in the following subsections but let's first focus on + some semantic differences between the various collections. = - Hibernate has several notions of collections. + Lists can be mapped in two different ways: = - + + + as ordered lists, the order is not materialized in the + database + = + + as indexed lists, the order is materialized in the + database + + + + To order lists in memory, add + @javax.persistence.OrderBy to your property. = This + annotation takes into parameter a list of comma separated proper= ties + (of the target entity) and order the collection accordingly (eg + firstname asc, age desc), if the string is empty, t= he + collection will be ordered by the primary key of the target + entity. + + @Entity +public class Customer { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + @OneToMany(mappedBy=3D"customer") + @OrderBy("number") + public List<Order> getOrders() { return orders; } + public void setOrders(List<Order> orders) { this.orders =3D order= s; } + private List<Order> orders; +} + +(a)Entity +public class Order { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + public String getNumber() { return number; } + public void setNumber(String number) { this.number =3D number; } + private String number; + + @ManyToOne + public Customer getCustomer() { return customer; } + public void setCustomer(Customer customer) { this.customer =3D customer= ; } + private Customer number; +} + +-- Table schema +|-------------| |----------| +| Order | | Customer | +|-------------| |----------| +| id | | id | +| number | |----------| = +| customer_id | +|-------------| + + To store the index value in a dedicated column, use the + @javax.persistence.OrderColumn annotation= on + your property. This annotations describes the column name and + attributes of the column keeping the index value. This column is + hosted on the table containing the association foreign key. If t= he + column name is not specified, the default is the name of the + referencing property, followed by underscore, followed by + ORDER (in the following example, it would be + orders_ORDER). + + @Entity +public class Customer { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + @OneToMany(mappedBy=3D"customer") + @OrderColumn(name"orders_index") + public List<Order> getOrders() { return orders; } + public void setOrders(List<Order> orders) { this.orders =3D order= s; } + private List<Order> orders; +} + +(a)Entity +public class Order { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + public String getNumber() { return number; } + public void setNumber(String number) { this.number =3D number; } + private String number; + + @ManyToOne + public Customer getCustomer() { return customer; } + public void setCustomer(Customer customer) { this.customer =3D customer= ; } + private Customer number; +} + +-- Table schema +|--------------| |----------| +| Order | | Customer | +|--------------| |----------| +| id | | id | +| number | |----------| = +| customer_id | +| orders_index | +|--------------| + + + We recommend you to convert + @org.hibernate.annotations.IndexColumn + usages to @OrderColumn unless you are + making use of the base property. The base + property lets you define the index value of the first element = (aka + as base index). The usual value is 0 or + 1. The default is 0 like in Java. + + + Likewise, maps can borrow their keys from one of the + associated entity properties or have dedicated columns to store = an + explicit key. + + To use one of the target entity property as a key of the m= ap, + use @MapKey(name=3D"myProperty") + (myProperty is a property name in the target + entity). When using @MapKey (without property + name), the target entity primary key is used. The map key uses t= he + same column as the property pointed out: there is no additional + column defined to hold the map key, and it does make sense since= the + map key actually represent a target property. Be aware that once + loaded, the key is no longer kept in sync with the property, in + other words, if you change the property value, the key will not + change automatically in your Java model. + + @Entity +public class Customer { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + @OneToMany(mappedBy=3D"customer") + @MapKey(name"number") + public Map<String,Order> getOrders() { return orders; } + public void setOrders(Map<String,Order> order) { this.orders =3D = orders; } + private Map<String,Order> orders; +} + +(a)Entity +public class Order { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + public String getNumber() { return number; } + public void setNumber(String number) { this.number =3D number; } + private String number; + + @ManyToOne + public Customer getCustomer() { return customer; } + public void setCustomer(Customer customer) { this.customer =3D customer= ; } + private Customer number; +} + +-- Table schema +|-------------| |----------| +| Order | | Customer | +|-------------| |----------| +| id | | id | +| number | |----------| = +| customer_id | +|-------------| + + Otherwise, the map key is mapped to a dedicated column or + columns. To customize things, use one of the following + annotations: + + + + @MapKeyColumn if the map key is= a + basic type, if you don't specify the column name, the name of + the property followed by underscore followed by + KEY is used (for example + orders_KEY). + + + + @MapKeyEnumerated / + @MapKeyTemporal if the map key type is + respectively an enum or a Date. + + + + @MapKeyJoinColumn/@M= apKeyJoinColumns + if the map key type is another entity. + + + + @AttributeOverride/@= AttributeOverrides + when the map key is a embeddable object. Use + key. as a prefix for your embeddable obje= ct + property names. + + + + You can also use @MapKeyClass to de= fine + the type of the key if you don't use generics (at this stage, you + should wonder why at this day and age you don't use + generics). + + @Entity +public class Customer { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + @OneToMany @JoinTable(name=3D"Cust_Order") + @MapKeyColumn(name"orders_number") + public Map<String,Order> getOrders() { return orders; } + public void setOrders(Map<String,Order> orders) { this.orders =3D= orders; } + private Map<String,Order> orders; +} + +(a)Entity +public class Order { + @Id @GeneratedValue public Integer getId() { return id; } + public void setId(Integer id) { this.id =3D id; } + private Integer id; + + public String getNumber() { return number; } + public void setNumber(String number) { this.number =3D number; } + private String number; + + @ManyToOne + public Customer getCustomer() { return customer; } + public void setCustomer(Customer customer) { this.customer =3D customer= ; } + private Customer number; +} + +-- Table schema +|-------------| |----------| |---------------| +| Order | | Customer | | Cust_Order | +|-------------| |----------| |---------------| +| id | | id | | customer_id | +| number | |----------| | order_id | +| customer_id | | orders_number | +|-------------| |---------------| + + Let's now explore the various collection semantics based on + the mapping you are choosing. + Collections semantics = @@ -1668,18 +1893,18 @@ = java.util.List, java.util.Collection = - @org.hibernate.annotations.CollectionOfElements or - @OneToMany or @ManyToMany + @ElementCollection or @OneToMany or + @ManyToMany = - Bag semantic with primary key (withtout the + Bag semantic with primary key (without the limitations of Bag semantic) = java.util.List, java.util.Collection = - (@org.hibernate.annotations.CollectionOfElements = or - @OneToMany or @ManyToMany) and @CollectionId + (@ElementCollection or @OneToMany or @ManyToMany)= and + @CollectionId = @@ -1687,9 +1912,9 @@ = java.util.List = - (@org.hibernate.annotations.CollectionOfElements = or - @OneToMany or @ManyToMany) and - @org.hibernate.annotations.IndexColumn + (@ElementCollection or @OneToMany or @ManyToMany)= and + (@OrderColumn or + @org.hibernate.annotations.IndexColumn) = @@ -1697,8 +1922,8 @@ = java.util.Set = - @org.hibernate.annotations.CollectionOfElements or - @OneToMany or @ManyToMany + @ElementCollection or @OneToMany or + @ManyToMany = @@ -1706,75 +1931,20 @@ = java.util.Map = - (@org.hibernate.annotations.CollectionOfElements = or - @OneToMany or @ManyToMany) and (nothing or - @org.hibernate.annotations.MapKey/MapKeyManyToMany for t= rue - map support, OR @javax.persistence.MapKey + (@ElementCollection or @OneToMany or @ManyToMany)= and + ((nothing or @MapKeyJoinColumn/@MapKeyColumn for true map + support) OR @javax.persistence.MapKey)
= - So specifically, java.util.List collections without - @org.hibernate.annotations.IndexColumn are going to be considere= d as + Specifically, java.util.List collections without + @OrderColumn or @IndexColumn are going to be considered as bags. = - Collection of primitive, core type or embedded objects is = not - supported by the EJB3 specification. Hibernate Annotations allows - them however (see ). - - @Entity public class City { - @OneToMany(mappedBy=3D"city") - @OrderBy("streetName") - public List<Street> getStreets() { - return streets; - } -... -} - -(a)Entity public class Street { - public String getStreetName() { - return streetName; - } - - @ManyToOne - public City getCity() { - return city; - } - ... -} - - -(a)Entity -public class Software { - @OneToMany(mappedBy=3D"software") - @MapKey(name=3D"codeName") - public Map<String, Version> getVersions() { - return versions; - } -... -} - -(a)Entity -(a)Table(name=3D"tbl_version") -public class Version { - public String getCodeName() {...} - - @ManyToOne - public Software getSoftware() { ... } -... -} - - So City has a collection of - Streets that are ordered by - streetName (of Street) when - the collection is loaded. Software has a map = of - Versions which key is the - Version codeName. - - Unless the collection is a generic, you will have to define - targetEntity. This is a annotation attribute = that - take the target entity class as a value. + More support for collections are available via Hibernate + specific extensions (see ).
=
-
\ No newline at end of file + Modified: core/trunk/annotations/src/main/java/org/hibernate/annotations/Ma= pKey.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- core/trunk/annotations/src/main/java/org/hibernate/annotations/MapKey.j= ava 2010-03-01 13:23:33 UTC (rev 18910) +++ core/trunk/annotations/src/main/java/org/hibernate/annotations/MapKey.j= ava 2010-03-01 13:26:56 UTC (rev 18911) @@ -32,13 +32,17 @@ = /** * Define the map key columns as an explicit column holding the map key - * This is completly different from {@link javax.persistence.MapKey} which= use an existing column + * This is completely different from {@link javax.persistence.MapKey} whic= h use an existing column * This annotation and {@link javax.persistence.MapKey} are mutually exclu= sive * + * @deprecated Use {@link javax.persistence.MapKeyColumn} + * This is the default behavior for Map properties marked as @= OneToMany, @ManyToMany + * or @ElementCollection * @author Emmanuel Bernard */ @Target({METHOD, FIELD}) @Retention(RUNTIME) +(a)Deprecated public @interface MapKey { Column[] columns() default {}; /** Modified: core/trunk/annotations/src/main/java/org/hibernate/annotations/Ma= pKeyManyToMany.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- core/trunk/annotations/src/main/java/org/hibernate/annotations/MapKeyMa= nyToMany.java 2010-03-01 13:23:33 UTC (rev 18910) +++ core/trunk/annotations/src/main/java/org/hibernate/annotations/MapKeyMa= nyToMany.java 2010-03-01 13:26:56 UTC (rev 18911) @@ -31,13 +31,17 @@ = /** * Define the map key columns as an explicit column holding the map key - * This is completly different from {@link javax.persistence.MapKey} which= use an existing column + * This is completely different from {@link javax.persistence.MapKey} whic= h use an existing column * This annotation and {@link javax.persistence.MapKey} are mutually exclu= sive * + * @deprecated Use {@link javax.persistence.MapKeyJoinColumn} {@link javax= .persistence.MapKeyJoinColumns} + * This is the default behavior for Map properties marked as @= OneToMany, @ManyToMany + * or @ElementCollection * @author Emmanuel Bernard */ @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) +(a)Deprecated public @interface MapKeyManyToMany { JoinColumn[] joinColumns() default {}; /** --===============0683741323612846987==--