prepersist example audited spring jpa dependency-injection spring-roo entitylisteners

example - jpa auditing spring boot



Inyectando una dependencia de Spring en un JPA EntityListener (7)

¿Y qué hay con esta solución?

@MappedSuperclass @EntityListeners(AbstractEntityListener.class) public abstract class AbstractEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; @Column(name = "creation_date") private Date creationDate; @Column(name = "modification_date") private Date modificationDate; }

Entonces el oyente ...

@Component public class AbstractEntityListener { @Autowired private DateTimeService dateTimeService; @PreUpdate public void preUpdate(AbstractEntity abstractEntity) { AutowireHelper.autowire(this, this.dateTimeService); abstractEntity.setModificationDate(this.dateTimeService.getCurrentDate()); } @PrePersist public void prePersist(AbstractEntity abstractEntity) { AutowireHelper.autowire(this, this.dateTimeService); Date currentDate = this.dateTimeService.getCurrentDate(); abstractEntity.setCreationDate(currentDate); abstractEntity.setModificationDate(currentDate); } }

Y el ayudante ...

/** * Helper class which is able to autowire a specified class. It holds a static reference to the {@link org * .springframework.context.ApplicationContext}. */ public final class AutowireHelper implements ApplicationContextAware { private static final AutowireHelper INSTANCE = new AutowireHelper(); private static ApplicationContext applicationContext; private AutowireHelper() { } /** * Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired * are null. * * @param classToAutowire the instance of the class which holds @Autowire annotations * @param beansToAutowireInClass the beans which have the @Autowire annotation in the specified {#classToAutowire} */ public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) { for (Object bean : beansToAutowireInClass) { if (bean == null) { applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire); } } } @Override public void setApplicationContext(final ApplicationContext applicationContext) { AutowireHelper.applicationContext = applicationContext; } /** * @return the singleton instance. */ public static AutowireHelper getInstance() { return INSTANCE; } }

Funciona para mi.

Fuente: http://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/

Intento inyectar una dependencia de Spring en un JPA EntityListener . Aquí está mi clase de oyente:

@Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true) public class PliListener { @Autowired private EvenementPliRepository evenementPliRepository; @PostPersist void onPostPersist(Pli pli) { EvenementPli ev = new EvenementPli(); ev.setPli(pli); ev.setDateCreation(new Date()); ev.setType(TypeEvenement.creation); ev.setMessage("Création d''un pli"); System.out.println("evenementPliRepository: " + evenementPliRepository); evenementPliRepository.save(ev); } }

Aquí está mi clase de Entidad:

