Convert basic chapter examples to unit tests for quoting, column transformers, and @Any mappings

This commit is contained in:
Vlad Mihalcea 2016-03-07 16:10:55 +02:00
parent fc24cdb708
commit 68af365916
23 changed files with 1118 additions and 88 deletions

View File

@ -1015,20 +1015,27 @@ You can force Hibernate to quote an identifier in the generated SQL by enclosing
Hibernate will use the correct quotation style for the SQL `Dialect`.
This is usually double quotes, but the SQL Server uses brackets and MySQL uses backticks.
[[basic-quoting-example]]
.Quoting column names
====
[source, JAVA, indent=0]
----
@Entity @Table(name="`Line Item`")
class LineItem {
include::{sourcedir}/basic/QuotingTest.java[tags=basic-quoting-example]
----
====
@Id
@Column(name="`Item Id`")
private Integer id;
Because `name` and `number` are reserved words, the `Product` entity mapping uses backtricks to quote these column names.
@Column(name="`Item #`")
private Integer itemNumber
}
When saving teh following `Product entity`, Hibernate generates the following SQL insert statement:
[[basic-quoting-persistence-example]]
.Persisting a quoted column name
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/QuotingTest.java[tags=basic-quoting-persistence-example, indent=0]
include::{extrasdir}/basic/basic-quoting-persistence-example.sql[indent=0]
----
====
@ -1115,25 +1122,14 @@ As you can see, the `new Date()` object value was used for assigning the `timest
==== Column transformers: read and write expressions
Hibernate allows you to customize the SQL it uses to read and write the values of columns mapped to `@Basic` types.
For example, if your database provides a set of data encryption functions, you can invoke them for individual columns like this:
For example, if your database provides a set of data encryption functions, you can invoke them for individual columns like in the following example.
[[mapping-column-read-and-write-example]]
.`@ColumnTransformer` example
====
[source, JAVA, indent=0]
----
@Entity
class CreditCard {
@Id
private Integer id;
@Column(name="credit_card_num")
@ColumnTransformer(
read="decrypt(credit_card_num)",
write="encrypt(?)"
)
private String creditCardNumber;
}
include::{sourcedir}/../fetching/FetchingTest.java[tags=mapping-column-read-and-write-example]
----
====
@ -1144,28 +1140,12 @@ You can use the plural form `@ColumnTransformers` if more than one columns need
If a property uses more than one column, you must use the `forColumn` attribute to specify which column, the expressions are targeting.
[[mapping-column-read-and-write-composite-type-example]]
.`@ColumnTransformer` `forColumn` attribute usage
====
[source, JAVA, indent=0]
----
@Entity
class User {
@Id
private Integer id;
@Type(type="com.acme.type.CreditCardType")
@Columns( {
@Column(name="credit_card_num"),
@Column(name="exp_date")
})
@ColumnTransformer(
forColumn="credit_card_num",
read="decrypt(credit_card_num)",
write="encrypt(?)"
)
private CreditCard creditCard;
}
include::{sourcedir}/basic/ColumnTransformerTest.java[tags=mapping-column-read-and-write-composite-type-example]
----
====
@ -1177,18 +1157,48 @@ This functionality is similar to a derived-property <<mapping-column-formula>> w
The `write` expression, if specified, must contain exactly one '?' placeholder for the value.
[[mapping-column-read-and-write-composite-type-persistence-example]]
.Persisting an entity with a `@ColumnTransformer` and a composite type
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/ColumnTransformerTest.java[tags=mapping-column-read-and-write-composite-type-persistence-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-column-read-and-write-composite-type-persistence-example.sql[]
----
====
[[mapping-column-formula]]
==== Formula
Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also create some kind of virtual column.
You can use a SQL fragment (aka formula) instead of mapping a property into a column. This kind of property is read only (its value is calculated by your formula fragment)
[[mapping-column-formula-example]]
.`@Formula` mapping usage
====
[source, JAVA, indent=0]
----
@Formula("obj_length * obj_height * obj_width")
private long objectVolume;
include::{sourcedir}/basic/FormulaTest.java[tags=mapping-column-formula-example]
----
====
When loading the `Account` entity, Hibernate is going to calculate the `interest` property using the configured `@Formula`:
[[mapping-column-formula-persistence-example]]
.Persisting an entity with a `@Formula` mapping
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/FormulaTest.java[tags=mapping-column-formula-persistence-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-column-formula-persistence-example.sql[]
----
====
@ -1198,7 +1208,7 @@ The SQL fragment can be as complex as you want and even include subselects.
====
[[mapping-column-any]]
==== Any
==== @Any mapping
There is one more type of property mapping.
The `@Any` mapping defines a polymorphic association to classes from multiple tables.
@ -1215,54 +1225,116 @@ This is not the usual way of mapping polymorphic associations and you should use
The `@Any` annotation describes the column holding the metadata information.
To link the value of the metadata information and an actual entity type, the `@AnyDef` and `@AnyDefs` annotations are used.
The `metaType` attribute allows the application to specify a custom type that maps database column values to persistent classes that have identifier properties of the type specified by `idType`.
You must specify the mapping from values of the metaType to class names.
You must specify the mapping from values of the `metaType` to class names.
For the next examples, consider the following `Property` class hierarchy:
[[mapping-column-any-property-example]]
.`Property` class hierarchy
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/any/Property.java[tags=mapping-column-any-property-example]
include::{sourcedir}/basic/any/IntegerProperty.java[tags=mapping-column-any-property-example]
include::{sourcedir}/basic/any/StringProperty.java[tags=mapping-column-any-property-example]
----
====
A `PropertyHolder` can reference any such property, and, because each `Property` belongs to a separate table, the `@Any` annotation is, therefore, required.
[[mapping-column-any-example]]
.`@Any` mapping usage
====
[source, JAVA, indent=0]
----
@Any(
metaColumn = @Column( name = "property_type" ),
fetch=FetchType.EAGER
)
@AnyMetaDef(
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
}
)
@JoinColumn( name = "property_id" )
private Property mainProperty;
include::{sourcedir}/basic/any/PropertyHolder.java[tags=mapping-column-any-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-column-any-example.sql[]
----
====
Note that `@AnyDef` can be mutualized and reused. It is recommended to place it as a package metadata in this case.
As you can see, there are two columns used to reference a `Property` instance: `property_id` and `property_type`.
The `property_id` is used to match the `id` column of either the `string_property` or `integer_property` tables,
while the `property_type` is used to match the `string_property` or the `integer_property` table.
.`@AnyMetaDef` mapping usage
The table resolving mapping is defined by the `metaDef` attribute which references an `@AnyMetaDef` mapping.
Although the `@AnyMetaDef` mapping could be set right next to the `@Any` annotation,
it's good practice to reuse it, therefore it makes sense to configure it on a class or package-level basis.
The `package-info.java` contains the `@AnyMetaDef` mapping:
[[mapping-column-any-meta-def-example]]
.`@Any` mapping usage
====
[source, JAVA, indent=0]
----
//on a package
@AnyMetaDef( name="property"
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
}
)
package org.hibernate.test.annotations.any;
//in a class
@Any(
metaDef="property",
metaColumn = @Column( name = "property_type" ),
fetch=FetchType.EAGER
)
@JoinColumn( name = "property_id" )
private Property mainProperty;
include::{sourcedir}/basic/any/package-info.java[tags=mapping-column-any-meta-def-example]
----
====
[NOTE]
====
It is recommended to place the `@AnyMetaDef` mapping as a package metadata.
====
To see how the `@Any` annotation in action, consider the following example:
[[mapping-column-any-persistence-example]]
.`@Any` mapping usage
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/any/AnyTest.java[tags=mapping-column-any-persistence-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-column-any-persistence-example.sql[]
----
====
[[mapping-column-many-to-any]]
===== `@ManyToAny` mapping
The `@Any` mapping is useful to emulate a `@ManyToOne` association when there can be multiple target entities.
To emulate a `@OneToMany` association, the `@ManyToAny` annotation must be used.
In the following example, the `PropertyRepository` entity has a collection of `Property` entities.
The `repository_properties` link table holds the associations between `PropertyRepository` and `Property` entities.
[[mapping-column-many-to-any-example]]
.`@ManyToAny` mapping usage
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/any/PropertyRepository.java[tags=mapping-column-many-to-any-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-column-many-to-any-example.sql[]
----
====
To see how the `@ManyToAny` annotation works, consider the following example:
[[mapping-column-many-to-any-persistence-example]]
.`@Any` mapping usage
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/any/ManyToAnyTest.java[tags=mapping-column-many-to-any-persistence-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-column-many-to-any-persistence-example.sql[]
----
====

View File

@ -0,0 +1,2 @@
INSERT INTO Product ("name", "number", id)
VALUES ('Mobile phone', '123-456-7890', 1)

View File

@ -0,0 +1,6 @@
CREATE TABLE property_holder (
id BIGINT NOT NULL,
property_type VARCHAR(255),
property_id BIGINT,
PRIMARY KEY ( id )
)

View File

@ -0,0 +1,25 @@
INSERT INTO integer_property
( "name", "value", id )
VALUES ( 'age', 23, 1 )
INSERT INTO string_property
( "name", "value", id )
VALUES ( 'name', 'John Doe', 1 )
INSERT INTO property_holder
( property_type, property_id, id )
VALUES ( 'S', 1, 1 )
SELECT ph.id AS id1_1_0_,
ph.property_type AS property2_1_0_,
ph.property_id AS property3_1_0_
FROM property_holder ph
WHERE ph.id = 1
SELECT sp.id AS id1_2_0_,
sp."name" AS name2_2_0_,
sp."value" AS value3_2_0_
FROM string_property sp
WHERE sp.id = 1

View File

@ -0,0 +1,12 @@
INSERT INTO Account (credit, rate, id)
VALUES (5000.0, 0.0125, 1)
SELECT
a.id as id1_0_0_,
a.credit as credit2_0_0_,
a.rate as rate3_0_0_,
a.credit * a.rate as formula0_0_
FROM
Account a
WHERE
a.id = 1

View File

@ -0,0 +1,10 @@
CREATE TABLE property_repository (
id BIGINT NOT NULL,
PRIMARY KEY ( id )
)
CREATE TABLE repository_properties (
repository_id BIGINT NOT NULL,
property_type VARCHAR(255),
property_id BIGINT NOT NULL
)

View File

@ -0,0 +1,36 @@
INSERT INTO integer_property
( "name", "value", id )
VALUES ( 'age', 23, 1 )
INSERT INTO string_property
( "name", "value", id )
VALUES ( 'name', 'John Doe', 1 )
INSERT INTO property_repository ( id )
VALUES ( 1 )
INSERT INTO repository_properties
( repository_id , property_type , property_id )
VALUES
( 1 , 'I' , 1 )
INSERT INTO repository_properties
( repository_id , property_type , property_id )
VALUES
( 1 , 'S' , 1 )
SELECT pr.id AS id1_1_0_
FROM property_repository pr
WHERE pr.id = 1
SELECT ip.id AS id1_0_0_ ,
integerpro0_."name" AS name2_0_0_ ,
integerpro0_."value" AS value3_0_0_
FROM integer_property integerpro0_
WHERE integerpro0_.id = 1
SELECT sp.id AS id1_3_0_ ,
sp."name" AS name2_3_0_ ,
sp."value" AS value3_3_0_
FROM string_property sp
WHERE sp.id = 1

View File

@ -0,0 +1,11 @@
INSERT INTO Savings (money, currency, id)
VALUES (10 * 100, 'USD', 1)
SELECT
s.id as id1_0_0_,
s.money / 100 as money2_0_0_,
s.currency as currency3_0_0_
FROM
Savings s
WHERE
s.id = 1

View File

@ -168,6 +168,7 @@ public class FetchingTest extends BaseEntityManagerFunctionalTestCase {
//Getters and setters omitted for brevity
}
//tag::mapping-column-read-and-write-example[]
@Entity(name = "Employee")
public static class Employee {
@ -194,6 +195,7 @@ public class FetchingTest extends BaseEntityManagerFunctionalTestCase {
//Getters and setters omitted for brevity
}
//end::mapping-column-read-and-write-example[]
@Entity(name = "Project")
public class Project {

View File

@ -0,0 +1,101 @@
/*
* 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.userguide.mapping.basic;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Locale;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Type;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.RequiresDialect;
import org.junit.Test;
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* @author Vlad Mihalcea
*/
@RequiresDialect(H2Dialect.class)
public class ColumnTransformerTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Savings.class
};
}
@Test
public void testLifecycle() {
//tag::mapping-column-read-and-write-composite-type-persistence-example[]
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::basic-datetime-temporal-date-persist-example[]
Savings savings = new Savings( );
savings.setId( 1L );
savings.setWallet( new MonetaryAmount( BigDecimal.TEN, Currency.getInstance( Locale.US ) ) );
entityManager.persist( savings );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Savings savings = entityManager.find( Savings.class, 1L );
assertEquals( 10, savings.getWallet().getAmount().intValue());
} );
//end::mapping-column-read-and-write-composite-type-persistence-example[]
}
//tag::mapping-column-read-and-write-composite-type-example[]
@Entity(name = "Savings")
public static class Savings {
@Id
private Long id;
@Type(type = "org.hibernate.userguide.mapping.basic.MonetaryAmountUserType")
@Columns(columns = {
@Column(name = "money"),
@Column(name = "currency")
})
@ColumnTransformer(
forColumn = "money",
read = "money / 100",
write = "? * 100"
)
private MonetaryAmount wallet;
//Getters and setters omitted for brevity
//end::mapping-column-read-and-write-composite-type-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public MonetaryAmount getWallet() {
return wallet;
}
public void setWallet(MonetaryAmount wallet) {
this.wallet = wallet;
}
//tag::mapping-column-read-and-write-composite-type-example[]
}
//end::mapping-column-read-and-write-composite-type-example[]
}

View File

@ -0,0 +1,103 @@
/*
* 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.userguide.mapping.basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Formula;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* @author Vlad Mihalcea
*/
public class FormulaTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Account.class
};
}
@Test
public void testLifecycle() {
//tag::mapping-column-formula-persistence-example[]
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::basic-datetime-temporal-date-persist-example[]
Account account = new Account( );
account.setId( 1L );
account.setCredit( 5000d );
account.setRate( 1.25 / 100 );
entityManager.persist( account );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Account account = entityManager.find( Account.class, 1L );
assertEquals( Double.valueOf( 62.5d ), account.getInterest());
} );
//end::mapping-column-formula-persistence-example[]
}
//tag::mapping-column-formula-example[]
@Entity(name = "Account")
public static class Account {
@Id
private Long id;
private Double credit;
private Double rate;
@Formula(value = "credit * rate")
private Double interest;
//Getters and setters omitted for brevity
//end::mapping-column-formula-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Double getCredit() {
return credit;
}
public void setCredit(Double credit) {
this.credit = credit;
}
public Double getRate() {
return rate;
}
public void setRate(Double rate) {
this.rate = rate;
}
public Double getInterest() {
return interest;
}
public void setInterest(Double interest) {
this.interest = interest;
}
//tag::mapping-column-formula-example[]
}
//end::mapping-column-formula-example[]
}

View File

@ -0,0 +1,42 @@
/*
* 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>.
*/
//$Id$
package org.hibernate.userguide.mapping.basic;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Currency;
/**
* @author Emmanuel Bernard
*/
public class MonetaryAmount implements Serializable {
private BigDecimal amount;
private Currency currency;
public MonetaryAmount(BigDecimal amount, Currency currency) {
this.amount = amount;
this.currency = currency;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public Currency getCurrency() {
return currency;
}
public void setCurrency(Currency currency) {
this.currency = currency;
}
}

View File

@ -0,0 +1,113 @@
/*
* 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>.
*/
//$Id$
package org.hibernate.userguide.mapping.basic;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Currency;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;
/**
* @author Emmanuel Bernard
*/
public class MonetaryAmountUserType implements CompositeUserType {
public String[] getPropertyNames() {
return new String[]{"amount", "currency"};
}
public Type[] getPropertyTypes() {
return new Type[]{ StandardBasicTypes.BIG_DECIMAL, StandardBasicTypes.CURRENCY };
}
public Object getPropertyValue(Object component, int property) throws HibernateException {
MonetaryAmount ma = (MonetaryAmount) component;
return property == 0 ? ma.getAmount() : ma.getCurrency();
}
public void setPropertyValue(Object component, int property, Object value)
throws HibernateException {
MonetaryAmount ma = (MonetaryAmount) component;
if ( property == 0 ) {
ma.setAmount( (BigDecimal) value );
}
else {
ma.setCurrency( (Currency) value );
}
}
public Class returnedClass() {
return String.class;
}
public boolean equals(Object x, Object y) throws HibernateException {
if ( x == y ) return true;
if ( x == null || y == null ) return false;
MonetaryAmount mx = (MonetaryAmount) x;
MonetaryAmount my = (MonetaryAmount) y;
return mx.getAmount().equals( my.getAmount() ) &&
mx.getCurrency().equals( my.getCurrency() );
}
public int hashCode(Object x) throws HibernateException {
return ( (MonetaryAmount) x ).getAmount().hashCode();
}
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
BigDecimal amt = StandardBasicTypes.BIG_DECIMAL.nullSafeGet( rs, names[0], session);
Currency cur = StandardBasicTypes.CURRENCY.nullSafeGet( rs, names[1], session );
if ( amt == null ) return null;
return new MonetaryAmount( amt, cur );
}
public void nullSafeSet(
PreparedStatement st, Object value, int index,
SessionImplementor session
) throws HibernateException, SQLException {
MonetaryAmount ma = (MonetaryAmount) value;
BigDecimal amt = ma == null ? null : ma.getAmount();
Currency cur = ma == null ? null : ma.getCurrency();
StandardBasicTypes.BIG_DECIMAL.nullSafeSet( st, amt, index, session );
StandardBasicTypes.CURRENCY.nullSafeSet( st, cur, index + 1, session );
}
public Object deepCopy(Object value) throws HibernateException {
MonetaryAmount ma = (MonetaryAmount) value;
return new MonetaryAmount( ma.getAmount(), ma.getCurrency() );
}
public boolean isMutable() {
return true;
}
public Serializable disassemble(Object value, SessionImplementor session)
throws HibernateException {
return (Serializable) deepCopy( value );
}
public Object assemble(Serializable cached, SessionImplementor session, Object owner)
throws HibernateException {
return deepCopy( cached );
}
public Object replace(Object original, Object target, SessionImplementor session, Object owner)
throws HibernateException {
return deepCopy( original ); //TODO: improve
}
}

View File

@ -0,0 +1,89 @@
/*
* 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.userguide.mapping.basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class QuotingTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Product.class
};
}
@Test
public void test() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::basic-quoting-persistence-example[]
Product product = new Product();
product.setId( 1L );
product.setName( "Mobile phone" );
product.setNumber( "123-456-7890" );
entityManager.persist( product );
//end::basic-quoting-persistence-example[]
} );
}
//tag::basic-quoting-example[]
@Entity(name = "Product")
public static class Product {
@Id
private Long id;
@Column(name = "`name`")
private String name;
@Column(name = "`number`")
private String number;
//Getters and setters are omitted for brevity
//end::basic-quoting-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
//tag::basic-quoting-example[]
}
//end::basic-quoting-example[]
}

View File

@ -0,0 +1,69 @@
/*
* 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.userguide.mapping.basic.any;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.userguide.util.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
/**
* @author Vlad Mihalcea
*/
public class AnyTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
IntegerProperty.class,
StringProperty.class,
PropertyHolder.class
};
}
@Override
protected String[] getAnnotatedPackages() {
return new String[] {
getClass().getPackage().getName()
};
}
@Test
public void test() {
//tag::mapping-column-any-persistence-example[]
doInHibernate( this::sessionFactory, session -> {
IntegerProperty ageProperty = new IntegerProperty();
ageProperty.setId( 1L );
ageProperty.setName( "age" );
ageProperty.setValue( 23 );
StringProperty nameProperty = new StringProperty();
nameProperty.setId( 1L );
nameProperty.setName( "name" );
nameProperty.setValue( "John Doe" );
session.persist( ageProperty );
session.persist( nameProperty );
PropertyHolder namePropertyHolder = new PropertyHolder();
namePropertyHolder.setId( 1L );
namePropertyHolder.setProperty( nameProperty );
session.persist( namePropertyHolder );
} );
doInHibernate( this::sessionFactory, session -> {
PropertyHolder propertyHolder = session.get( PropertyHolder.class, 1L );
assertEquals("name", propertyHolder.getProperty().getName());
assertEquals("John Doe", propertyHolder.getProperty().getValue());
} );
//end::mapping-column-any-persistence-example[]
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.userguide.mapping.basic.any;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
//tag::mapping-column-any-property-example[]
@Entity
@Table(name="integer_property")
public class IntegerProperty implements Property<Integer> {
@Id
private Long id;
@Column(name = "`name`")
private String name;
@Column(name = "`value`")
private Integer value;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
}
//end::mapping-column-any-property-example[]

View File

@ -0,0 +1,73 @@
/*
* 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.userguide.mapping.basic.any;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.userguide.util.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Vlad Mihalcea
*/
public class ManyToAnyTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
IntegerProperty.class,
StringProperty.class,
PropertyRepository.class
};
}
@Override
protected String[] getAnnotatedPackages() {
return new String[] {
getClass().getPackage().getName()
};
}
@Test
public void test() {
//tag::mapping-column-many-to-any-persistence-example[]
doInHibernate( this::sessionFactory, session -> {
IntegerProperty ageProperty = new IntegerProperty();
ageProperty.setId( 1L );
ageProperty.setName( "age" );
ageProperty.setValue( 23 );
StringProperty nameProperty = new StringProperty();
nameProperty.setId( 1L );
nameProperty.setName( "name" );
nameProperty.setValue( "John Doe" );
session.persist( ageProperty );
session.persist( nameProperty );
PropertyRepository propertyRepository = new PropertyRepository();
propertyRepository.setId( 1L );
propertyRepository.getProperties().add( ageProperty );
propertyRepository.getProperties().add( nameProperty );
session.persist( propertyRepository );
} );
doInHibernate( this::sessionFactory, session -> {
PropertyRepository propertyRepository = session.get( PropertyRepository.class, 1L );
assertEquals(2, propertyRepository.getProperties().size());
for(Property property : propertyRepository.getProperties()) {
assertNotNull( property.getValue() );
}
} );
//end::mapping-column-many-to-any-persistence-example[]
}
}

View File

@ -0,0 +1,16 @@
/*
* 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.userguide.mapping.basic.any;
//tag::mapping-column-any-property-example[]
public interface Property<T> {
String getName();
T getValue();
}
//end::mapping-column-any-property-example[]

View File

@ -0,0 +1,52 @@
/*
* 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.userguide.mapping.basic.any;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.Any;
//tag::mapping-column-any-example[]
@Entity
@Table( name = "property_holder" )
public class PropertyHolder {
@Id
private Long id;
@Any(
metaDef = "PropertyMetaDef",
metaColumn = @Column( name = "property_type" )
)
@JoinColumn( name = "property_id" )
private Property property;
//Getters and setters are omitted for brevity
//end::mapping-column-any-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
//tag::mapping-column-any-example[]
}
//end::mapping-column-any-example[]

View File

@ -0,0 +1,57 @@
/*
* 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.userguide.mapping.basic.any;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.ManyToAny;
//tag::mapping-column-many-to-any-example[]
@Entity
@Table( name = "property_repository" )
public class PropertyRepository {
@Id
private Long id;
@ManyToAny(
metaDef = "PropertyMetaDef",
metaColumn = @Column( name = "property_type" )
)
@Cascade( { org.hibernate.annotations.CascadeType.ALL })
@JoinTable(name = "repository_properties",
joinColumns = @JoinColumn(name = "repository_id"),
inverseJoinColumns = @JoinColumn(name = "property_id")
)
private List<Property<?>> properties = new ArrayList<>( );
//Getters and setters are omitted for brevity
//end::mapping-column-many-to-any-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Property<?>> getProperties() {
return properties;
}
//tag::mapping-column-many-to-any-example[]
}
//end::mapping-column-many-to-any-example[]

View File

@ -0,0 +1,54 @@
/*
* 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.userguide.mapping.basic.any;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
//tag::mapping-column-any-property-example[]
@Entity
@Table(name="string_property")
public class StringProperty implements Property<String> {
@Id
private Long id;
@Column(name = "`name`")
private String name;
@Column(name = "`value`")
private String value;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
//end::mapping-column-any-property-example[]

View File

@ -0,0 +1,24 @@
/*
* 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>.
*/
// $Id$
//tag::mapping-column-any-meta-def-example[]
@AnyMetaDefs(
@AnyMetaDef( name= "PropertyMetaDef", metaType = "string", idType = "long",
metaValues = {
@MetaValue(value = "S", targetEntity = StringProperty.class),
@MetaValue(value = "I", targetEntity = IntegerProperty.class)
}
)
)
package org.hibernate.userguide.mapping.basic.any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.AnyMetaDefs;
import org.hibernate.annotations.MetaValue;
//end::mapping-column-any-meta-def-example[]

View File

@ -4,16 +4,22 @@
* 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>.
*/
// $Id$
@AnyMetaDefs(
@AnyMetaDef( name= "Property", metaType = "string", idType = "integer",
metaValues = {
@MetaValue(value = "C", targetEntity = CharProperty.class),
@MetaValue(value = "I", targetEntity = IntegerProperty.class),
@MetaValue(value = "S", targetEntity = StringProperty.class),
@MetaValue(value = "L", targetEntity = LongProperty.class)
})
@AnyMetaDef(
name = "PropertyMetaDef",
idType = "integer",
metaType = "string",
metaValues = {
@MetaValue(
value = "S",
targetEntity = StringProperty.class
),
@MetaValue(
value = "I",
targetEntity = IntegerProperty.class
)
}
)
)
package org.hibernate.test.annotations.any;