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:
parent
f9cb1d5cb4
commit
87ce2670e7
|
@ -1,7 +1,7 @@
|
|||
[[entity]]
|
||||
=== Entity types
|
||||
: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-persister: ../../../../../test/java/org/hibernate/userguide/persister
|
||||
: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>>.
|
||||
|
||||
.Identifier
|
||||
[[entity-pojo-identifier-mapping-example]]
|
||||
.Identifier mapping
|
||||
====
|
||||
[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.
|
||||
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]
|
||||
----
|
||||
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.
|
||||
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`
|
||||
====
|
||||
[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.
|
||||
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
|
||||
====
|
||||
[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
|
||||
====
|
||||
[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:
|
||||
|
||||
[[entity-pojo-multi-session-identity-scope-example]]
|
||||
.Mixed Sessions
|
||||
====
|
||||
[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]
|
||||
----
|
||||
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:
|
||||
|
||||
[[entity-pojo-transient-set-identity-scope-example]]
|
||||
.Sets with transient entities
|
||||
====
|
||||
[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.
|
||||
|
||||
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
|
||||
====
|
||||
[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]
|
||||
----
|
||||
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.
|
||||
`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.
|
||||
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`:
|
||||
|
||||
.Forcing identifier generation
|
||||
[[entity-pojo-naive-equals-hashcode-persist-force-flush-example]]
|
||||
.Forcing the flush before adding to the Set
|
||||
====
|
||||
[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.
|
||||
|
||||
.Better equals/hashCode with natural-id
|
||||
[[entity-pojo-natural-id-equals-hashcode-example]]
|
||||
.Natural Id equals/hashCode
|
||||
====
|
||||
[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]
|
||||
----
|
||||
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]
|
||||
----
|
||||
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]
|
||||
----
|
||||
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]
|
||||
----
|
||||
====
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
@Id
|
||||
private Integer id;
|
|
@ -1,4 +0,0 @@
|
|||
@Entity
|
||||
public class Simple {
|
||||
...
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
@Entity
|
||||
@Table( catalog = "CRM", schema = "purchasing", name = "t_simple" )
|
||||
public class Simple {
|
||||
...
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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 );
|
|
@ -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 );
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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[]
|
||||
}
|
|
@ -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[]
|
||||
}
|
|
@ -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[]
|
||||
}
|
|
@ -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[]
|
||||
}
|
Loading…
Reference in New Issue