@RooJavaBean @RooToString @RooJpaActiveRecord @EntityListeners(PliListener.class) public class Pli implements Serializable{ ...

Sin embargo, mi dependencia (es decir, evenementPliRepository ) siempre es nula .

Alguien puede ayudarme porfavor?


Comencé a seguir el camino de usar AOP para inyectar un bean de primavera en un oyente de Entity. Después de un día y medio de investigación y probando diferentes cosas, encontré este link que decía:

No es posible inyectar beans administrados por resorte en una clase JPA EntityListener. Esto se debe a que el mecanismo del oyente JPA debe basarse en una clase sin estado, por lo que los métodos son efectivamente estáticos y no tienen en cuenta el contexto. ... Ninguna cantidad de AOP te salvará, nada se inyecta al ''objeto'' que representa al oyente, porque las implementaciones en realidad no crean instancias, sino que usan el método de clase.

En este punto, me reagrupé y tropecé con EclipseLink DescriptorEventAdapter . Usando esta información, creé una clase de oyente que extendía el Descriptor Adapter.

public class EntityListener extends DescriptorEventAdapter { private String injectedValue; public void setInjectedValue(String value){ this.injectedValue = value; } @Override public void aboutToInsert(DescriptorEvent event) { // Do what you need here } }

Para usar la clase, podría haber usado la anotación @EntityListeners en mi clase de entidad. Desafortunadamente, este método no le permitió a Spring controlar la creación de mi oyente y, como resultado, no permitió la inyección de la dependencia. En cambio, agregué la siguiente función ''init'' a mi clase:

public void init() { JpaEntityManager entityManager = null; try { // Create an entity manager for use in this function entityManager = (JpaEntityManager) entityManagerFactory.createEntityManager(); // Use the entity manager to get a ClassDescriptor for the Entity class ClassDescriptor desc = entityManager.getSession().getClassDescriptor(<EntityClass>.class); // Add this class as a listener to the class descriptor desc.getEventManager().addListener(this); } finally { if (entityManager != null) { // Cleanup the entity manager entityManager.close(); } } }

Agregue una pequeña configuración Spring XML

<!-- Define listener object --> <bean id="entityListener" class="EntityListener " init-method="init"> <property name="injectedValue" value="Hello World"/> <property name="entityManagerFactory" ref="emf"/> </bean>

Ahora tenemos una situación en la que Spring crea un oyente de entidad, lo inyecta con las dependencias necesarias, y el objeto oyente se registra con la clase de entidad a la que intenta escuchar.

Espero que esto ayude.



Esta es una vieja pregunta, pero encontré una solución alternativa:

public class MyEntityListener { @Autowired private ApplicationEventPublisher publisher; @PostPersist public void postPersist(MyEntity target) { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); publisher.publishEvent(new OnCreatedEvent<>(this, target)); } @PostUpdate public void postUpdate(MyEntity target) { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); publisher.publishEvent(new OnUpdatedEvent<>(this, target)); } @PostRemove public void postDelete(MyEntity target) { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); publisher.publishEvent(new OnDeletedEvent<>(this, target)); } }

Probablemente no sea el mejor, pero mejor que las variables estáticas sin AOP + weaving.


Otra opción:

Crea un servicio para hacer que AplicationContext sea accesible:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import lombok.Setter; @Service class ContextWrapper { @Setter private static ApplicationContext context; @Autowired public ContextWrapper(ApplicationContext ac) { setContext(ac); } public static ApplicationContext getContext() { return context; } }

Úselo:

... public class AuditListener { private static final String AUDIT_REPOSITORY = "AuditRepository"; @PrePersist public void beforePersist(Object object){ //TODO: } @PreUpdate public void beforeUpdate(Object object){ //TODO: } @PreRemove public void beforeDelete(Object object) { getRepo().save(getAuditElement("DEL",object)); } private Audit getAuditElement(String Operation,Object object){ Audit audit = new Audit(); audit.setActor("test"); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); audit.setDate(timestamp); return audit; } private AuditRepository getRepo(){ return ContextWrapper.getContext().getBean(AUDIT_REPOSITORY, AuditRepository.class); } }

Esta clase se crea como un oyente de jpa:

... @Entity @EntityListeners(AuditListener.class) @NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c") public class Customer implements Serializable { private static final long serialVersionUID = 1L; ...

Como el oyente no está bajo el control de Spring, no puede acceder al bean de contexto. He intentado varias opciones (@Configurable (...)) y ninguna ha funcionado excepto para crear una clase que tenga acceso estático al contexto. Ya en ese dilema, creo que esta es una opción elegante.


Probé el enfoque sugerido en https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/ y funcionó. No muy limpio, pero hace el trabajo. La clase AutowireHelper ligeramente modificada para mí se veía así:

import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class AutowireHelper implements ApplicationContextAware { private static ApplicationContext applicationContext; private AutowireHelper() { } public static void autowire(Object classToAutowire) { AutowireHelper.applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire); } @Override public void setApplicationContext(final ApplicationContext applicationContext) { AutowireHelper.applicationContext = applicationContext; } }

Luego llamé esto desde el oyente de la entidad de esta manera:

public class MyEntityAccessListener { @Autowired private MyService myService; @PostLoad public void postLoad(Object target) { AutowireHelper.autowire(this); myService.doThings(); ... } public void setMyService(MyService myService) { this.myService = myService; } }


Un truco para inyectar dependencias en beans sin estado, es definir la dependencia como "estática", crear un método setter para que Spring pueda inyectar la dependencia (asignándola a la dependencia estática).

Declara la dependencia como estática.

static private EvenementPliRepository evenementPliRepository;

Crea un método para que Spring pueda inyectarlo.

@Autowired public void init(EvenementPliRepository evenementPliRepository) { MyListenerClass.evenementPliRepository = evenementPliRepository; logger.info("Initializing with dependency ["+ evenementPliRepository +"]"); }

Más detalles en: http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html