HHH-11290 - Migrate all documentation snippets that derive the source code from extras instead of actual Unit Tests

Fixed in the Entity chapter
This commit is contained in:
Vlad Mihalcea 2017-07-04 12:05:12 +03:00
parent f9cb1d5cb4
commit 87ce2670e7
17 changed files with 761 additions and 183 deletions

View File

@ -1,7 +1,7 @@
[[entity]] [[entity]]
=== Entity types === Entity types
:sourcedir-locking: ../../../../../test/java/org/hibernate/userguide/locking :sourcedir-locking: ../../../../../test/java/org/hibernate/userguide/locking
:sourcedir-mapping: ../../../../../test/java/org/hibernate/userguide/mapping/basic :sourcedir-mapping: ../../../../../test/java/org/hibernate/userguide/mapping/
:sourcedir-proxy: ../../../../../test/java/org/hibernate/userguide/proxy :sourcedir-proxy: ../../../../../test/java/org/hibernate/userguide/proxy
:sourcedir-persister: ../../../../../test/java/org/hibernate/userguide/persister :sourcedir-persister: ../../../../../test/java/org/hibernate/userguide/persister
:extrasdir: extras :extrasdir: extras
@ -99,11 +99,12 @@ We recommend that you declare consistently-named identifier attributes on persis
The placement of the `@Id` annotation marks the <<chapters/domain/access.adoc#access,persistence state access strategy>>. The placement of the `@Id` annotation marks the <<chapters/domain/access.adoc#access,persistence state access strategy>>.
.Identifier [[entity-pojo-identifier-mapping-example]]
.Identifier mapping
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/Identifier.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-identifier-mapping-example, indent=0]
---- ----
==== ====
@ -116,11 +117,12 @@ The main piece in mapping the entity is the `javax.persistence.Entity` annotatio
The `@Entity` annotation defines just one attribute `name` which is used to give a specific entity name for use in JPQL queries. The `@Entity` annotation defines just one attribute `name` which is used to give a specific entity name for use in JPQL queries.
By default, the entity name represents the unqualified name of the entity class itself. By default, the entity name represents the unqualified name of the entity class itself.
.Simple `@Entity` [[entity-pojo-mapping-example]]
.Simple `@Entity` mapping
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/SimpleEntity.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-mapping-example, indent=0]
---- ----
==== ====
@ -129,11 +131,12 @@ The identifier uniquely identifies each row in that table.
By default, the name of the table is assumed to be the same as the name of the entity. By default, the name of the table is assumed to be the same as the name of the entity.
To explicitly give the name of the table or to specify other information about the table, we would use the `javax.persistence.Table` annotation. To explicitly give the name of the table or to specify other information about the table, we would use the `javax.persistence.Table` annotation.
[[entity-pojo-table-mapping-example]]
.Simple `@Entity` with `@Table` .Simple `@Entity` with `@Table`
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/SimpleEntityWithTable.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTableTest.java[tag=entity-pojo-table-mapping-example, indent=0]
---- ----
==== ====
@ -162,88 +165,109 @@ Hibernate, however, works hard to make sure that does not happen within a given
In fact, Hibernate guarantees equivalence of persistent identity (database row) and Java identity inside a particular session scope. In fact, Hibernate guarantees equivalence of persistent identity (database row) and Java identity inside a particular session scope.
So if we ask a Hibernate `Session` to load that specific Person multiple times we will actually get back the same __instance__: So if we ask a Hibernate `Session` to load that specific Person multiple times we will actually get back the same __instance__:
[[entity-pojo-identity-scope-example]]
.Scope of identity .Scope of identity
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing1.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-identity-scope-example, indent=0]
---- ----
==== ====
Consider another example using a persistent `java.util.Set`: Consider we have a `Library` parent entity which contains a `java.util.Set` of `Book` entities:
[[entity-pojo-set-mapping-example]]
Library entity mapping
====
[source,java]
----
include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-set-mapping-example, indent=0]
----
====
[[entity-pojo-set-identity-scope-example]]
.Set usage with Session-scoped identity .Set usage with Session-scoped identity
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing3.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-set-identity-scope-example, indent=0]
---- ----
==== ====
However, the semantic changes when we mix instances loaded from different Sessions: However, the semantic changes when we mix instances loaded from different Sessions:
[[entity-pojo-multi-session-identity-scope-example]]
.Mixed Sessions .Mixed Sessions
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing2.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-multi-session-identity-scope-example, indent=0]
---- ----
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing4.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-multi-session-set-identity-scope-example, indent=0]
---- ----
==== ====
Specifically the outcome in this last example will depend on whether the `Person` class implemented equals/hashCode, and, if so, how. Specifically the outcome in this last example will depend on whether the `Book` class
implemented equals/hashCode, and, if so, how.
If the `Book` class did not override the default equals/hashCode,
then the two `Book` object reference are not going to be equal since their references are different.
Consider yet another case: Consider yet another case:
[[entity-pojo-transient-set-identity-scope-example]]
.Sets with transient entities .Sets with transient entities
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing5.java[] include::{sourcedir-mapping}/identifier/SimpleEntityTest.java[tag=entity-pojo-transient-set-identity-scope-example, indent=0]
---- ----
==== ====
In cases where you will be dealing with entities outside of a Session (whether they be transient or detached), especially in cases where you will be using them in Java collections, In cases where you will be dealing with entities outside of a Session (whether they be transient or detached),
especially in cases where you will be using them in Java collections,
you should consider implementing equals/hashCode. you should consider implementing equals/hashCode.
A common initial approach is to use the entity's identifier attribute as the basis for equals/hashCode calculations: A common initial approach is to use the entity's identifier attribute as the basis for equals/hashCode calculations:
[[entity-pojo-naive-equals-hashcode-example]]
.Naive equals/hashCode implementation .Naive equals/hashCode implementation
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing6.java[] include::{sourcedir-mapping}/identifier/NaiveEqualsHashCodeEntityTest.java[tag=entity-pojo-naive-equals-hashcode-example, indent=0]
---- ----
==== ====
It turns out that this still breaks when adding transient instance of `Person` to a set as we saw in the last example: It turns out that this still breaks when adding transient instance of `Book` to a set as we saw in the last example:
.Still trouble [[entity-pojo-naive-equals-hashcode-example]]
.Auto-generated identifiers with Sets and naive equals/hashCode
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing7.java[] include::{sourcedir-mapping}/identifier/NaiveEqualsHashCodeEntityTest.java[tag=entity-pojo-naive-equals-hashcode-persist-example, indent=0]
---- ----
==== ====
The issue here is a conflict between the use of generated identifier, the contract of `Set` and the equals/hashCode implementations. The issue here is a conflict between the use of generated identifier, the contract of `Set` and the equals/hashCode implementations.
`Set` says that the equals/hashCode value for an object should not change while the object is part of the `Set`. `Set` says that the equals/hashCode value for an object should not change while the object is part of the `Set`.
But that is exactly what happened here because the equals/hasCode are based on the (generated) id, which was not set until the `session.getTransaction().commit()` call. But that is exactly what happened here because the equals/hasCode are based on the (generated) id, which was not set until the JPA transaction is committed.
Note that this is just a concern when using generated identifiers. Note that this is just a concern when using generated identifiers.
If you are using assigned identifiers this will not be a problem, assuming the identifier value is assigned prior to adding to the `Set`. If you are using assigned identifiers this will not be a problem, assuming the identifier value is assigned prior to adding to the `Set`.
Another option is to force the identifier to be generated and set prior to adding to the `Set`: Another option is to force the identifier to be generated and set prior to adding to the `Set`:
.Forcing identifier generation [[entity-pojo-naive-equals-hashcode-persist-force-flush-example]]
.Forcing the flush before adding to the Set
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing8.java[] include::{sourcedir-mapping}/identifier/NaiveEqualsHashCodeEntityTest.java[tag=entity-pojo-naive-equals-hashcode-persist-force-flush-example, indent=0]
---- ----
==== ====
@ -251,11 +275,23 @@ But this is often not feasible.
The final approach is to use a "better" equals/hashCode implementation, making use of a natural-id or business-key. The final approach is to use a "better" equals/hashCode implementation, making use of a natural-id or business-key.
.Better equals/hashCode with natural-id [[entity-pojo-natural-id-equals-hashcode-example]]
.Natural Id equals/hashCode
==== ====
[source,java] [source,java]
---- ----
include::{extrasdir}/entity/listing9.java[] include::{sourcedir-mapping}/identifier/NaturalIdEqualsHashCodeEntityTest.java[tag=entity-pojo-natural-id-equals-hashcode-example, indent=0]
----
====
This time, when adding a `Book` to the `Library` `Set`, you can retrieve the `Book` even after it's being persisted:
[[entity-pojo-natural-id-equals-hashcode-persist-example]]
.Natural Id equals/hashCode persist example
====
[source,java]
----
include::{sourcedir-mapping}/identifier/NaturalIdEqualsHashCodeEntityTest.java[tag=entity-pojo-natural-id-equals-hashcode-persist-example, indent=0]
---- ----
==== ====
@ -283,7 +319,7 @@ You can map an entity to a SQL query using the https://docs.jboss.org/hibernate/
==== ====
[source,java] [source,java]
---- ----
include::{sourcedir-mapping}/SubselectTest.java[tag=mapping-Subselect-example,indent=0] include::{sourcedir-mapping}/basic/SubselectTest.java[tag=mapping-Subselect-example,indent=0]
---- ----
==== ====
@ -299,7 +335,7 @@ So, if we have the following `AccountTransaction` record, the `AccountSummary` b
==== ====
[source,java] [source,java]
---- ----
include::{sourcedir-mapping}/SubselectTest.java[tag=mapping-Subselect-entity-find-example,indent=0] include::{sourcedir-mapping}/basic/SubselectTest.java[tag=mapping-Subselect-entity-find-example,indent=0]
---- ----
==== ====
@ -310,7 +346,7 @@ If we add a new `AccountTransaction` entity and refresh the `AccountSummary` ent
==== ====
[source,java] [source,java]
---- ----
include::{sourcedir-mapping}/SubselectTest.java[tag=mapping-Subselect-entity-refresh-example,indent=0] include::{sourcedir-mapping}/basic/SubselectTest.java[tag=mapping-Subselect-entity-refresh-example,indent=0]
---- ----
==== ====

