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="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>
|
||||
|
||||
|
|
|
@ -189,4 +189,8 @@
|
|||
</section>
|
||||
-->
|
||||
</section>
|
||||
|
||||
JPA portability
|
||||
* HQL/JPQL differences
|
||||
* naming strategies
|
||||
</chapter>
|
||||
|
|
|
@ -46,13 +46,20 @@
|
|||
|
||||
<xi:include href="chapters/categoizations/Data_Categorizations.xml" />
|
||||
<xi:include href="chapters/basic/Basic_Types.xml" />
|
||||
<!--
|
||||
<xi:include href="chapters/component/Component.xml" />
|
||||
<xi:include href="chapters/composition/Composition.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? -->
|
||||
<!-- 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…
Reference in New Issue