diff --git a/documentation/src/main/docbook/integration/en-US/Hibernate_Integrations.xml b/documentation/src/main/docbook/integration/en-US/Hibernate_Integrations.xml
index c240514fa3..4c5a484af3 100644
--- a/documentation/src/main/docbook/integration/en-US/Hibernate_Integrations.xml
+++ b/documentation/src/main/docbook/integration/en-US/Hibernate_Integrations.xml
@@ -44,7 +44,11 @@
-
+
+
+
diff --git a/documentation/src/main/docbook/manual/en-US/chapters/portability/Portability.xml b/documentation/src/main/docbook/manual/en-US/chapters/portability/Portability.xml
index cf58b1abbc..38c8e458be 100644
--- a/documentation/src/main/docbook/manual/en-US/chapters/portability/Portability.xml
+++ b/documentation/src/main/docbook/manual/en-US/chapters/portability/Portability.xml
@@ -189,4 +189,8 @@
-->
+
+ JPA portability
+ * HQL/JPQL differences
+ * naming strategies
diff --git a/documentation/src/main/docbook/mapping/en-US/Hibernate_Mapping.xml b/documentation/src/main/docbook/mapping/en-US/Hibernate_Mapping.xml
index 7babd3a1b1..242ce43ed2 100644
--- a/documentation/src/main/docbook/mapping/en-US/Hibernate_Mapping.xml
+++ b/documentation/src/main/docbook/mapping/en-US/Hibernate_Mapping.xml
@@ -46,13 +46,20 @@
-
+
+
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/collection/Collection.xml b/documentation/src/main/docbook/mapping/en-US/chapters/collection/Collection.xml
new file mode 100644
index 0000000000..44d1e466d1
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/collection/Collection.xml
@@ -0,0 +1,111 @@
+
+
+
+ Collections
+
+
+ Collections as a value type
+
+ discussions of what it means for them to be value types - lifecycle, opt-locking
+
+
+ Collections have the usual behavior of value types. They are automatically persisted when referenced by
+ a persistent object and are automatically deleted when unreferenced. If a collection is passed from one
+ persistent object to another, its elements might be moved from one table to another. Two entities cannot
+ share a reference to the same collection instance.
+
+
+ Collection attributes do not support null value semantics; Hibernate does not distinguish between a null
+ collection reference and an empty collection.
+
+
+ It is important that collections be defined using the appropriate Java Collections Framework interface
+ rather than a specific implementation. From a theoretical perspective, this just follows good design
+ principles. From a practical perspective, Hibernate (really all persistence providers) will use
+ their own collection implementations which conform to the Java Collections Framework interfaces.
+
+
+
+
+ Collections of value types
+
+ collection of values - elements can be of any value type except for collections (in fact even compositions as the element cannot contain collections)
+ * basics
+ * compositions
+
+
+
+
+ Collections of entities
+
+ * one-to-many
+ * many-to-many
+
+
+
+
+ List - index
+
+
+ todo : discuss mapping list index
+
+
+
+
+ Map - key
+
+
+ todo : discuss mapping map key
+
+
+
+
+ Bags
+
+
+ todo : discuss mapping bags
+
+
+
+
+ Arrays
+
+
+ todo : discuss mapping arrays
+
+
+
+
+ Collections as basic value type
+
+ Notice how all the previous examples explicitly mark the collection attribute as either
+ ElementCollection, OneToMany or ManyToMany. Collections not marked as such, or collections explicitly
+ maked with @Basic are treated as JPA basic value. Meaning there value is stored into a single
+ column in the containing table.
+
+
+ This is sometimes beneficial. Consider a use-case such as a VARCHAR column that represents a
+ delimited list or set of Strings.
+
+
+ Delimited set of tags
+
+
+
+
+ See the Hibernate Integrations Guide for more details on developing
+ custom value type mappings.
+
+
+
+
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/collection/extras/DelimitedStringTagsExample.java b/documentation/src/main/docbook/mapping/en-US/chapters/collection/extras/DelimitedStringTagsExample.java
new file mode 100644
index 0000000000..3571beeb01
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/collection/extras/DelimitedStringTagsExample.java
@@ -0,0 +1,59 @@
+@Entity
+public static class Post {
+ @Id
+ public Integer id;
+ @Basic
+ @Type( type = "delimited_strings" )
+ Set tags;
+}
+
+
+public static class DelimitedStringsType extends AbstractSingleColumnStandardBasicType {
+ public DelimitedStringsType() {
+ super(
+ VarcharTypeDescriptor.INSTANCE,
+ new DelimitedStringsJavaTypeDescriptor()
+ );
+ }
+
+ @Override
+ public String getName() {
+ return "delimited_strings";
+ }
+}
+
+public static class DelimitedStringsJavaTypeDescriptor extends AbstractTypeDescriptor {
+ public DelimitedStringsJavaTypeDescriptor() {
+ super(
+ Set.class,
+ new MutableMutabilityPlan() {
+ @Override
+ protected Set deepCopyNotNull(Set value) {
+ Set copy = new HashSet();
+ copy.addAll( value );
+ return copy;
+ }
+ }
+ );
+ }
+
+ @Override
+ public String toString(Set value) {
+ return null;
+ }
+
+ @Override
+ public Set fromString(String string) {
+ return null;
+ }
+
+ @Override
+ public X unwrap(Set value, Class type, WrapperOptions options) {
+ return null;
+ }
+
+ @Override
+ public Set wrap(X value, WrapperOptions options) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/Composition.xml b/documentation/src/main/docbook/mapping/en-US/chapters/composition/Composition.xml
new file mode 100644
index 0000000000..acbf66d60a
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/Composition.xml
@@ -0,0 +1,210 @@
+
+
+
+ Compositions
+
+
+ Related topics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Historically Hibernate called these components. JPA calls them embeddables. Either way the concept is the
+ same: a composition of values. For example we might have a Name class that is a composition of
+ first-name and last-name, or an Address class that is a composition of street, city, postal code, etc.
+
+
+
+ Simple composition example
+
+
+
+
+
+ A composition is another form of value type. The lifecycle of a composition is defined by the thing that
+ contains it.
+
+
+
+ A composition inherits the attribute access of its parent. For details on attribute access, see
+ .
+
+
+
+ Compositions can be made up of basic values as well as associations, with the caveat that compositions which
+ are used as collection elements cannot themselves define collections.
+
+
+
+ Component / Embedded
+
+ This is the form of composition you will see most often. Here an entity or another composition
+ is the container.
+
+
+
+ Simple Embedded
+
+
+
+
+
+ Notice that JPA defines 2 terms for composition: Embeddable and Embedded. Embeddable is used to
+ describe the composition class itself (Name). Embedded is used to describe a usage of that
+ composition (Person.name).
+
+
+
+
+ The composition here is the Name type related to Person.name.
+
+
+
+ Person table
+
+
+
+
+ The composed values are mapped to the same table as the parent table. Composition is part of good
+ OO data modeling (idiomatic java). In fact that table could also be mapped by the following entity instead.
+
+
+
+ Alternative to composition
+
+
+
+
+ The composition form is certainly more OO. And that becomes more evident as we work with multiple
+ compositions.
+
+
+
+
+ Multiple compositions
+
+
+ Multiple compositions
+
+
+
+
+ It is certainly more convenient to work with the compositions. However, an interesting thing happens
+ in this particular example. By default, this mapping actually will not work as-is.
+ The problem is in how JPA defines implicit naming rules for columns that are part of a composition, which
+ say that all of the Address compositions would map to the same implicit column names.
+
+
+
+ This occurs any time we have multiple compositions based on the same embeddable in a given parent.
+ We have a few options to handle this issue.
+
+
+
+ JPA's AttributeOverride
+
+
+ The JPA-defined way to handle this situation is through the use of its AttributeOverride annotation.
+
+
+
+ JPA's AttributeOverride
+
+
+
+
+ Now, essentially there are no implicit column names in the Address compositions. We have explicitly
+ named them.
+
+
+
+
+ ImplicitNamingStrategy
+
+
+
+ This is a Hibernate specific feature. Users concerned with JPA provider portability should instead
+ prefer explicit column naming with AttributeOverride as per
+
+
+
+
+ Hibernate naming strategies are covered in detail in . However, for the purposes
+ of this discussion, Hibernate has the capability to interpret implicit column names in a way that is
+ safe for use with multiple compositions.
+
+
+
+ Enabling composition-safe implicit naming
+
+
+
+
+ Now the "path" to attributes are used in the implicit column naming.
+
+
+
+ Enabling composition-safe implicit naming
+
+
+
+
+ You could even develop your own to do special implicit naming.
+
+
+
+
+
+ Collections of compositions
+
+ Collections of compositions are specifically value collections (as compositions are a value type). Value
+ collections are covered in detail in .
+
+
+ The one thing to add to the discussion of value collections in regards to compositions is that
+ the composition cannot, in turn, define collections.
+
+
+
+
+ Compositions as Map key
+
+ Compositions can also be used as the key values for Maps. Mapping Maps and their keys is convered in
+ detail in .
+
+
+ Again, compositions used as a Map key cannot, in turn, define collections.
+
+
+
+
+ Compositions as identifiers
+
+ Compositions can also be used as entity identifiers. This usage is covered in detail in
+
+
+
+ Again, compositions used as an entity identifier cannot, in turn, define collections.
+
+
+
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Address.java b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Address.java
new file mode 100644
index 0000000000..fc13041d01
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Address.java
@@ -0,0 +1,15 @@
+@Embeddable
+public class Address {
+ private String line1;
+ private String line2;
+ @Embedded
+ private ZipCode zipCode;
+ ...
+
+ @Embeddable
+ public static class Zip {
+ private String postalCode;
+ private String plus4;
+ ...
+ }
+}
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact-AttributeOverride.java b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact-AttributeOverride.java
new file mode 100644
index 0000000000..f8c23dda1d
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact-AttributeOverride.java
@@ -0,0 +1,68 @@
+@Entity
+public class Contact {
+ @Id
+ private Integer id;
+ @Embedded
+ private Name name;
+ @Embedded
+ @AttributeOverrides(
+ @AttributeOverride(
+ name="line1",
+ column = @Column(name = "home_address_line1"),
+ ),
+ @AttributeOverride(
+ name="line2",
+ column = @Column(name = "home_address_line2")
+ ),
+ @AttributeOverride(
+ name="zipCode.postalCode",
+ column = @Column(name = "home_address_postal_cd")
+ ),
+ @AttributeOverride(
+ name="zipCode.plus4",
+ column = @Column(name = "home_address_postal_plus4")
+ )
+ )
+ private Address homeAddress;
+ @Embedded
+ @AttributeOverrides(
+ @AttributeOverride(
+ name="line1",
+ column = @Column(name = "mailing_address_line1"),
+ ),
+ @AttributeOverride(
+ name="line2",
+ column = @Column(name = "mailing_address_line2")
+ ),
+ @AttributeOverride(
+ name="zipCode.postalCode",
+ column = @Column(name = "mailing_address_postal_cd")
+ ),
+ @AttributeOverride(
+ name="zipCode.plus4",
+ column = @Column(name = "mailing_address_postal_plus4")
+ )
+ )
+ private Address mailingAddress;
+ @Embedded
+ @AttributeOverrides(
+ @AttributeOverride(
+ name="line1",
+ column = @Column(name = "work_address_line1"),
+ ),
+ @AttributeOverride(
+ name="line2",
+ column = @Column(name = "work_address_line2")
+ ),
+ @AttributeOverride(
+ name="zipCode.postalCode",
+ column = @Column(name = "work_address_postal_cd")
+ ),
+ @AttributeOverride(
+ name="zipCode.plus4",
+ column = @Column(name = "work_address_postal_plus4")
+ )
+ )
+ private Address workAddress;
+ ...
+}
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact-ImplicitNamingStrategy.sql b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact-ImplicitNamingStrategy.sql
new file mode 100644
index 0000000000..09f59a25c7
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact-ImplicitNamingStrategy.sql
@@ -0,0 +1,19 @@
+create table Contact(
+ id integer not null,
+ name_firstName VARCHAR,
+ name_middleName VARCHAR,
+ name_lastName VARCHAR,
+ homeAddress_line1 VARCHAR,
+ homeAddress_line2 VARCHAR,
+ homeAddress_zipCode_postalCode VARCHAR,
+ homeAddress_zipCode_plus4 VARCHAR,
+ mailingAddress_line1 VARCHAR,
+ mailingAddress_line2 VARCHAR,
+ mailingAddress_zipCode_postalCode VARCHAR,
+ mailingAddress_zipCode_plus4 VARCHAR,
+ workAddress_line1 VARCHAR,
+ workAddress_line2 VARCHAR,
+ workAddress_zipCode_postalCode VARCHAR,
+ workAddress_zipCode_plus4 VARCHAR,
+ ...
+)
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact.java b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact.java
new file mode 100644
index 0000000000..32cdea07b8
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Contact.java
@@ -0,0 +1,14 @@
+@Entity
+public class Contact {
+ @Id
+ private Integer id;
+ @Embedded
+ private Name name;
+ @Embedded
+ private Address homeAddress;
+ @Embedded
+ private Address mailingAddress;
+ @Embedded
+ private Address workAddress;
+ ...
+}
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Name.java b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Name.java
new file mode 100644
index 0000000000..69835c2999
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Name.java
@@ -0,0 +1,7 @@
+@Embeddable
+public class Name {
+ private String firstName;
+ private String middleName;
+ private String lastName;
+ ...
+}
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person.java b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person.java
new file mode 100644
index 0000000000..37db28cf3a
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person.java
@@ -0,0 +1,8 @@
+@Entity
+public class Person {
+ @Id
+ private Integer id;
+ @Embedded
+ private Name name;
+ ...
+}
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person1.sql b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person1.sql
new file mode 100644
index 0000000000..7dbc0647c4
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person1.sql
@@ -0,0 +1,7 @@
+create table Person (
+ id integer not null,
+ firstName VARCHAR,
+ middleName VARCHAR,
+ lastName VARCHAR,
+ ...
+)
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person_alt.java b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person_alt.java
new file mode 100644
index 0000000000..7f296932c3
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/Person_alt.java
@@ -0,0 +1,9 @@
+@Entity
+public class Person {
+ @Id
+ private Integer id;
+ private String firstName;
+ private String middleName;
+ private String lastName;
+ ...
+}
\ No newline at end of file
diff --git a/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/component-safe-implicit-naming.java b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/component-safe-implicit-naming.java
new file mode 100644
index 0000000000..8fa6857948
--- /dev/null
+++ b/documentation/src/main/docbook/mapping/en-US/chapters/composition/extras/component-safe-implicit-naming.java
@@ -0,0 +1,9 @@
+MetadataSources sources = ...;
+sources.addAnnotatedClass( Address.class );
+sources.addAnnotatedClass( Name.class );
+sources.addAnnotatedClass( Contact.class );
+
+Metadata metadata = sources.getMetadataBuilder()
+ .applyImplicitNamingStrategy( ImplicitNamingStrategyComponentPathImpl.INSTANCE )
+ ...
+ .build();
\ No newline at end of file
diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/basic/CollectionAsBasicTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/basic/CollectionAsBasicTest.java
new file mode 100644
index 0000000000..af4d5b786b
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/basic/CollectionAsBasicTest.java
@@ -0,0 +1,111 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.test.annotations.basic;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.persistence.Basic;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.hibernate.annotations.Type;
+import org.hibernate.boot.Metadata;
+import org.hibernate.boot.MetadataSources;
+import org.hibernate.boot.registry.StandardServiceRegistry;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.type.AbstractSingleColumnStandardBasicType;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
+import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
+import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
+
+import org.hibernate.testing.junit4.BaseUnitTestCase;
+import org.junit.Test;
+
+/**
+ * @author Steve Ebersole
+ */
+public class CollectionAsBasicTest extends BaseUnitTestCase {
+ @Test
+ public void testCollectionAsBasic() {
+ StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
+ try {
+ Metadata metadata = new MetadataSources().addAnnotatedClass( Post.class )
+ .getMetadataBuilder().applyBasicType( new DelimitedStringsType() )
+ .build();
+ PersistentClass postBinding = metadata.getEntityBinding( Post.class.getName() );
+ Property tagsAttribute = postBinding.getProperty( "tags" );
+ }
+ finally {
+ StandardServiceRegistryBuilder.destroy( ssr );
+ }
+ }
+
+ @Entity
+ @Table( name = "post")
+ public static class Post {
+ @Id
+ public Integer id;
+ @Basic
+ @Type( type = "delimited_strings" )
+ Set tags;
+ }
+
+ public static class DelimitedStringsType extends AbstractSingleColumnStandardBasicType {
+
+ public DelimitedStringsType() {
+ super(
+ VarcharTypeDescriptor.INSTANCE,
+ new DelimitedStringsJavaTypeDescriptor()
+ );
+ }
+
+ @Override
+ public String getName() {
+ return "delimited_strings";
+ }
+ }
+
+ public static class DelimitedStringsJavaTypeDescriptor extends AbstractTypeDescriptor {
+ public DelimitedStringsJavaTypeDescriptor() {
+ super(
+ Set.class,
+ new MutableMutabilityPlan() {
+ @Override
+ protected Set deepCopyNotNull(Set value) {
+ Set copy = new HashSet();
+ copy.addAll( value );
+ return copy;
+ }
+ }
+ );
+ }
+
+ @Override
+ public String toString(Set value) {
+ return null;
+ }
+
+ @Override
+ public Set fromString(String string) {
+ return null;
+ }
+
+ @Override
+ public X unwrap(Set value, Class type, WrapperOptions options) {
+ return null;
+ }
+
+ @Override
+ public Set wrap(X value, WrapperOptions options) {
+ return null;
+ }
+ }
+}