View File

@ -1,4 +0,0 @@
@Entity
public class Simple {
...
}

View File

@ -1,5 +0,0 @@
@Entity
@Table( catalog = "CRM", schema = "purchasing", name = "t_simple" )
public class Simple {
...
}

View File

@ -1,7 +0,0 @@
Session session=...;
Person p1 = session.get( Person.class,1 );
Person p2 = session.get( Person.class,1 );
// this evaluates to true
assert p1==p2;

View File

@ -1,8 +0,0 @@
Session session1=...;
Session session2=...;
Person p1 = session1.get( Person.class,1 );
Person p2 = session2.get( Person.class,1 );
// this evaluates to false
assert p1==p2;

View File

@ -1,12 +0,0 @@
Session session=...;
Club club = session.get( Club.class,1 );
Person p1 = session.get( Person.class,1 );
Person p2 = session.get( Person.class,1 );
club.getMembers().add( p1 );
club.getMembers().add( p2 );
// this evaluates to true
assert club.getMembers.size()==1;

View File

@ -1,13 +0,0 @@
Session session1=...;
Session session2=...;
Club club = session1.get( Club.class,1 );
Person p1 = session1.get( Person.class,1 );
Person p2 = session2.get( Person.class,1 );
club.getMembers().add( p1 );
club.getMembers().add( p2 );
// this evaluates to ... well it depends
assert club.getMembers.size()==1;

