I'm trying to get Spring Data Auditing to work in my Spring 3.2.8 / Spring Data 1.5 / Hibernate 4 project.
As per the Spring Data Auditing docs, I've added the @CreatedBy
, etc annotations to my entities, created by AuditorAware
implementation, and instantiated it from within my JavaConfig. However, it never seems to fire.
I find the docs a little confusing. It appears that the JavaConfig entry replaces the xml entry, but I am not sure.
I don't currently have any orm.xml
file in my application. To be entirely honest, I'm not even sure where/how to configure it, or why I need it. All my entities are using annotations. I have tried adding @EntityListeners(AuditingEntityListener.class) to the entity, but that has not helped.
My current entity manager is defined without a persistence.xml file:
<!-- entity manager -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
<property name="packagesToScan" value="com.ia.domain"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.query.substitutions">true '1', false '0'</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
</props>
</property>
</bean>
JavaConfig:
@Configuration
@EnableJpaAuditing
public class AuditConfig {
@Bean
public AuditorAware<User> auditorProvider(){
return new SpringSecurityAuditorAware();
}
}
Entity:
@EntityListeners({AuditingEntityListener.class})
@Entity
public class User
{
@TableGenerator(name="UUIDGenerator", pkColumnValue="user_id", table="uuid_generator", allocationSize=1)
@Id
@GeneratedValue(strategy=GenerationType.TABLE, generator="UUIDGenerator")
@Column(name="id")
private Long id;
@NotNull
private String username;
@CreatedDate
@NotNull
@Temporal(TemporalType.TIMESTAMP)
@Column(name="created_date", nullable=false)
private Date createdDate;
@LastModifiedDate
@NotNull
@Temporal(TemporalType.TIMESTAMP)
@Column(name="last_modified_date", nullable=false)
private Date lastModifiedDate;
@CreatedBy
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="created_by")
private User createdBy;
@LastModifiedBy
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="last_modified_by")
private User lastModifiedBy;
private String password;
private Boolean enabled;
...
}
I've put a breakpoint in my SpringSecurityAuditorAware
class but it is never being hit.
Do I still need an orm.xml file? How/where is this referenced from the EntityManager?
As of JPA 2.0, it is not possible to define such entity listener without an XML file (orm.xml
).
Default entity listeners—entity listeners that apply to all entities in the persistence unit—can be specified by means of the XML descriptor. (p.93)
If all entities in your project extends an AbstractAuditable
superclass then you can put @EntityListeners({AuditingEntityListener.class})
on AbstractAuditable
. Listeners attached to an entity class are inherited by its subclasses.
Multiple entity classes and mapped superclasses in an inheritance hierarchy may define listener classes and/or lifecycle callback methods directly on the class. (p.93)
Note that a subclass can exclude explicitly an inherited listener using the @ExcludeSuperclassListeners
annotation.
There is one last interesting footnote from the spec I'd like to quote:
Excluded listeners may be reintroduced on an entity class by listing them explicitly in the EntityListeners annotation or XML entity-listeners element. (Footnote [45] p.97)
Here is some code for illustrating the workaround:
AbstractAuditableEntity.java
import java.util.Date;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@MappedSuperclass
@EntityListeners({AuditingEntityListener.class}) // AuditingEntityListener will also audit any subclasses of AbstractAuditable...
public abstract class AbstractAuditableEntity {
@Id
@GeneratedValue
private Long id;
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
}
MyEntity.java
@Entity
public abstract class MyEntity extends AbstractAuditableEntity {
}
I think an interface Auditable
may be used (@EntityListeners
can appear on an interface) instead of an AbstractAuditable
class but I didn't try...