HHH-11186 - Add examples for all Hibernate annotations

Document default entity listeners, @ExcludeDefaultListeners, and @ExcludeSuperclassListeners
This commit is contained in:
Vlad Mihalcea 2017-06-08 17:23:08 +03:00
parent 43f74be58e
commit 9c53bfdd73
9 changed files with 455 additions and 2 deletions

View File

@ -182,14 +182,14 @@ See the <<chapters/domain/basic_types.adoc#basic-enums-Enumerated, `@Enumerated`
The http://docs.oracle.com/javaee/7/api/javax/persistence/ExcludeDefaultListeners.html[`@ExcludeDefaultListeners`] annotation is used to specify that the current annotated entity skips the invocation of any default listener.
//TODO: Add example
See the <<chapters/events/Events.adoc#events-exclude-default-listener, Exclude default entity listeners>> section for more info.
[[annotations-jpa-excludesuperclasslisteners]]
==== `@ExcludeSuperclassListeners`
The http://docs.oracle.com/javaee/7/api/javax/persistence/ExcludeSuperclassListeners.html[`@ExcludeSuperclassListeners`] annotation is used to specify that the current annotated entity skips the invocation of listeners declared by its superclass.
//TODO: Add example
See the <<chapters/events/Events.adoc#events-exclude-default-listener, Exclude default entity listeners>> section for more info.
[[annotations-jpa-fieldresult]]
==== `@FieldResult`

View File

