Reputation: 705
I have a problem for filling auditing fields by using @PreUpdate and @PrePersist. For instance When I'd like to update a client entity, the field updatedBy and updatedAt are still null; despite when I debug, the code of preUpdate() which is annotated with @PreUpdate is executed.
Below the code of AuditingField which is responsible for creating/updating the auditing fields in each JPA entity:
@Embeddable
@Getter
@Setter
@NoArgsConstructor
public class FieldAuditing implements Serializable {
@Column(name = "DATE_CREATION", updatable = false)
private Instant createdAt;
@Column(name = "DATE_MODIFICATION", updatable = false)
private Instant updatedAt;
@Column(name = "DATE_SUPRESSION", updatable = false)
private Instant deletedAt;
@Column(name = "AUTEUR_CREATION", updatable = false, length = 100)
private String createdBy;
@Column(name = "AUTEUR_MODIFICATION", updatable = false, length = 100)
private String updatedBy;
@Column(name = "AUTEUR_SUPRESSION", updatable = false, length = 100)
private String deletedBy;
@Column(name = "IS_SUPPRIMER", nullable = false, updatable = false)
private boolean isDeleted;
@PrePersist
public void prePersist() {
setCreatedAt(Instant.now());
setCreatedBy(LoggedInUser.get());
}
@PreUpdate
public void preUpdate() {
setUpdatedAt(Instant.now());
setUpdatedBy(LoggedInUser.get());
}
@PreRemove
public void preRemove() {
setDeletedAt(Instant.now());
setDeleted(true);
setDeletedBy(LoggedInUser.get());
}
}
The client entity that contains embedded auditing fields:
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name="TF_CLIENT", schema="dbo")
public class Client implements Serializable {
private static final long serialVersionUID = 8832848102370267801L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator="native")
@GenericGenerator(name = "native", strategy = "native")
@Column(name = "CLT_ID", nullable = false)
private Long id;
@Column(name = "CLT_LIBELLE", nullable = false, length = 50, unique = true)
private String libelle;
@Temporal(TemporalType.DATE)
@Column(name = "CLT_DT_OUVERTURE", nullable = false)
private Date dateOuverture;
@Temporal(TemporalType.DATE)
@Column(name = "CLT_DT_FERMETURE")
private Date dateFermeture;
@Column(name = "CLT_B_ACTIF")
private boolean isActif;
@Embedded
private FieldAuditing fieldAuditing = new FieldAuditing() ;
//... rest of another attributes
}
The method that updates the client entity
private ClientDto save(ClientDto clientDto, Client client) {
startDateShouldBeBeforeEndDate(clientDto);
hasUniqueCodePaies(clientDto.getCodePaies());
Client clientSaved = clientRepository.save(clientMapper.toEntity(clientDto, client));
clientMapper.addOrRemoveClientActions(clientDto, clientSaved);
clientMapper.addOrRemoveClientEtats(clientDto, clientSaved);
clientRepository.save(clientSaved);
clientDto.setId(clientSaved.getId());
return clientDto;
}
And finally the persistence context configuration:
@Configuration
@PropertySource({"classpath:application.yml"})
@EnableJpaRepositories(
basePackages = "com.github.maaoutir.clientManager",
entityManagerFactoryRef = "mainEntityManager")
public class PersistenceContext {
private final Environment env;
public PersistenceContext(Environment env) {
this.env = env;
}
@Bean
@Primary
public DataSource mainDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Objects.requireNonNull(env.getProperty("spring.datasource.driverClassName")));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean mainEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(mainDataSource());
em.setPackagesToScan("com.github.maaoutir.clientManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
// properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("spring.jpa.hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
@Primary
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(mainEntityManager().getObject());
return transactionManager;
}
}
I'm thankful for any help.
Upvotes: 2
Views: 5449
Reputation: 11
Create a constructor in the class @Embeddable with super.
It worked for me.
public FieldAuditing(){
super();
}
Upvotes: 1
Reputation: 2948
You are using updatable=false
on those columns:
@Column(name = "DATE_MODIFICATION", updatable = false)
private Instant updatedAt;
@Column(name = "AUTEUR_MODIFICATION", updatable = false, length = 100)
private String updatedBy;
This means that JPA doesn't use this field to update the column. From the JPA spec for updatable
:
Whether the column is included in SQL UPDATE statements generated by the persistence provider.
This make sense for the createdBy
or createdAt
columns, that are set on @PrePersist and persisted with the first INSERT, and you don't want them to be modified afterwards. But columns updated in @PreUpdate (or @PreRemove) wont be updated with the UPDATE statement if updatable
is set to false
Upvotes: 2
Reputation: 86627
Check if you have @EnableJpaAuditing
on your configuration, and @EntityListeners(AuditingEntityListener.class)
on your entity class.
Upvotes: 0