View File

@ -1,12 +0,0 @@
Session session=...;
Club club = session.get( Club.class,1 );
Person p1 = new Person(...);
Person p2 = new Person(...);
club.getMembers().add( p1 );
club.getMembers().add( p2 );
// this evaluates to ... again, it depends
assert club.getMembers.size()==1;

View File

@ -1,24 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Integer id;
@Override
public int hashCode() {
return Objects.hash( id );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( !( o instanceof Person ) ) {
return false;
}
Person person = (Person) o;
return Objects.equals( id, person.id );
}
}

View File

@ -1,15 +0,0 @@
Session session=...;
session.getTransaction().begin();
Club club = session.get( Club.class,1 );
Person p1 = new Person(...);
Person p2 = new Person(...);
club.getMembers().add( p1 );
club.getMembers().add( p2 );
session.getTransaction().commit();
// will actually resolve to false!
assert club.getMembers().contains( p1 );

View File

@ -1,19 +0,0 @@
Session session=...;
session.getTransaction().begin();
Club club = session.get( Club.class,1 );
Person p1 = new Person(...);
Person p2 = new Person(...);
session.save( p1 );
session.save( p2 );
session.flush();
club.getMembers().add( p1 );
club.getMembers().add( p2 );
session.getTransaction().commit();
// will actually resolve to false!
assert club.getMembers().contains( p1 );

View File

@ -1,36 +0,0 @@
@Entity
public class Person {
@Id
@GeneratedValue
private Integer id;
@NaturalId
private String ssn;
protected Person() {
// Constructor for ORM
}
public Person( String ssn ) {
// Constructor for app
this.ssn = ssn;
}
@Override
public int hashCode() {
return Objects.hash( ssn );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( !( o instanceof Person ) ) {
return false;
}
Person person = (Person) o;
return Objects.equals( ssn, person.ssn );
}
}

View File

@ -0,0 +1,201 @@
/*
* 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.identifier;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Vlad Mihalcea
*/
public class NaiveEqualsHashCodeEntityTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Library.class,
Book.class
};
}
@Before
public void init() {
doInJPA( this::entityManagerFactory, entityManager -> {
Library library = new Library();
library.setId( 1L );
library.setName( "Amazon" );
entityManager.persist( library );
} );
}
@Test
public void testPersist() {
//tag::entity-pojo-naive-equals-hashcode-persist-example[]
Book book1 = new Book();
book1.setTitle( "High-Performance Java Persistence" );
Book book2 = new Book();
book2.setTitle( "Java Persistence with Hibernate" );
Library library = doInJPA( this::entityManagerFactory, entityManager -> {
Library _library = entityManager.find( Library.class, 1L );
_library.getBooks().add( book1 );
_library.getBooks().add( book2 );
return _library;
} );
assertFalse( library.getBooks().contains( book1 ) );
assertFalse( library.getBooks().contains( book2 ) );
//end::entity-pojo-naive-equals-hashcode-persist-example[]
}
@Test
public void testPersistForceFlush() {
//tag::entity-pojo-naive-equals-hashcode-persist-force-flush-example[]
Book book1 = new Book();
book1.setTitle( "High-Performance Java Persistence" );
Book book2 = new Book();
book2.setTitle( "Java Persistence with Hibernate" );
Library library = doInJPA( this::entityManagerFactory, entityManager -> {
Library _library = entityManager.find( Library.class, 1L );
entityManager.persist( book1 );
entityManager.persist( book2 );
entityManager.flush();
_library.getBooks().add( book1 );
_library.getBooks().add( book2 );
return _library;
} );
assertTrue( library.getBooks().contains( book1 ) );
assertTrue( library.getBooks().contains( book2 ) );
//end::entity-pojo-naive-equals-hashcode-persist-force-flush-example[]
}
//tag::entity-pojo-naive-equals-hashcode-example[]
@Entity(name = "Library")
public static class Library {
@Id
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "book_id")
private Set<Book> books = new HashSet<>();
//Getters and setters are omitted for brevity
//end::entity-pojo-naive-equals-hashcode-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 Set<Book> getBooks() {
return books;
}
//tag::entity-pojo-naive-equals-hashcode-example[]
}
@Entity(name = "Book")
public static class Book {
@Id
@GeneratedValue
private Long id;
private String title;
private String author;
//Getters and setters are omitted for brevity
//end::entity-pojo-naive-equals-hashcode-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
//tag::entity-pojo-naive-equals-hashcode-example[]
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Book book = (Book) o;
return Objects.equals( id, book.id );
}
@Override
public int hashCode() {
return Objects.hash( id );
}
}
//end::entity-pojo-naive-equals-hashcode-example[]
}

View File

@ -0,0 +1,180 @@
/*
* 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.identifier;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import org.hibernate.annotations.NaturalId;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Vlad Mihalcea
*/
public class NaturalIdEqualsHashCodeEntityTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Library.class,
Book.class
};
}
@Before
public void init() {
doInJPA( this::entityManagerFactory, entityManager -> {
Library library = new Library();
library.setId( 1L );
library.setName( "Amazon" );
entityManager.persist( library );
} );
}
@Test
public void testPersist() {
//tag::entity-pojo-natural-id-equals-hashcode-persist-example[]
Book book1 = new Book();
book1.setTitle( "High-Performance Java Persistence" );
book1.setIsbn( "978-9730228236" );
Library library = doInJPA( this::entityManagerFactory, entityManager -> {
Library _library = entityManager.find( Library.class, 1L );
_library.getBooks().add( book1 );
return _library;
} );
assertTrue( library.getBooks().contains( book1 ) );
//end::entity-pojo-natural-id-equals-hashcode-persist-example[]
}
//tag::entity-pojo-natural-id-equals-hashcode-example[]
@Entity(name = "Library")
public static class Library {
@Id
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "book_id")
private Set<Book> books = new HashSet<>();
//Getters and setters are omitted for brevity
//end::entity-pojo-natural-id-equals-hashcode-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 Set<Book> getBooks() {
return books;
}
//tag::entity-pojo-natural-id-equals-hashcode-example[]
}
@Entity(name = "Book")
public static class Book {
@Id
@GeneratedValue
private Long id;
private String title;
private String author;
@NaturalId
private String isbn;
//Getters and setters are omitted for brevity
//end::entity-pojo-natural-id-equals-hashcode-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
//tag::entity-pojo-natural-id-equals-hashcode-example[]
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Book book = (Book) o;
return Objects.equals( isbn, book.isbn );
}
@Override
public int hashCode() {
return Objects.hash( isbn );
}
}
//end::entity-pojo-natural-id-equals-hashcode-example[]
}

