-
Notifications
You must be signed in to change notification settings - Fork 1.5k
AuditingEntityListener with Embeddable classes #2365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I crafted a test case and verified that indeed, @Entity
@EntityListeners(AuditingEntityListener.class)
public class AuditableEntity {
@Id
@GeneratedValue //
private Long id;
private String data;
@Embedded //
private AuditableEmbeddable auditDetails;
...
} @Embeddable
public class AuditableEmbeddable {
@CreatedDate //
private Instant dateCreated;
@LastModifiedDate //
private Instant dateUpdated;
...
} public interface AuditableEntityRepository extends JpaRepository<AuditableEntity, Long> {} @ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:auditing/auditing-entity-with-embeddable-listener.xml")
public class AuditingEntityWithEmbeddableListenerTests {
@Autowired AuditableEntityRepository repository;
private AuditableEntity entity;
private AuditableEmbeddable auditDetails;
@BeforeEach
void setUp() {
entity = new AuditableEntity();
entity.setId(1L);
entity.setData("original value");
auditDetails = new AuditableEmbeddable();
entity.setAuditDetails(auditDetails);
}
@Test
void auditsEmbeddedCorrectly() {
// when
repository.saveAndFlush(entity);
Optional<AuditableEntity> optionalEntity = repository.findById(1L);
// then
assertThat(optionalEntity).isNotEmpty();
AuditableEntity auditableEntity = optionalEntity.get();
assertThat(auditableEntity.getData()).isEqualTo("original value");
assertThat(auditableEntity.getAuditDetails().getDateCreated()).isNotNull();
assertThat(auditableEntity.getAuditDetails().getDateUpdated()).isNotNull();
Instant originalCreationDate = auditableEntity.getAuditDetails().getDateCreated();
Instant originalDateUpdated = auditableEntity.getAuditDetails().getDateUpdated();
auditableEntity.setData("updated value");
repository.saveAndFlush(auditableEntity);
Optional<AuditableEntity> optionalRevisedEntity = repository.findById(1L);
assertThat(optionalRevisedEntity).isNotEmpty();
AuditableEntity revisedEntity = optionalRevisedEntity.get();
assertThat(revisedEntity.getData()).isEqualTo("updated value");
assertThat(revisedEntity.getAuditDetails().getDateCreated()).isEqualTo(originalCreationDate);
assertThat(revisedEntity.getAuditDetails().getDateUpdated()).isAfter(originalDateUpdated);
}
} <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<import resource="../infrastructure.xml"/>
<jpa:auditing/>
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"/>
</beans> When I run this test case, it passes just fine, proving that the embeddable fields are populated and updated by the Something I'm missing? |
If there is no further comment, I will add these additional test cases to our test suite. |
Hello, sorry for the late answer, i upgraded the demo i made to 2.6.4 from 2.5.7, and still the same, to replicate the problem i did this: The entity: import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.UUID;
@Entity
@EntityListeners(AuditingEntityListener.class) //doesn't work for embedded
public class EntityB implements AuditableEntity {
@Id
@GeneratedValue
private UUID uuid;
private String randomAtt;
@Embedded
private Auditable auditable;
//getter and setters
//toString
} Note: im using composition over inheritance for the embedded auditable. the auditable class: import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.Embeddable;
import java.time.Instant;
@Embeddable
public class Auditable {
@CreatedDate //-> doesnt work with @EntityListeners(AuditingEntityListener.class)
private Instant dateCreated;
@LastModifiedDate
private Instant dateUpdated;
//getter and setter
} The interface that acts as composition public interface AuditableEntity {
Auditable getAuditable();
void setAuditable(Auditable auditable);
} Repo interface: import com.example.demoauditable.entities.EntityB;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface EntityBRepository extends JpaRepository<EntityB, UUID> {
} And the demo... @SpringBootApplication
@EnableJpaAuditing
public class DemoAuditableApplication {
private static final Logger log = LoggerFactory.getLogger(DemoAuditableApplication.class);
@Autowired
EntityBRepository entityBRepository;
public static void main(String[] args) {
SpringApplication.run(DemoAuditableApplication.class, args);
}
@Bean
ApplicationRunner afterInit() {
return args -> {
EntityB entityB = new EntityB().setRandomAtt("test2");
entityBRepository.save(entityB);
List<EntityB> bResult = entityBRepository.findAll();
log.info("EntityB result: {}", bResult);
};
}
} output
EDIT: I did another test initializing the it seems that is an issue with Hibernate doesnt initialize by default the composite components so i added this property to avoid using spring:
jpa:
properties:
hibernate:
create_empty_composites:
enabled: true
open-in-view: false so i think the solution to this is to initialize your |
It looks like https://hibernate.atlassian.net/browse/HHH-11936 is an effort currently open to track Hibernate's experimental flag If we are in agreement that the issue is properly initializing the embeddable entry, I think we're done here. I will gladly merge these additional test cases I wrote into the |
i see that an Entity with
@CreatedDate
and@LastModifiedDate
and other auditable annotations works nice when those attributes are in the same class annotated with @entity, but it doesn't seem to work when those are on a@Embeddable
annotated classit would be cool if it can be used a similar approach by using the same
AuditingEntityListener.class
in addition to a custom onei made a demo project showing this
The text was updated successfully, but these errors were encountered: