Migrate User Guide Events chapter extras to test folder
This commit is contained in:
parent
ff3b54a962
commit
3642bb63cd
|
@ -1,6 +1,6 @@
|
|||
[[events]]
|
||||
== Interceptors and events
|
||||
:sourcedir: extras
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/events
|
||||
|
||||
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.
|
||||
|
@ -12,8 +12,15 @@ The `org.hibernate.Interceptor` interface provides callbacks from the session to
|
|||
allowing the application to inspect and/or manipulate properties of a persistent object before it is saved, updated, deleted or loaded.
|
||||
|
||||
One possible use for this is to track auditing information.
|
||||
The following example shows an `Interceptor` implementation that automatically sets the `createTimestamp` property when an `Auditable` entity is created,
|
||||
and it updates the `lastUpdateTimestamp` property when an Auditable entity is updated.
|
||||
The following example shows an `Interceptor` implementation that automatically logs when an entity is updated.
|
||||
|
||||
[[events-interceptors-example]]
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/InterceptorTest.java[tags=events-interceptors-example]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
|
@ -24,10 +31,11 @@ An Interceptor can be either `Session`-scoped or `SessionFactory`-scoped.
|
|||
|
||||
A Session-scoped interceptor is specified when a session is opened.
|
||||
|
||||
[[events-interceptors-session-scope-example]]
|
||||
====
|
||||
[source,java]
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SessionScopedExample.java[]
|
||||
include::{sourcedir}/InterceptorTest.java[tags=events-interceptors-session-scope-example]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -36,10 +44,11 @@ Unless a session is opened explicitly specifying the interceptor to use, the `Se
|
|||
`SessionFactory`-scoped interceptors must be thread safe.
|
||||
Ensure that you do not store session-specific states, since multiple sessions will use this interceptor potentially concurrently.
|
||||
|
||||
[[events-interceptors-session-factory-scope-example]]
|
||||
====
|
||||
[source,java]
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SessionFactoryScopedExample.java[]
|
||||
include::{sourcedir}/InterceptorTest.java[tags=events-interceptors-session-factory-scope-example]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -65,14 +74,16 @@ A custom listener implements the appropriate interface for the event it wants to
|
|||
(or even the default event listeners used by Hibernate out-of-the-box as these are declared non-final for this purpose).
|
||||
Here is an example of a custom load event listener:
|
||||
|
||||
[[events-interceptors-load-listener-example]]
|
||||
.Custom LoadListener example
|
||||
====
|
||||
[source,java]
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/LoadListenerExample.java[]
|
||||
include::{sourcedir}/ListenerTest.java[tags=events-interceptors-load-listener-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[events-declarative-security]]
|
||||
=== Hibernate declarative security
|
||||
|
||||
Usually, declarative security in Hibernate applications is managed in a session facade layer.
|
||||
|
@ -84,16 +95,18 @@ Again, see <<chapters/bootstrap/Bootstrap.adoc#bootstrap-event-listener-registra
|
|||
|
||||
Below is an example of an appropriate `org.hibernate.integrator.spi.Integrator` implementation for this purpose.
|
||||
|
||||
[[events-declarative-security-jacc-example]]
|
||||
.JACC listener registration example
|
||||
====
|
||||
[source,java]
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/jacc-event-reg-example.java[]
|
||||
include::{sourcedir}/ListenerTest.java[tags=events-declarative-security-jacc-example]
|
||||
----
|
||||
====
|
||||
|
||||
You must also decide how to configure your JACC provider. Consult your JACC provider documentation.
|
||||
|
||||
[[events-jpa-callbacks]]
|
||||
=== JPA Callbacks
|
||||
|
||||
JPA also defines a more limited set of callbacks through annotations.
|
||||
|
@ -111,7 +124,7 @@ JPA also defines a more limited set of callbacks through annotations.
|
|||
|@PostLoad |Executed after an entity has been loaded into the current persistence context or an entity has been refreshed.
|
||||
|=======================================================================
|
||||
|
||||
There are 2 available approaches defined for specifying callback handling:
|
||||
There are two available approaches defined for specifying callback handling:
|
||||
|
||||
* The first approach is to annotate methods on the entity itself to receive notification of particular entity life cycle event(s).
|
||||
* The second is to use a separate entity listener class.
|
||||
|
@ -119,11 +132,12 @@ An entity listener is a stateless class with a no-arg constructor.
|
|||
The callback annotations are placed on a method of this class instead of the entity class.
|
||||
The entity listener class is then associated with the entity using the `javax.persistence.EntityListeners` annotation
|
||||
|
||||
[[events-jpa-callbacks-example]]
|
||||
.Example of specifying JPA callbacks
|
||||
====
|
||||
[source,java]
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/JpaCallbacksExample.java[]
|
||||
include::{sourcedir}/ListenerTest.java[tags=events-jpa-callbacks-example]
|
||||
----
|
||||
====
|
||||
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
import org.hibernate.EmptyInterceptor;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
public class AuditInterceptor extends EmptyInterceptor {
|
||||
|
||||
private int updates;
|
||||
|
||||
private int creates;
|
||||
|
||||
private int loads;
|
||||
|
||||
public void onDelete(Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public boolean onFlushDirty(Object entity,
|
||||
Serializable id,
|
||||
Object[] currentState,
|
||||
Object[] previousState,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
|
||||
if ( entity instanceof Auditable ) {
|
||||
updates++;
|
||||
for ( int i = 0; i < propertyNames.length; i++ ) {
|
||||
if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
|
||||
currentState[i] = new Date();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onLoad(Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
if ( entity instanceof Auditable ) {
|
||||
loads++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onSave(Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
|
||||
if ( entity instanceof Auditable ) {
|
||||
creates++;
|
||||
for ( int i = 0; i < propertyNames.length; i++ ) {
|
||||
if ( "createTimestamp".equals( propertyNames[i] ) ) {
|
||||
state[i] = new Date();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void afterTransactionCompletion( Transaction tx ) {
|
||||
if ( tx.wasCommitted() ) {
|
||||
System.out.println( "Creations: " + creates + ", Updates: " + updates + "Loads: " + loads );
|
||||
}
|
||||
updates = 0;
|
||||
creates = 0;
|
||||
loads = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
@Entity
|
||||
@EntityListeners( LastUpdateListener.class )
|
||||
public class Cat {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
private Calendar dateOfBirth;
|
||||
|
||||
@Transient
|
||||
private int age;
|
||||
|
||||
private Date lastUpdate;
|
||||
//getters and setters
|
||||
|
||||
/**
|
||||
* Set my transient property at load time based on a calculation,
|
||||
* note that a native Hibernate formula mapping is better for this purpose.
|
||||
*/
|
||||
@PostLoad
|
||||
public void calculateAge() {
|
||||
Calendar birth = new GregorianCalendar();
|
||||
birth.setTime( dateOfBirth );
|
||||
Calendar now = new GregorianCalendar();
|
||||
now.setTime( new Date() );
|
||||
int adjust = 0;
|
||||
if ( now.get( Calendar.DAY_OF_YEAR ) - birth.get( Calendar.DAY_OF_YEAR ) & lt;
|
||||
0){
|
||||
adjust = -1;
|
||||
}
|
||||
age = now.get( Calendar.YEAR ) - birth.get( Calendar.YEAR ) + adjust;
|
||||
}
|
||||
}
|
||||
|
||||
public class LastUpdateListener {
|
||||
/**
|
||||
* automatic property set before any database persistence
|
||||
*/
|
||||
@PreUpdate
|
||||
@PrePersist
|
||||
public void setLastUpdate( Cat o ) {
|
||||
o.setLastUpdate( new Date() );
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
public class LoadListenerExample implements LoadEventListener {
|
||||
// this is the single method defined by the LoadEventListener interface
|
||||
public void onLoad( LoadEvent event, LoadEventListener.LoadType loadType )
|
||||
throws HibernateException {
|
||||
if (!MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() )) {
|
||||
throw MySecurityException( "Unauthorized access" );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
SessionFactory sessionFactory = new Configuration()
|
||||
.setInterceptor( new AuditInterceptor() )
|
||||
...
|
||||
.buildSessionFactory();
|
|
@ -1 +0,0 @@
|
|||
Session session = sf.withOptions().interceptor( new AuditInterceptor() ).openSession();
|
|
@ -1,42 +0,0 @@
|
|||
import org.hibernate.event.service.spi.DuplicationStrategy;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.integrator.spi.Integrator;
|
||||
import org.hibernate.secure.internal.*;
|
||||
|
||||
public class JaccEventListenerIntegrator implements Integrator {
|
||||
|
||||
private static final DuplicationStrategy JACC_DUPLICATION_STRATEGY = new DuplicationStrategy() {
|
||||
|
||||
@Override
|
||||
public boolean areMatch( Object listener, Object original ) {
|
||||
return listener.getClass().equals( original.getClass() ) &&
|
||||
JACCSecurityListener.class.isInstance( original );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
return Action.KEEP_ORIGINAL;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public void integrate(
|
||||
Configuration configuration,
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
SessionFactoryServiceRegistry serviceRegistry) {
|
||||
boolean isSecurityEnabled = configuration.getProperties().containsKey( AvailableSettings.JACC_ENABLED );
|
||||
if (!isSecurityEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
|
||||
eventListenerRegistry.addDuplicationStrategy( JACC_DUPLICATION_STRATEGY );
|
||||
|
||||
final String jaccContextId = configuration.getProperty( Environment.JACC_CONTEXTID );
|
||||
eventListenerRegistry.prependListeners( EventType.PRE_DELETE, new JACCPreDeleteEventListener( jaccContextId ) );
|
||||
eventListenerRegistry.prependListeners( EventType.PRE_INSERT, new JACCPreInsertEventListener( jaccContextId ) );
|
||||
eventListenerRegistry.prependListeners( EventType.PRE_UPDATE, new JACCPreUpdateEventListener( jaccContextId ) );
|
||||
eventListenerRegistry.prependListeners( EventType.PRE_LOAD, new JACCPreLoadEventListener( jaccContextId ) );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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 java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.EmptyInterceptor;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InterceptorTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger( InterceptorTest.class );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Customer.class
|
||||
};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( new Customer( "John Doe" ) );
|
||||
Customer customer = new Customer();
|
||||
entityManager.persist( customer );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionInterceptor() {
|
||||
EntityManagerFactory entityManagerFactory = entityManagerFactory();
|
||||
Serializable customerId = 1L;
|
||||
//tag::events-interceptors-session-scope-example[]
|
||||
SessionFactory sessionFactory = entityManagerFactory.unwrap( SessionFactory.class );
|
||||
Session session = sessionFactory
|
||||
.withOptions()
|
||||
.interceptor(new LoggingInterceptor() )
|
||||
.openSession();
|
||||
|
||||
Customer customer = session.get( Customer.class, customerId );
|
||||
customer.setName( "Mr. John Doe" );
|
||||
//Entity Customer#1 changed from [John Doe, 0] to [Mr. John Doe, 0]
|
||||
session.flush();
|
||||
//end::events-interceptors-session-scope-example[]
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionFactoryInterceptor() {
|
||||
|
||||
Serializable customerId = 1L;
|
||||
//tag::events-interceptors-session-factory-scope-example[]
|
||||
SessionFactory sessionFactory = new MetadataSources( new StandardServiceRegistryBuilder().build() )
|
||||
.addAnnotatedClass( Customer.class )
|
||||
.getMetadataBuilder()
|
||||
.build()
|
||||
.getSessionFactoryBuilder()
|
||||
.applyInterceptor( new LoggingInterceptor() )
|
||||
.build();
|
||||
//end::events-interceptors-session-factory-scope-example[]
|
||||
Session session = sessionFactory.openSession();
|
||||
|
||||
Customer customer = session.get( Customer.class, customerId );
|
||||
customer.setName( "Mr. John Doe" );
|
||||
//Entity Customer#1 changed from [John Doe, 0] to [Mr. John Doe, 0]
|
||||
session.flush();
|
||||
session.close();
|
||||
sessionFactory.close();
|
||||
}
|
||||
|
||||
@Entity(name = "Customer")
|
||||
public static class Customer {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Customer() {
|
||||
}
|
||||
|
||||
public Customer(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
//tag::events-interceptors-example[]
|
||||
public static class LoggingInterceptor extends EmptyInterceptor {
|
||||
@Override
|
||||
public boolean onFlushDirty(
|
||||
Object entity,
|
||||
Serializable id,
|
||||
Object[] currentState,
|
||||
Object[] previousState,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
LOGGER.debugv( "Entity {0}#{1} changed from {2} to {3}",
|
||||
entity.getClass().getSimpleName(),
|
||||
id,
|
||||
Arrays.toString( previousState ),
|
||||
Arrays.toString( currentState )
|
||||
);
|
||||
return super.onFlushDirty( entity, id, currentState,
|
||||
previousState, propertyNames, types
|
||||
);
|
||||
}
|
||||
}
|
||||
//end::events-interceptors-example[]
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* 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 java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.PrePersist;
|
||||
import javax.persistence.PreUpdate;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.event.service.spi.DuplicationStrategy;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
import org.hibernate.event.spi.LoadEvent;
|
||||
import org.hibernate.event.spi.LoadEventListener;
|
||||
import org.hibernate.integrator.spi.ServiceContributingIntegrator;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.secure.internal.DisabledJaccServiceImpl;
|
||||
import org.hibernate.secure.internal.JaccPreDeleteEventListener;
|
||||
import org.hibernate.secure.internal.JaccPreInsertEventListener;
|
||||
import org.hibernate.secure.internal.JaccPreLoadEventListener;
|
||||
import org.hibernate.secure.internal.JaccPreUpdateEventListener;
|
||||
import org.hibernate.secure.internal.JaccSecurityListener;
|
||||
import org.hibernate.secure.internal.StandardJaccServiceImpl;
|
||||
import org.hibernate.secure.spi.GrantedPermission;
|
||||
import org.hibernate.secure.spi.IntegrationException;
|
||||
import org.hibernate.secure.spi.JaccPermissionDeclarations;
|
||||
import org.hibernate.secure.spi.JaccService;
|
||||
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class ListenerTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Customer.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test(expected = SecurityException.class)
|
||||
public void testLoadListener() {
|
||||
Serializable customerId = 1L;
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::events-interceptors-load-listener-example[]
|
||||
EntityManagerFactory entityManagerFactory = entityManagerFactory();
|
||||
SessionFactoryImplementor sessionFactory = entityManagerFactory.unwrap( SessionFactoryImplementor.class );
|
||||
sessionFactory
|
||||
.getServiceRegistry()
|
||||
.getService( EventListenerRegistry.class )
|
||||
.prependListeners( EventType.LOAD, new SecuredLoadEntityListener() );
|
||||
|
||||
Customer customer = entityManager.find( Customer.class, customerId );
|
||||
//end::events-interceptors-load-listener-example[]
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJPACallback() {
|
||||
Long personId = 1L;
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person();
|
||||
person.id = personId;
|
||||
person.name = "John Doe";
|
||||
person.dateOfBirth = Timestamp.valueOf(LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ));
|
||||
entityManager.persist( person );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = entityManager.find( Person.class, personId );
|
||||
assertTrue(person.age > 0);
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Customer")
|
||||
public static class Customer {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Customer() {
|
||||
}
|
||||
|
||||
public Customer(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
//tag::events-jpa-callbacks-example[]
|
||||
@Entity
|
||||
@EntityListeners( LastUpdateListener.class )
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private Date dateOfBirth;
|
||||
|
||||
@Transient
|
||||
private long age;
|
||||
|
||||
private Date lastUpdate;
|
||||
|
||||
public void setLastUpdate(Date lastUpdate) {
|
||||
this.lastUpdate = lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transient property at load time based on a calculation.
|
||||
* Note that a native Hibernate formula mapping is better for this purpose.
|
||||
*/
|
||||
@PostLoad
|
||||
public void calculateAge() {
|
||||
age = ChronoUnit.YEARS.between( LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli( dateOfBirth.getTime()), ZoneOffset.UTC),
|
||||
LocalDateTime.now()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LastUpdateListener {
|
||||
|
||||
@PreUpdate
|
||||
@PrePersist
|
||||
public void setLastUpdate( Person p ) {
|
||||
p.setLastUpdate( new Date() );
|
||||
}
|
||||
}
|
||||
//end::events-jpa-callbacks-example[]
|
||||
|
||||
//tag::events-interceptors-example[]
|
||||
public static class SecuredLoadEntityListener implements LoadEventListener {
|
||||
// this is the single method defined by the LoadEventListener interface
|
||||
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
|
||||
throws HibernateException {
|
||||
if ( !Principal.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
|
||||
throw new SecurityException( "Unauthorized access" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//end::events-Principal-example[]
|
||||
public static class Principal {
|
||||
public static boolean isAuthorized(String clazz, Serializable id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//tag::events-declarative-security-jacc-example[]
|
||||
public static class JaccIntegrator implements ServiceContributingIntegrator {
|
||||
|
||||
private static final Logger log = Logger.getLogger( JaccIntegrator.class );
|
||||
|
||||
private static final DuplicationStrategy DUPLICATION_STRATEGY =
|
||||
new DuplicationStrategy() {
|
||||
@Override
|
||||
public boolean areMatch(Object listener, Object original) {
|
||||
return listener.getClass().equals( original.getClass() ) &&
|
||||
JaccSecurityListener.class.isInstance( original );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
return Action.KEEP_ORIGINAL;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void prepareServices(
|
||||
StandardServiceRegistryBuilder serviceRegistryBuilder) {
|
||||
boolean isSecurityEnabled = serviceRegistryBuilder
|
||||
.getSettings().containsKey( AvailableSettings.JACC_ENABLED );
|
||||
final JaccService jaccService = isSecurityEnabled ?
|
||||
new StandardJaccServiceImpl() : new DisabledJaccServiceImpl();
|
||||
serviceRegistryBuilder.addService( JaccService.class, jaccService );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void integrate(
|
||||
Metadata metadata,
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
SessionFactoryServiceRegistry serviceRegistry) {
|
||||
doIntegration(
|
||||
serviceRegistry
|
||||
.getService( ConfigurationService.class ).getSettings(),
|
||||
// pass no permissions here, because atm actually injecting the
|
||||
// permissions into the JaccService is handled on SessionFactoryImpl via
|
||||
// the org.hibernate.boot.cfgxml.spi.CfgXmlAccessService
|
||||
null,
|
||||
serviceRegistry
|
||||
);
|
||||
}
|
||||
|
||||
private void doIntegration(
|
||||
Map properties,
|
||||
JaccPermissionDeclarations permissionDeclarations,
|
||||
SessionFactoryServiceRegistry serviceRegistry) {
|
||||
boolean isSecurityEnabled = properties
|
||||
.containsKey( AvailableSettings.JACC_ENABLED );
|
||||
if ( ! isSecurityEnabled ) {
|
||||
log.debug( "Skipping JACC integration as it was not enabled" );
|
||||
return;
|
||||
}
|
||||
|
||||
final String contextId = (String) properties
|
||||
.get( AvailableSettings.JACC_CONTEXT_ID );
|
||||
if ( contextId == null ) {
|
||||
throw new IntegrationException( "JACC context id must be specified" );
|
||||
}
|
||||
|
||||
final JaccService jaccService = serviceRegistry
|
||||
.getService( JaccService.class );
|
||||
if ( jaccService == null ) {
|
||||
throw new IntegrationException( "JaccService was not set up" );
|
||||
}
|
||||
|
||||
if ( permissionDeclarations != null ) {
|
||||
for ( GrantedPermission declaration : permissionDeclarations
|
||||
.getPermissionDeclarations() ) {
|
||||
jaccService.addPermission( declaration );
|
||||
}
|
||||
}
|
||||
|
||||
final EventListenerRegistry eventListenerRegistry =
|
||||
serviceRegistry.getService( EventListenerRegistry.class );
|
||||
eventListenerRegistry.addDuplicationStrategy( DUPLICATION_STRATEGY );
|
||||
|
||||
eventListenerRegistry.prependListeners(
|
||||
EventType.PRE_DELETE, new JaccPreDeleteEventListener() );
|
||||
eventListenerRegistry.prependListeners(
|
||||
EventType.PRE_INSERT, new JaccPreInsertEventListener() );
|
||||
eventListenerRegistry.prependListeners(
|
||||
EventType.PRE_UPDATE, new JaccPreUpdateEventListener() );
|
||||
eventListenerRegistry.prependListeners(
|
||||
EventType.PRE_LOAD, new JaccPreLoadEventListener() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disintegrate(SessionFactoryImplementor sessionFactory,
|
||||
SessionFactoryServiceRegistry serviceRegistry) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
//end::events-declarative-security-jacc-example[]
|
||||
}
|
|
@ -49,3 +49,4 @@ log4j.logger.org.hibernate.tool.hbm2ddl=info
|
|||
### provide information about merged entity copies.
|
||||
#log4j.logger.org.hibernate.event.internal.EntityCopyAllowedLoggedObserver=warn
|
||||
|
||||
log4j.logger.org.hibernate.userguide=debug
|
||||
|
|
Loading…
Reference in New Issue