How to use Spring Domain Example + Spring Domain Auditable - java

I have the following issue using Spring Data Domain Example with an entity that implements Spring Data Domain Auditable.
Error :
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [Optional.empty] did not match expected type [java.time.LocalDateTime (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [Optional.empty] did not match expected type [java.time.LocalDateTime (n/a)]
Entity :
import org.springframework.data.domain.Auditable;
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "my_table")
#Builder
public class MyEntity implements Serializable, Auditable<String, Integer, LocalDateTime>
#Getter
#Setter
#Id
#Column(name = "my_table_id", nullable = false)
private Integer id;
// Some fields
// ...
// AUDIT
#Column(name = "my_table_created_by")
#CreatedBy
private String createdBy;
#Column(name = "my_table_created_date")
#CreatedDate
#Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentLocalDateTime")
private LocalDateTime createdDate;
#Column(name = "my_table_last_modified_by")
#LastModifiedBy
private String lastModifiedBy;
#Column(name = "my_table_last_modified_date")
#LastModifiedDate
#Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentLocalDateTime")
private LocalDateTime lastModifiedDate;
#Override
public boolean isNew()
{
return id == null;
}
#NotNull
#Override
public Optional<String> getCreatedBy()
{
return Optional.ofNullable(createdBy);
}
#Override
public void setCreatedBy(#NotNull String createdBy)
{
this.createdBy = createdBy;
}
#NotNull
#Override
public Optional<LocalDateTime> getCreatedDate()
{
return Optional.ofNullable(createdDate);
}
#Override
public void setCreatedDate(
#NotNull
LocalDateTime creationDate)
{
this.createdDate = creationDate;
}
#NotNull
#Override
public Optional<String> getLastModifiedBy()
{
return Optional.ofNullable(lastModifiedBy);
}
#Override
public void setLastModifiedBy(#NotNull String lastModifiedBy)
{
this.lastModifiedBy = lastModifiedBy;
}
#NotNull
#Override
public Optional<LocalDateTime> getLastModifiedDate()
{
return Optional.ofNullable(lastModifiedDate);
}
#Override
public void setLastModifiedDate(#NotNull LocalDateTime lastModifiedDate)
{
this.lastModifiedDate = lastModifiedDate;
}
}
Test :
import org.springframework.data.domain.Example;
#Test
public void test()
{
MyEntity entity = MyEntity.builder()
.someField("someValue")
.build();
List<MyEntity> entities = repository.findAll(Example.of(entity));
}
I guess it has to do with the Optional Methods that I have with Auditable, but I don't see how to fix this problem.
It works if I duplicate the entity class without Auditable but it's not clean.

Fix, I don't need to implement Auditable since I already use #EntityListeners(AuditingEntityListener.class).

Related

JPA / Hibernate - Update parent entity on child changes

I have following two entities in OneToOne relationship.
#Entity
#Audited
public class Parent implements Serializable {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
#Size(max = 36)
#Column(length = 36)
private String id;
#NotNull
private String createdUser;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#Temporal(TemporalType.TIMESTAMP)
#NotNull
private Date createdDate;
private String modifiedUser;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#Temporal(TemporalType.TIMESTAMP)
private Date modifiedDate;
#OneToOne(targetEntity = Child.class, optional = false, fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval = true)
#Fetch(FetchMode.JOIN)
private Child child = new Child();
#PrePersist
public void prePersist() {
createdDate = new Date();
createdUser = "SampleUser";
}
#PreUpdate
public void preUpdate() {
modifiedDate = new Date();
modifiedUser = "SampleUser";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
public Date getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(Date modifiedDate) {
this.modifiedDate = modifiedDate;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
#Entity
#Audited
public class Child implements Serializable {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
#Size(max = 36)
#Column(length = 36)
private String id;
private String sampleField;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSampleField() {
return sampleField;
}
public void setSampleField(String sampleField) {
this.sampleField = sampleField;
}
}
Now I need to update the parents modified (date & user) fields. If i change only the field (sampleField) in the child then the modified fields are not updated in the parent.
I found following two statements and if I understand it correctly then its not a good idea to change the parent in lifecycle events (Interceptor, EventListener, Callbacks) of the child.
Statement 1
Statement 2
What is a save way to address my problem?
Update
I found this way from vlad: How to increment the parent entity version. But I'm not sure if this way is safe. Life cycle events are also used.
I can only offer you a workaround, as I don't think what you want to achieve is doable out of the box with Hibernate or any other JPA provider.
You can encapsulate the Child completely, and with a bi-directional reference, you can make sure that the modifiedDate is dirtied when necessary. A model similar to this should suffice:
#Entity
#Audited
public class Parent implements Serializable {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
#Size(max = 36)
#Column(length = 36)
private String id;
#NotNull
private String createdUser;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#Temporal(TemporalType.TIMESTAMP)
#NotNull
private Date createdDate;
private String modifiedUser;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#Temporal(TemporalType.TIMESTAMP)
private Date modifiedDate;
#OneToOne(targetEntity = Child.class, optional = false, fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval = true)
#Fetch(FetchMode.JOIN)
private Child child;
public Parent() {
child = new Child(this);
}
#PrePersist
public void prePersist() {
createdDate = modifiedDate = new Date();
createdUser = modifiedUser = "SampleUser";
}
#PreUpdate
public void preUpdate() {
modifiedDate = new Date();
modifiedUser = "SampleUser";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
public Date getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(Date modifiedDate) {
this.modifiedDate = modifiedDate;
}
public Child getChild() {
return child;
}
// Note that I removed the setter
}
#Entity
#Audited
public class Child implements Serializable {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
#Size(max = 36)
#Column(length = 36)
private String id;
private String sampleField;
#OneToOne(mappedBy = "child")
private Parent parent;
public Child() {}
public Child(Parent parent) {
this.parent = parent;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSampleField() {
return sampleField;
}
public void setSampleField(String sampleField) {
if (parent != null && !Objects.equals(this.sampleField, sampleField)) {
parent.setModifiedDate(null);
}
this.sampleField = sampleField;
}
}

PostgreSQL not doing autoincrement

I am using postgresql with springboot. So whenever I am using post method to add a new detail in my table instead of autoincrementing id it's going from 1 to 3. It's taking alternate values rather than consecutive values. I have given following properties and then created table:
spring.jpa.hibernate.ddl-auto=create
Didn't create the table manually. What is the reason for this error? This is my entity class.
#Entity
#Table(name = "NORTHBOUND_SUBSCRIPTION")
public class NBSubscription {
#Id
#GeneratedValue
#Column(name = "nb_id")
private Long nbId;
#Column(name = "DEVICE_FILTER")
private String deviceFilter;
#Column(name = "INTERFACE_FILTER")
private String interfaceFilter;
#ManyToOne
#JoinColumn(name="subscriber_id", referencedColumnName="SUBSCRIBER_ID")
private Subscriber subscriber;
#OneToOne
#JoinColumn(name="sensor_group_id", referencedColumnName="ID")
private SensorGroup sensorGroup;
#Column(name = "EVENT_TYPE")
private String eventType;
#Column(name = "SAMPLING_INTERVAL")
private Integer samplingInterval;
#Column(name = "CREATEAT")
#DateTimeFormat(pattern = "dd-MM-yyyy HH:mm")
private Timestamp createAt;
#Column(name = "MODIFIEDAT")
#DateTimeFormat(pattern = "dd-MM-yyyy HH:mm")
private Timestamp modifiedAt;
#Column(name = "CREATEDBY")
private String createdBy;
#Column(name = "MODIFIEDBY")
private String modifiedBy;
#Column(name = "mark_deletion")
private String markDeletion;
public NBSubscription() {
super();
}
public NBSubscription(Subscriber subscriber, SensorGroup sensorGroup) {
super();
this.subscriber = subscriber;
this.sensorGroup = sensorGroup;
}
public Long getNbId() {
return nbId;
}
public void setNbId(Long nbId) {
this.nbId = nbId;
}
public String getDeviceFilter() {
return deviceFilter;
}
public void setDeviceFilter(String deviceFilter) {
this.deviceFilter = deviceFilter;
}
public String getInterfaceFilter() {
return interfaceFilter;
}
public void setInterfaceFilter(String interfaceFilter) {
this.interfaceFilter = interfaceFilter;
}
#JsonIgnore
public Subscriber getSubscriber() {
return subscriber;
}
public void setSubscriber(Subscriber subscriber) {
this.subscriber = subscriber;
}
public SensorGroup getSensorGroup() {
return sensorGroup;
}
public void setSensorGroup(SensorGroup sensorGroup) {
this.sensorGroup = sensorGroup;
}
public Integer getSamplingInterval() {
return samplingInterval;
}
public void setSamplingInterval(Integer samplingInterval) {
this.samplingInterval = samplingInterval;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public Timestamp getCreateAt() {
return createAt;
}
public void setCreateAt(Timestamp createAt) {
this.createAt = createAt;
}
public Timestamp getModifiedAt() {
return modifiedAt;
}
public void setModifiedAt(Timestamp modifiedAt) {
this.modifiedAt = modifiedAt;
}
public String getMarkDeletion() {
return markDeletion;
}
public void setMarkDeletion(String markDeletion) {
this.markDeletion = markDeletion;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public String getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
Try this
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
if it doesn't work, try using the table sequence
#Id
#GeneratedValue(strategy=SEQUENCE, generator="CUST_SEQ")
Your autoincrement is implemented with a sequence, and by default entities share the same sequence, so the autoincrement values get spread across the entities.
You could assign each entity its own sequence. But be aware sequences don't participate in transactions. That means if you have a rollback there will be a break in the numbering. Occasional gaps are not avoidable.
If you are making this sequence visible to users and they expect the numbering to be contiguous, my advice is to not use a sequence for that, and keep the user-visible counter in a field separate from the id. If it is visible to users, then at some point it will need to change, and you can't change ids.

Hibernate - Subclass has to be binded after it's mother class

after upgrading hibernate to from version 4.3.7.Final to 5.3.18.Final I got the error below
#Entity
#Audited
#AuditPermission(Permission.VIEW_INDIVIDUAL)
public class Individual implements ITemporalEntity {
#Id
#Column(name = "Individual_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Individual_generator")
#SequenceGenerator(name = "Individual_generator", initialValue = 1, allocationSize = 1, sequenceName = "Individual_id_seq")
private Long id;
#Embedded
private TemporalEntity temporal = new TemporalEntity();
#Override
public DateTime getCreateDate() {
return temporal.getCreateDate();
}
#Override
public void setCreateDate(DateTime createDate) {
temporal.setCreateDate(createDate);
}
.......
...
}
TemporalEntity class
#Embeddable
public class TemporalEntity {
#Column(updatable = false)
private DateTime createDate;
#Column
private DateTime lastModifiedDate;
#ManyToOne
#JoinColumn(name = "created_by_id", updatable = false)
private AdminUser createdBy;
#ManyToOne
#JoinColumn(name = "last_modified_by_id")
private AdminUser lastModifiedBy;
#Column(nullable = false, columnDefinition = "boolean not null default false")
private boolean deleted = false;
public DateTime getCreateDate() {
return createDate;
}
public void setCreateDate(DateTime createDate) {
if (createDate == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null create date not allowed");
}
this.createDate = createDate;
}
public DateTime getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(DateTime lastModifiedDate) {
if (lastModifiedDate == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null last modified date not allowed");
}
this.lastModifiedDate = lastModifiedDate;
}
public AdminUser getCreatedBy() {
return createdBy;
}
public void setCreatedBy(AdminUser createdBy) {
if (createdBy == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null created by not allowed");
}
this.createdBy = createdBy;
}
public AdminUser getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(AdminUser lastModifiedBy) {
if (lastModifiedBy == null) {
//ignore attempts to clear this field
return;
//throw new IllegalStateException("Null lastModifiedBy not allowed");
}
this.lastModifiedBy = lastModifiedBy;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
ITemporalEntity interface
public interface ITemporalEntity {
public DateTime getCreateDate();
public void setCreateDate(DateTime createDate);
public DateTime getLastModifiedDate();
public void setLastModifiedDate(DateTime lastModifiedDate);
public AdminUser getCreatedBy();
public void setCreatedBy(AdminUser createdBy);
public AdminUser getLastModifiedBy();
public void setLastModifiedBy(AdminUser lastModifiedBy);
public boolean isDeleted();
public void setDeleted(boolean deleted);
}
the error stack
an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: Subclass has to be binded after it's mother class: com.berwick.dal.TemporalEntity
23:11:29,486 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 87) MSC000001: Failed to start service jboss.persistenceunit."bds-core-1.0-SNAPSHOT.war#com.berwick.dal": org.jboss.msc.service.StartException in service jboss.persistenceunit."bds-core-1.0-SNAPSHOT.war#com.berwick.dal": org.hibernate.AssertionFailure: Subclass has to be binded after it's mother class: com.berwick.dal.TemporalEntity
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:198) [wildfly-jpa-21.0.0.Final.jar:21.0.0.Final]
My tries to fix the issue
add #MappedSuperclass to TemporalEntity class
that makes this error gone but I got more errors
Duplicate generator name Individual_generator you will likely want to set the property hibernate.jpa.compliance.global_id_generators to false
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:198) [wildfly-jpa-21.0.0.Final.jar:21.0.0.Final]
The question is quite similar asked in linked thread.
Error Mapping Embedded class exposed through an interface
write your entity class name that you want to embed in the #target annotation
#Embedded
#Target(TemporalEntity.class)
private ITemporalEntity temporal;
you do not need to tightly couple the TemporalEntity by creating new object embedded do it

Annotation based Auditing for spring-data-elasticsearch?

I am trying to use the annotation based approach to audit my elasticsearch document using spring-data-elasticsearch. I followed the JPA guide while implementing my classes/setup.
My code looks like this:
#Configuration
#EnableJpaAuditing
public class SpringSecurityAuditorAwareConfiguration {
#Bean
public AuditorAware<String> auditorProvider() {
return new SpringSecurityAuditorAware();
}
}
public class SpringSecurityAuditorAware implements AuditorAware<String> {
public Optional<String> getCurrentAuditor() {
return Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(User.class::cast)
.map(User::getUsername);
}
}
#Document(indexName = "myEntity", type = "myEntityType")
public class MyEntity {
#Id
private String id;
#CreatedBy
protected String createdBy;
#CreatedDate
protected OffsetDateTime createdDate;
#LastModifiedBy
protected String lastModifiedBy;
#LastModifiedDate
protected OffsetDateTime lastModifiedDate;
public void setDictionaryId(DictionaryId dictionaryId) {
this.dictionaryId = dictionaryId;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public OffsetDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(OffsetDateTime createdDate) {
this.createdDate = createdDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public OffsetDateTime getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(OffsetDateTime lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
}
Unfortunately it when I save a new instance the properties are always set to null. Does spring-data-elasticsearch support the annotation based approach?
Edit:
This is implemented since version 4.0 which has been released in May 2020.
Original answer:
No, this is currently not supported in Spring Data Elasticsearch.
Please create a ticket to add support for this, this is definitely a feature worth to have.

Spring Data error Caused by: org.hibernate.QueryException: could not resolve property

My application throws the error while starting
Caused by: org.hibernate.QueryException: could not resolve property: version of: org.mycompany.system.model.Code
My repository method
#Query("SELECT NEW org.mycompany.util.CodeVO(c.id, c.description,c.version) FROM Code c where c.groupName = :groupName")
List<CodeVO> getByGroupName(#Param("groupName") String groupName);
Base class
public abstract class ModelObject<ID extends Serializable> implements Serializable {
#Column(nullable = false, length = 100)
private String createdBy;
#Column(nullable = false)
private LocalDateTime createdTime = LocalDateTime.now();
#Version
private int version;
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
Sub class
#Entity
#Table(uniqueConstraints = { #UniqueConstraint(columnNames = { "groupName", "description" }) })
public class Code extends ModelObject<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
#NotBlank
private String description;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
is it not possible to refer superclass properties?
ModelObject must be annotated with #MappedSuperclass there is no automatic inheritance in JPA
From the spec:
11.1.38 MappedSuperclass Annotation
The MappedSuperclass annotation designates a class whose mapping information is applied to the entities that inherit from it. A mapped superclass has no separate table defined for it.

Categories

Resources