View File

@ -0,0 +1,82 @@
/*
* 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.identifier;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertTrue;
/**
* @author Vlad Mihalcea
*/
public class SimpleEntityTableTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Book.class
};
}
@Test
public void test() {
}
//tag::entity-pojo-table-mapping-example[]
@Entity(name = "Book")
@Table(
catalog = "public",
schema = "store",
name = "book"
)
public static class Book {
@Id
private Long id;
private String title;
private String author;
//Getters and setters are omitted for brevity
//end::entity-pojo-table-mapping-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
//tag::entity-pojo-table-mapping-example[]
}
//end::entity-pojo-table-mapping-example[]
}

View File

@ -0,0 +1,236 @@
/*
* 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.identifier;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Vlad Mihalcea
*/
public class SimpleEntityTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Library.class,
Book.class
};
}
@Before
public void init() {
doInJPA( this::entityManagerFactory, entityManager -> {
Library library = new Library();
library.setId( 1L );
library.setName( "Amazon" );
entityManager.persist( library );
Book book = new Book();
book.setId( 1L );
book.setTitle( "High-Performance Java Persistence" );
book.setAuthor( "Vlad Mihalcea" );
entityManager.persist( book );
} );
}
@Test
public void testIdentityScope() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entity-pojo-identity-scope-example[]
Book book1 = entityManager.find( Book.class, 1L );
Book book2 = entityManager.find( Book.class, 1L );
assertTrue( book1 == book2 );
//end::entity-pojo-identity-scope-example[]
} );
}
@Test
public void testSetIdentityScope() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entity-pojo-set-identity-scope-example[]
Library library = entityManager.find( Library.class, 1L );
Book book1 = entityManager.find( Book.class, 1L );
Book book2 = entityManager.find( Book.class, 1L );
library.getBooks().add( book1 );
library.getBooks().add( book2 );
assertEquals( 1, library.getBooks().size() );
//end::entity-pojo-set-identity-scope-example[]
} );
}
@Test
public void testMultiSessionIdentityScope() {
//tag::entity-pojo-multi-session-identity-scope-example[]
Book book1 = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.find( Book.class, 1L );
} );
Book book2 = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.find( Book.class, 1L );
} );
assertFalse( book1 == book2 );
//end::entity-pojo-multi-session-identity-scope-example[]
}
@Test
public void testMultiSessionSetIdentityScope() {
Book book1 = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.find( Book.class, 1L );
} );
Book book2 = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.find( Book.class, 1L );
} );
//tag::entity-pojo-multi-session-set-identity-scope-example[]
doInJPA( this::entityManagerFactory, entityManager -> {
Set<Book> books = new HashSet<>();
books.add( book1 );
books.add( book2 );
assertEquals( 2, books.size() );
} );
//end::entity-pojo-multi-session-set-identity-scope-example[]
}
@Test
public void testTransientSetIdentityScope() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entity-pojo-transient-set-identity-scope-example[]
Library library = entityManager.find( Library.class, 1L );
Book book1 = new Book();
book1.setId( 100L );
book1.setTitle( "High-Performance Java Persistence" );
Book book2 = new Book();
book2.setId( 101L );
book2.setTitle( "Java Persistence with Hibernate" );
library.getBooks().add( book1 );
library.getBooks().add( book2 );
assertEquals( 2, library.getBooks().size() );
//end::entity-pojo-transient-set-identity-scope-example[]
} );
}
//tag::entity-pojo-set-mapping-example[]
@Entity(name = "Library")
public static class Library {
@Id
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "book_id")
private Set<Book> books = new HashSet<>();
//Getters and setters are omitted for brevity
//end::entity-pojo-set-mapping-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 Set<Book> getBooks() {
return books;
}
//tag::entity-pojo-set-mapping-example[]
}
//end::entity-pojo-set-mapping-example[]
//tag::entity-pojo-mapping-example[]
@Entity(name = "Book")
public static class Book {
//tag::entity-pojo-identifier-mapping-example[]
@Id
private Long id;
//end::entity-pojo-identifier-mapping-example[]
private String title;
private String author;
//Getters and setters are omitted for brevity
//end::entity-pojo-mapping-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
//tag::entity-pojo-mapping-example[]
}
//end::entity-pojo-mapping-example[]
}