mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-03-01 07:19:15 +00:00
HHH-9998 - Continue documentation TLC - mapping compositions & collections
This commit is contained in:
parent
9d22ed62ed
commit
7a67f39e38
@ -44,7 +44,11 @@
|
|||||||
|
|
||||||
<xi:include href="Preface.xml" />
|
<xi:include href="Preface.xml" />
|
||||||
|
|
||||||
<xi:include href="chapters/services/Services.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
<xi:include href="chapters/services/Services.xml" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<xi:include href="chapters/types/Custom_Types.xml" />
|
||||||
|
-->
|
||||||
|
|
||||||
</book>
|
</book>
|
||||||
|
|
||||||
|
@ -189,4 +189,8 @@
|
|||||||
</section>
|
</section>
|
||||||
-->
|
-->
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
JPA portability
|
||||||
|
* HQL/JPQL differences
|
||||||
|
* naming strategies
|
||||||
</chapter>
|
</chapter>
|
||||||
|
@ -46,13 +46,20 @@
|
|||||||
|
|
||||||
<xi:include href="chapters/categoizations/Data_Categorizations.xml" />
|
<xi:include href="chapters/categoizations/Data_Categorizations.xml" />
|
||||||
<xi:include href="chapters/basic/Basic_Types.xml" />
|
<xi:include href="chapters/basic/Basic_Types.xml" />
|
||||||
<!--
|
<xi:include href="chapters/composition/Composition.xml" />
|
||||||
<xi:include href="chapters/component/Component.xml" />
|
|
||||||
<xi:include href="chapters/collection/Collection.xml" />
|
<xi:include href="chapters/collection/Collection.xml" />
|
||||||
<xi:include href="chapters/entity/Entity.xml" />
|
|
||||||
<xi:include href="chapters/id/Identifiers.xml" />
|
<!--
|
||||||
<xi:include href="chapters/association/Associations.xml" />
|
<xi:include href="chapters/entity/Entity.xml" />
|
||||||
-->
|
<xi:include href="chapters/id/Identifiers.xml" />
|
||||||
|
<xi:include href="chapters/association/Associations.xml" />
|
||||||
|
<xi:include href="chapters/natural_id/Natural_Id.xml" />
|
||||||
|
|
||||||
|
<xi:include href="chapters/generation/Generated_attributes.xml" />
|
||||||
|
<xi:include href="chapters/access/Attribute_Access.xml" />
|
||||||
|
<xi:include href="chapters/overrides/Mapping_Overrides.xml" /> AttributeOverrides/AssociationOverrides
|
||||||
|
<xi:include href="chapters/naming/Naming_Strategies.xml" />
|
||||||
|
-->
|
||||||
|
|
||||||
<!-- appendices? -->
|
<!-- appendices? -->
|
||||||
<!-- biblio? -->
|
<!-- biblio? -->
|
||||||
|
@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
-->
|
||||||
|
<chapter xml:id="collections"
|
||||||
|
version="5.0"
|
||||||
|
xml:lang="en"
|
||||||
|
xmlns="http://docbook.org/ns/docbook"
|
||||||
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||||
|
>
|
||||||
|
<title>Collections</title>
|
||||||
|
|
||||||
|
<section xml:id="collections-synopsis">
|
||||||
|
<title>Collections as a value type</title>
|
||||||
|
<para>
|
||||||
|
discussions of what it means for them to be value types - lifecycle, opt-locking
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Collection attributes do not support null value semantics; Hibernate does not distinguish between a null
|
||||||
|
collection reference and an empty collection.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="collections-value">
|
||||||
|
<title>Collections of value types</title>
|
||||||
|
<para>
|
||||||
|
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
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="collections-entity">
|
||||||
|
<title>Collections of entities</title>
|
||||||
|
<para>
|
||||||
|
* one-to-many
|
||||||
|
* many-to-many
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="collections-list">
|
||||||
|
<title>List - index</title>
|
||||||
|
<para>
|
||||||
|
<!-- todo : discuss mapping list index -->
|
||||||
|
todo : discuss mapping list index
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="collections-map">
|
||||||
|
<title>Map - key</title>
|
||||||
|
<para>
|
||||||
|
<!-- todo : discuss mapping map key -->
|
||||||
|
todo : discuss mapping map key
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="collections-bag">
|
||||||
|
<title>Bags</title>
|
||||||
|
<para>
|
||||||
|
<!-- todo : discuss mapping bags -->
|
||||||
|
todo : discuss mapping bags
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="collections-array">
|
||||||
|
<title>Arrays</title>
|
||||||
|
<para>
|
||||||
|
<!-- todo : discuss mapping arrays -->
|
||||||
|
todo : discuss mapping arrays
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="collections-as-basic">
|
||||||
|
<title>Collections as basic value type</title>
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This is sometimes beneficial. Consider a use-case such as a VARCHAR column that represents a
|
||||||
|
delimited list or set of Strings.
|
||||||
|
</para>
|
||||||
|
<example>
|
||||||
|
<title>Delimited set of tags</title>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/DelimitedStringTagsExample.java" parse="text" /></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
See the <citetitle>Hibernate Integrations Guide</citetitle> for more details on developing
|
||||||
|
custom value type mappings.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</chapter>
|
@ -0,0 +1,59 @@
|
|||||||
|
@Entity
|
||||||
|
public static class Post {
|
||||||
|
@Id
|
||||||
|
public Integer id;
|
||||||
|
@Basic
|
||||||
|
@Type( type = "delimited_strings" )
|
||||||
|
Set<String> tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class DelimitedStringsType extends AbstractSingleColumnStandardBasicType<Set> {
|
||||||
|
public DelimitedStringsType() {
|
||||||
|
super(
|
||||||
|
VarcharTypeDescriptor.INSTANCE,
|
||||||
|
new DelimitedStringsJavaTypeDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "delimited_strings";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DelimitedStringsJavaTypeDescriptor extends AbstractTypeDescriptor<Set> {
|
||||||
|
public DelimitedStringsJavaTypeDescriptor() {
|
||||||
|
super(
|
||||||
|
Set.class,
|
||||||
|
new MutableMutabilityPlan<Set>() {
|
||||||
|
@Override
|
||||||
|
protected Set deepCopyNotNull(Set value) {
|
||||||
|
Set<String> copy = new HashSet<String>();
|
||||||
|
copy.addAll( value );
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Set value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set fromString(String string) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> X unwrap(Set value, Class<X> type, WrapperOptions options) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> Set wrap(X value, WrapperOptions options) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,210 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
-->
|
||||||
|
<chapter xml:id="composition"
|
||||||
|
version="5.0"
|
||||||
|
xml:lang="en"
|
||||||
|
xmlns="http://docbook.org/ns/docbook"
|
||||||
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||||
|
>
|
||||||
|
<title>Compositions</title>
|
||||||
|
|
||||||
|
<sidebar>
|
||||||
|
<title>Related topics</title>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para><xref linkend="access"/></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para><xref linkend="overrides"/></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para><xref linkend="naming"/></para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</sidebar>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example xml:id="composition-ex-embeddable">
|
||||||
|
<title>Simple composition example</title>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/Name.java" parse="text" /></programlisting>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/Address.java" parse="text" /></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A composition is another form of value type. The lifecycle of a composition is defined by the thing that
|
||||||
|
contains it.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A composition inherits the attribute access of its parent. For details on attribute access, see
|
||||||
|
<xref linkend="access"/>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Component / Embedded</title>
|
||||||
|
<para>
|
||||||
|
This is the form of composition you will see most often. Here an entity or another composition
|
||||||
|
is the container.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example xml:id="composition-ex-embedded1">
|
||||||
|
<title>Simple Embedded</title>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/Person.java" parse="text" /></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>
|
||||||
|
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).
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The composition here is the Name type related to Person.name.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example xml:id="composition-ex-embedded1-sql">
|
||||||
|
<title>Person table</title>
|
||||||
|
<programlisting role="SQL"><xi:include href="extras/Person1.sql" parse="text" /></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example xml:id="composition-ex-no-composition">
|
||||||
|
<title>Alternative to composition</title>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/Person_alt.java" parse="text" /></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The composition form is certainly more OO. And that becomes more evident as we work with multiple
|
||||||
|
compositions.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="composition-multiple">
|
||||||
|
<title>Multiple compositions</title>
|
||||||
|
|
||||||
|
<example xml:id="composition-ex-multiple-compositions">
|
||||||
|
<title>Multiple compositions</title>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/Contact.java" parse="text" /></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<section xml:id="composition-multiple-jpa">
|
||||||
|
<title>JPA's AttributeOverride</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The JPA-defined way to handle this situation is through the use of its AttributeOverride annotation.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>JPA's AttributeOverride</title>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/Contact-AttributeOverride.java" parse="text"/></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Now, essentially there are no implicit column names in the Address compositions. We have explicitly
|
||||||
|
named them.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="composition-multiple-namingstrategy">
|
||||||
|
<title>ImplicitNamingStrategy</title>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>
|
||||||
|
This is a Hibernate specific feature. Users concerned with JPA provider portability should instead
|
||||||
|
prefer explicit column naming with AttributeOverride as per <xref linkend="composition-multiple-jpa"/>
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Hibernate naming strategies are covered in detail in <xref linkend="naming"/>. 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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>Enabling composition-safe implicit naming</title>
|
||||||
|
<programlisting role="JAVA"><xi:include href="extras/component-safe-implicit-naming.java" parse="text"/></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Now the "path" to attributes are used in the implicit column naming.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>Enabling composition-safe implicit naming</title>
|
||||||
|
<programlisting role="SQL"><xi:include href="extras/Contact-ImplicitNamingStrategy.sql" parse="text"/></programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
You could even develop your own to do special implicit naming.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="composition-collections">
|
||||||
|
<title>Collections of compositions</title>
|
||||||
|
<para>
|
||||||
|
Collections of compositions are specifically value collections (as compositions are a value type). Value
|
||||||
|
collections are covered in detail in <xref linkend="collections-value"/>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The one thing to add to the discussion of value collections in regards to compositions is that
|
||||||
|
the composition cannot, in turn, define collections.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="composition-mapkey">
|
||||||
|
<title>Compositions as Map key</title>
|
||||||
|
<para>
|
||||||
|
Compositions can also be used as the key values for Maps. Mapping Maps and their keys is convered in
|
||||||
|
detail in <xref linkend="collections-map-key"/>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Again, compositions used as a Map key cannot, in turn, define collections.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="composition-identifier">
|
||||||
|
<title>Compositions as identifiers</title>
|
||||||
|
<para>
|
||||||
|
Compositions can also be used as entity identifiers. This usage is covered in detail in
|
||||||
|
<xref linkend="identifier-composite"/>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Again, compositions used as an entity identifier cannot, in turn, define collections.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
</chapter>
|
@ -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;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
...
|
||||||
|
}
|
@ -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,
|
||||||
|
...
|
||||||
|
)
|
@ -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;
|
||||||
|
...
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
@Embeddable
|
||||||
|
public class Name {
|
||||||
|
private String firstName;
|
||||||
|
private String middleName;
|
||||||
|
private String lastName;
|
||||||
|
...
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
@Entity
|
||||||
|
public class Person {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
@Embedded
|
||||||
|
private Name name;
|
||||||
|
...
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
create table Person (
|
||||||
|
id integer not null,
|
||||||
|
firstName VARCHAR,
|
||||||
|
middleName VARCHAR,
|
||||||
|
lastName VARCHAR,
|
||||||
|
...
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
@Entity
|
||||||
|
public class Person {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
private String firstName;
|
||||||
|
private String middleName;
|
||||||
|
private String lastName;
|
||||||
|
...
|
||||||
|
}
|
@ -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();
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<String> tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DelimitedStringsType extends AbstractSingleColumnStandardBasicType<Set> {
|
||||||
|
|
||||||
|
public DelimitedStringsType() {
|
||||||
|
super(
|
||||||
|
VarcharTypeDescriptor.INSTANCE,
|
||||||
|
new DelimitedStringsJavaTypeDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "delimited_strings";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DelimitedStringsJavaTypeDescriptor extends AbstractTypeDescriptor<Set> {
|
||||||
|
public DelimitedStringsJavaTypeDescriptor() {
|
||||||
|
super(
|
||||||
|
Set.class,
|
||||||
|
new MutableMutabilityPlan<Set>() {
|
||||||
|
@Override
|
||||||
|
protected Set deepCopyNotNull(Set value) {
|
||||||
|
Set<String> copy = new HashSet<String>();
|
||||||
|
copy.addAll( value );
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Set value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set fromString(String string) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> X unwrap(Set value, Class<X> type, WrapperOptions options) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> Set wrap(X value, WrapperOptions options) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user