@ -1,6 +1,7 @@
[[events]]
== Interceptors and events
:sourcedir: ../../../../../test/java/org/hibernate/userguide/events
:extrasdir: extras
It is useful for the application to react to certain events that occur inside Hibernate.
This allows for the implementation of generic functionality and the extension of Hibernate functionality.
@ -177,3 +178,105 @@ See the `javax.persistence.ExcludeSuperclassListener`s annotation.
If a callback type is annotated on both an entity and one or more of its superclasses without method overriding, both would be called, the most general superclass first.
An entity class is also allowed to override a callback method defined in a superclass in which case the super callback would not get invoked; the overriding method would get invoked provided it is annotated.
[[events-default-listener]]
=== Default entity listeners
The JPA specification allows you to define a default entity listener which is going to be applied for every entity in that particular system.
Default entity listeners can only be defined in XML mapping files.
[[events-default-listener-mapping-example]]
.Default event listner mapping
====
[source, JAVA, indent=0]
----
include::{sourcedir}/DefaultEntityListener.java[tags=events-default-listener-mapping-example]
----
[source, XML, indent=0]
----
include::{sourcedir}/DefaultEntityListener-orm.xml[tags=events-default-listener-mapping-example]
----
====
Considering that all entities extend the `BaseEntity` class:
[source, JAVA, indent=0]
----
include::{sourcedir}/BaseEntity.java[tags=events-default-listener-mapping-example]
----
[source, JAVA, indent=0]
----
include::{sourcedir}/DefaultEntityListenerTest.java[tags=events-default-listener-mapping-example]
----
When persisting a `Person` or `Book` entity, the `createdOn` is going to be set by the `onPersist` method of the `DefaultEntityListener`.
[[events-default-listener-persist-example]]
.Default event listner persist event
====
[source, JAVA, indent=0]
----
include::{sourcedir}/DefaultEntityListenerTest.java[tags=events-default-listener-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/events-default-listener-persist-example.sql[]
----
====
When updating a `Person` or `Book` entity, the `updatedOn` is going to be set by the `onUpdate` method of the `DefaultEntityListener`.
[[events-default-listener-update-example]]
.Default event listner update event
====
[source, JAVA, indent=0]
----
include::{sourcedir}/DefaultEntityListenerTest.java[tags=events-default-listener-update-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/events-default-listener-update-example.sql[]
----
====
[[events-exclude-default-listener]]
==== Exclude default entity listeners
If you already registered a default entity listener, but you don't want to apply it to a particular entity,
you can use the
http://docs.oracle.com/javaee/7/api/javax/persistence/ExcludeDefaultListeners.html[`@ExcludeDefaultListeners`] and
http://docs.oracle.com/javaee/7/api/javax/persistence/ExcludeSuperclassListeners.html[`@ExcludeSuperclassListeners`] JPA annotations.
`@ExcludeDefaultListeners` instructs the current class to ignore the default entity listeners for the current entity
while `@ExcludeSuperclassListeners` is used to ignore the default entity listeners propagated to the `BaseEntity` super-class.
[[events-exclude-default-listener-mapping-example]]
.Exclude default event listner mapping
====
[source, JAVA, indent=0]
----
include::{sourcedir}/DefaultEntityListenerTest.java[tags=events-exclude-default-listener-mapping-example]
----
====
When persisting a `Publisher` entity,
the `createdOn` is not going to be set by the `onPersist` method of the `DefaultEntityListener`
because the `Publisher` entity was marked with the `@ExcludeDefaultListeners` and `@ExcludeSuperclassListeners` annotations.
[[events-exclude-default-listener-persist-example]]
.Excluding default event listner events
====
[source, JAVA, indent=0]
----
include::{sourcedir}/DefaultEntityListenerTest.java[tags=events-exclude-default-listener-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/events-exclude-default-listener-persist-example.sql[]
----
====

View File

@ -0,0 +1,24 @@
insert
into
Person
(createdOn, updatedOn, name, id)
values
(?, ?, ?, ?)
-- binding parameter [1] as [TIMESTAMP] - [2017-06-08 19:23:48.224]
-- binding parameter [2] as [TIMESTAMP] - [null]
-- binding parameter [3] as [VARCHAR] - [Vlad Mihalcea]
-- binding parameter [4] as [BIGINT] - [1]
insert
into
Book
(createdOn, updatedOn, author_id, title, id)
values
(?, ?, ?, ?, ?)
-- binding parameter [1] as [TIMESTAMP] - [2017-06-08 19:23:48.246]
-- binding parameter [2] as [TIMESTAMP] - [null]
-- binding parameter [3] as [BIGINT] - [1]
-- binding parameter [4] as [VARCHAR] - [High-Performance Java Persistence]
-- binding parameter [5] as [BIGINT] - [1]

View File

@ -0,0 +1,29 @@
update
Person
set
createdOn=?,
updatedOn=?,
name=?
where
id=?
-- binding parameter [1] as [TIMESTAMP] - [2017-06-08 19:23:48.224]
-- binding parameter [2] as [TIMESTAMP] - [2017-06-08 19:23:48.316]
-- binding parameter [3] as [VARCHAR] - [Vlad-Alexandru Mihalcea]
-- binding parameter [4] as [BIGINT] - [1]
update
Book
set
createdOn=?,
updatedOn=?,
author_id=?,
title=?
where
id=?
-- binding parameter [1] as [TIMESTAMP] - [2017-06-08 19:23:48.246]
-- binding parameter [2] as [TIMESTAMP] - [2017-06-08 19:23:48.317]
-- binding parameter [3] as [BIGINT] - [1]
-- binding parameter [4] as [VARCHAR] - [High-Performance Java Persistence 2nd Edition]
-- binding parameter [5] as [BIGINT] - [1]

View File

@ -0,0 +1,11 @@
insert
into
Publisher
(createdOn, updatedOn, name, id)
values
(?, ?, ?, ?)
-- binding parameter [1] as [TIMESTAMP] - [null]
-- binding parameter [2] as [TIMESTAMP] - [null]
-- binding parameter [3] as [VARCHAR] - [Amazon]
-- binding parameter [4] as [BIGINT] - [1]

View File

@ -0,0 +1,34 @@
package org.hibernate.userguide.events;
import java.sql.Timestamp;
import javax.persistence.MappedSuperclass;
/**
* @author Vlad Mihalcea
*/
//tag::events-default-listener-mapping-example[]
@MappedSuperclass
public abstract class BaseEntity {
private Timestamp createdOn;
private Timestamp updatedOn;
public Timestamp getCreatedOn() {
return createdOn;
}
void setCreatedOn(Timestamp createdOn) {
this.createdOn = createdOn;
}
public Timestamp getUpdatedOn() {
return updatedOn;
}
void setUpdatedOn(Timestamp updatedOn) {
this.updatedOn = updatedOn;
}
}
//end::events-default-listener-mapping-example[]

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<!--tag::events-default-listener-mapping-example[]-->
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"
version="2.1">
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener
class="org.hibernate.userguide.events.DefaultEntityListener">
<pre-persist method-name="onPersist"/>
<pre-update method-name="onUpdate"/>
</entity-listener>
</entity-listeners>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
<!--end::events-default-listener-mapping-example[]-->

View File

@ -0,0 +1,33 @@
package org.hibernate.userguide.events;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* @author Vlad Mihalcea
*/
//tag::events-default-listener-mapping-example[]
public class DefaultEntityListener {
public void onPersist(Object entity) {
if ( entity instanceof BaseEntity ) {
BaseEntity baseEntity = (BaseEntity) entity;
baseEntity.setCreatedOn( now() );
}
}
public void onUpdate(Object entity) {
if ( entity instanceof BaseEntity ) {
BaseEntity baseEntity = (BaseEntity) entity;
baseEntity.setUpdatedOn( now() );
}
}
private Timestamp now() {
return Timestamp.from(
LocalDateTime.now().toInstant( ZoneOffset.UTC )
);
}
}
//end::events-default-listener-mapping-example[]

View File

@ -0,0 +1,192 @@
/*
* 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.events;
import javax.persistence.Entity;
import javax.persistence.ExcludeDefaultListeners;
import javax.persistence.ExcludeSuperclassListeners;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static junit.framework.TestCase.assertNull;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class DefaultEntityListenerTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Book.class,
Publisher.class
};
}
@Override
protected String[] getMappings() {
return new String[] { "org/hibernate/userguide/events/DefaultEntityListener-orm.xml" };
}
@Test
public void test() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::events-default-listener-persist-example[]
Person author = new Person();
author.setId( 1L );
author.setName( "Vlad Mihalcea" );
entityManager.persist( author );
Book book = new Book();
book.setId( 1L );
book.setTitle( "High-Performance Java Persistence" );
book.setAuthor( author );
entityManager.persist( book );
//end::events-default-listener-persist-example[]
} );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::events-default-listener-update-example[]
Person author = entityManager.find( Person.class, 1L );
author.setName( "Vlad-Alexandru Mihalcea" );
Book book = entityManager.find( Book.class, 1L );
book.setTitle( "High-Performance Java Persistence 2nd Edition" );
//end::events-default-listener-update-example[]
} );
}
@Test
public void testExclude() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::events-exclude-default-listener-persist-example[]
Publisher publisher = new Publisher();
publisher.setId( 1L );
publisher.setName( "Amazon" );
entityManager.persist( publisher );
//end::events-exclude-default-listener-persist-example[]
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Publisher publisher = entityManager.find( Publisher.class, 1L );
assertNull(publisher.getCreatedOn());
} );
}
//tag::events-default-listener-mapping-example[]
@Entity(name = "Person")
public static class Person extends BaseEntity {
@Id
private Long id;
private String name;
//Getters and setters omitted for brevity
//end::events-default-listener-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;
}
//tag::events-default-listener-mapping-example[]
}
@Entity(name = "Book")
public static class Book extends BaseEntity {
@Id
private Long id;
private String title;
@ManyToOne
private Person author;
//Getters and setters omitted for brevity
//end::events-default-listener-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 Person getAuthor() {
return author;
}
public void setAuthor(Person author) {
this.author = author;
}
//tag::events-default-listener-mapping-example[]
}
//end::events-default-listener-mapping-example[]
//tag::events-exclude-default-listener-mapping-example[]
@Entity(name = "Publisher")
@ExcludeDefaultListeners
@ExcludeSuperclassListeners
public static class Publisher extends BaseEntity {
@Id
private Long id;
private String name;
//Getters and setters omitted for brevity
//end::events-exclude-default-listener-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;
}
//tag::events-exclude-default-listener-mapping-example[]
}
//end::events-exclude-default-listener-mapping-example[]
}