HHH-11186 - Add examples for all Hibernate annotations
Document more annotations: - @BatchSize - @Check
This commit is contained in:
parent
0a2bb3c811
commit
78f8619a7d
|
@ -624,6 +624,8 @@ However, if this annotation is used with either value="property" or value="field
|
|||
|
||||
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/BatchSize.html[`@BatchSize`] annotation is used to specify the size for batch loading the entries of a lazy collection.
|
||||
|
||||
See the <<chapters/fetching/Fetching.adoc#fetching-batch, Batch fetching>> section for more info.
|
||||
|
||||
[[annotations-hibernate-cache]]
|
||||
==== `@Cache`
|
||||
|
||||
|
@ -643,7 +645,9 @@ When combining both JPA and Hibernate `CascadeType` strategies, Hibernate will m
|
|||
[[annotations-hibernate-check]]
|
||||
==== `@Check`
|
||||
|
||||
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Check.html[`@Check`] annotation is used to specify an arbitrary SQL CHECK constraint which can be defined at the class, property or collection level.
|
||||
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Check.html[`@Check`] annotation is used to specify an arbitrary SQL CHECK constraint which can be defined at the class level.
|
||||
|
||||
See the <<chapters/schema/Schema.adoc#schema-generation-database-checks,Database-level checks>> chapter for more info.
|
||||
|
||||
[[annotations-hibernate-collectionid]]
|
||||
==== `@CollectionId`
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[[fetching]]
|
||||
== Fetching
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/fetching
|
||||
:extrasdir: extras
|
||||
|
||||
Fetching, essentially, is the process of grabbing data from the database and making it available to the application.
|
||||
Tuning how an application does fetching is one of the biggest factors in determining how an application will perform.
|
||||
|
@ -169,4 +170,51 @@ include::{sourcedir}/ProfileFetchingTest.java[tags=fetching-strategies-dynamic-f
|
|||
|
||||
Here the `Employee` is obtained by natural-id lookup and the Employee's `Project` data is fetched eagerly.
|
||||
If the `Employee` data is resolved from cache, the `Project` data is resolved on its own.
|
||||
However, if the `Employee` data is not resolved in cache, the `Employee` and `Project` data is resolved in one SQL query via join as we saw above.
|
||||
However, if the `Employee` data is not resolved in cache, the `Employee` and `Project` data is resolved in one SQL query via join as we saw above.
|
||||
|
||||
[[fetching-batch]]
|
||||
=== Batch fetching
|
||||
|
||||
Hibernate offers the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/BatchSize.html[`@BatchSize`] annotation,
|
||||
which can be used when fetching uninitialized entity proxies.
|
||||
|
||||
Considering the following entity mapping:
|
||||
|
||||
[[fetching-batch-mapping-example]]
|
||||
.`@BatchSize` mapping example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BatchFetchingTest.java[tags=fetching-batch-mapping-example]
|
||||
----
|
||||
====
|
||||
|
||||
Considering that we have previously fetched several `Department` entities,
|
||||
and now we need to initialize the `employees` entity collection for each particular `Department`,
|
||||
the `@BatchSize` annotations allows us to load multiple `Employee` entities in a single database roundtrip.
|
||||
|
||||
[[fetching-batch-fetching-example]]
|
||||
.`@BatchSize` fetching example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/BatchFetchingTest.java[tags=fetching-batch-fetching-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/fetching-batch-fetching-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
As you can see in the example above, there are only two SQL statements used to fetch the `Employee` entities associated to multiple `Department` entities.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
Without `@BatchSize`, you'd run into a N+1 query issue, so,
|
||||
instead of 2 SQL statements, there would be 10 queries needed for fetching the `Employee` child entities.
|
||||
|
||||
However, although `@BatchSize` is better than running into an N+1 query issue,
|
||||
most of the time, a DTO projection or a `JOIN FETCH` is a much better alternative since
|
||||
it allows you to fetch all the required data with a single query.
|
||||
====
|
|
@ -0,0 +1,33 @@
|
|||
SELECT
|
||||
d.id as id1_0_
|
||||
FROM
|
||||
Department d
|
||||
INNER JOIN
|
||||
Employee employees1_
|
||||
ON d.id=employees1_.department_id
|
||||
|
||||
SELECT
|
||||
e.department_id as departme3_1_1_,
|
||||
e.id as id1_1_1_,
|
||||
e.id as id1_1_0_,
|
||||
e.department_id as departme3_1_0_,
|
||||
e.name as name2_1_0_
|
||||
FROM
|
||||
Employee e
|
||||
WHERE
|
||||
e.department_id IN (
|
||||
0, 2, 3, 4, 5
|
||||
)
|
||||
|
||||
SELECT
|
||||
e.department_id as departme3_1_1_,
|
||||
e.id as id1_1_1_,
|
||||
e.id as id1_1_0_,
|
||||
e.department_id as departme3_1_0_,
|
||||
e.name as name2_1_0_
|
||||
FROM
|
||||
Employee e
|
||||
WHERE
|
||||
e.department_id IN (
|
||||
6, 7, 8, 9, 1
|
||||
)
|
|
@ -38,6 +38,7 @@ include::{extrasdir}/sql-schema-generation-domain-model-example.sql[]
|
|||
----
|
||||
====
|
||||
|
||||
[[schema-generation-script-files]]
|
||||
=== Importing script files
|
||||
|
||||
To customize the schema generation process, the `hibernate.hbm2ddl.import_files` configuration property must be used to provide other scripts files that Hibernate can use when the `SessionFactory` is started.
|
||||
|
@ -68,6 +69,7 @@ If we configure Hibernate to import the script above:
|
|||
|
||||
Hibernate is going to execute the script file after the schema is automatically generated.
|
||||
|
||||
[[schema-generation-database-objects]]
|
||||
=== Database objects
|
||||
|
||||
Hibernate allows you to customize the schema generation process via the HBM `database-object` element.
|
||||
|
@ -83,5 +85,40 @@ include::{sourcedir}/SchemaGenerationTest.hbm.xml[]
|
|||
----
|
||||
====
|
||||
|
||||
When the `SessionFactory` is bootstrapped, Hibernate is going to execute the `database-object`, therefore creating the `sp_count_books` funtion.
|
||||
When the `SessionFactory` is bootstrapped, Hibernate is going to execute the `database-object`, therefore creating the `sp_count_books` function.
|
||||
|
||||
[[schema-generation-database-checks]]
|
||||
=== Database-level checks
|
||||
|
||||
Hibernate offers the `@Check` annotation so that you can specify an arbitrary SQL CHECK constraint which can be defined as follows:
|
||||
|
||||
[[schema-generation-database-checks-example]]
|
||||
.Database check entity mapping example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/CheckTest.java[tag=schema-generation-database-checks-example]
|
||||
----
|
||||
====
|
||||
|
||||
Now, if you try to add a `Book` entity with an `isbn` attribute whose length is not 13 characters,
|
||||
a `ConstraintViolationException` is going to be thrown.
|
||||
|
||||
[[stag::schema-generation-database-checks-persist-example]]
|
||||
.Database check failure example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/CheckTest.java[tag=schema-generation-database-checks-persist-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/schema-generation-database-checks-persist-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
INSERT INTO Book (isbn, price, title, id)
|
||||
VALUES ('11-11-2016', 49.99, 'High-Performance Java Persistence', 1)
|
||||
|
||||
-- WARN SqlExceptionHelper:129 - SQL Error: 0, SQLState: 23514
|
||||
-- ERROR SqlExceptionHelper:131 - ERROR: new row for relation "book" violates check constraint "book_isbn_check"
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.fetching;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.annotations.BatchSize;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class BatchFetchingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger log = Logger.getLogger( BatchFetchingTest.class );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Department.class,
|
||||
Employee.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
for ( long i = 0; i < 10; i++ ) {
|
||||
Department department = new Department();
|
||||
department.id = i;
|
||||
entityManager.persist( department );
|
||||
|
||||
for ( int j = 0; j < Math.random() * 5; j++ ) {
|
||||
Employee employee = new Employee();
|
||||
employee.id = (i * 5) + j;
|
||||
employee.name = String.format( "John %d", employee.getId() );
|
||||
employee.department = department;
|
||||
entityManager.persist( employee );
|
||||
department.employees.add( employee );
|
||||
}
|
||||
entityManager.flush();
|
||||
}
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::fetching-batch-fetching-example[]
|
||||
List<Department> departments = entityManager.createQuery(
|
||||
"select d " +
|
||||
"from Department d " +
|
||||
"inner join d.employees e " +
|
||||
"where e.name like 'John%'", Department.class)
|
||||
.getResultList();
|
||||
|
||||
for ( Department department : departments ) {
|
||||
log.infof(
|
||||
"Department %d has {} employees",
|
||||
department.getId(),
|
||||
department.getEmployees().size()
|
||||
);
|
||||
}
|
||||
//end::fetching-batch-fetching-example[]
|
||||
} );
|
||||
}
|
||||
|
||||
//tag::fetching-batch-mapping-example[]
|
||||
@Entity(name = "Department")
|
||||
public static class Department {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@OneToMany(mappedBy = "department")
|
||||
//@BatchSize(size = 5)
|
||||
private List<Employee> employees = new ArrayList<>();
|
||||
|
||||
//Getters and setters omitted for brevity
|
||||
|
||||
//end::fetching-batch-mapping-example[]
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<Employee> getEmployees() {
|
||||
return employees;
|
||||
}
|
||||
//tag::fetching-batch-mapping-example[]
|
||||
}
|
||||
|
||||
@Entity(name = "Employee")
|
||||
public static class Employee {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
private String name;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private Department department;
|
||||
|
||||
//Getters and setters omitted for brevity
|
||||
//end::fetching-batch-mapping-example[]
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Department getDepartment() {
|
||||
return department;
|
||||
}
|
||||
//tag::fetching-batch-mapping-example[]
|
||||
}
|
||||
//end::fetching-batch-mapping-example[]
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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.schema;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.hibernate.annotations.Check;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.PostgreSQL81Dialect;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect( PostgreSQL81Dialect.class )
|
||||
public class CheckTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Book.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Book book = new Book();
|
||||
book.setId( 0L );
|
||||
book.setTitle( "Hibernate in Action" );
|
||||
book.setPrice( 49.99d );
|
||||
|
||||
entityManager.persist( book );
|
||||
} );
|
||||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::schema-generation-database-checks-persist-example[]
|
||||
Book book = new Book();
|
||||
book.setId( 1L );
|
||||
book.setPrice( 49.99d );
|
||||
book.setTitle( "High-Performance Java Persistence" );
|
||||
book.setIsbn( "11-11-2016" );
|
||||
|
||||
entityManager.persist( book );
|
||||
//end::schema-generation-database-checks-persist-example[]
|
||||
} );
|
||||
fail("Should fail because the ISBN is not of the right length!");
|
||||
}
|
||||
catch ( PersistenceException e ) {
|
||||
assertEquals( ConstraintViolationException.class, e.getCause().getCause().getClass() );
|
||||
}
|
||||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person();
|
||||
person.setId( 1L );
|
||||
person.setName( "John Doe" );
|
||||
person.setCode( 0L );
|
||||
|
||||
entityManager.persist( person );
|
||||
} );
|
||||
fail("Should fail because the code is 0!");
|
||||
}
|
||||
catch ( PersistenceException e ) {
|
||||
assertEquals( ConstraintViolationException.class, e.getCause().getCause().getClass() );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Check( constraints = "code > 0" )
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
// This one does not work! Only the entity-level annotation works.
|
||||
// @Check( constraints = "code > 0" )
|
||||
private Long code;
|
||||
|
||||
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 Long getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Long code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
//tag::schema-generation-database-checks-example[]
|
||||
@Entity(name = "Book")
|
||||
@Check( constraints = "CASE WHEN isbn IS NOT NULL THEN LENGTH(isbn) = 13 ELSE true END")
|
||||
public static class Book {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String title;
|
||||
|
||||
@NaturalId
|
||||
private String isbn;
|
||||
|
||||
private Double price;
|
||||
|
||||
//Getters and setters omitted for brevity
|
||||
|
||||
//end::schema-generation-database-checks-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 getIsbn() {
|
||||
return isbn;
|
||||
}
|
||||
|
||||
public void setIsbn(String isbn) {
|
||||
this.isbn = isbn;
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(Double price) {
|
||||
this.price = price;
|
||||
}
|
||||
//tag::schema-generation-database-checks-example[]
|
||||
}
|
||||
//end::schema-generation-database-checks-example[]
|
||||
}
|
Loading…
Reference in New Issue