Annotation based Auditing for spring-data-elasticsearch? - java

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.

Related

How to use Spring Domain Example + Spring Domain Auditable

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).

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.

What to use instead of "System.out.println" in java Spring Boot

#RequestMapping(method=RequestMethod.POST, value= {"/LMSServer/getNoOfDaysOfApplicationBycellNo"} )
#PreAuthorize("hasAuthority('CUSTOMER_MANAGEMENT_R') OR hasAuthority('CUSTOMER_MANAGEMENT_RW')")
public BasicResponce getNoOfDaysOfApplicationBycellNo(#RequestParam(value = "cellNo") long cellNo)
{
if(LOG.isInfoEnabled()){
LOG.info("WebClientRestContoller.getNoOfDaysOfApplicationBycellNo--Start");
LOG.info("Cell NO: "+cellNo);
}
BasicResponce authResp = null;
try {
Customer fromDB= (Customer) objLMSDAO.getDetailsByCellno(cellNo);
DaysOfApplicationResponseDTO toSend= new DaysOfApplicationResponseDTO();
toSend.setCreatedAt(fromDB.getCreatedAt()+"");
toSend.setUpdatedAt(fromDB.getUpdatedAt()+"");
toSend.setRequested_Action(true);
authResp=toSend;
} catch (Exception e) {
e.printStackTrace();
}
if(LOG.isInfoEnabled()){
LOG.info("Returned Response is:");
LOG.info("Response Requested_Action: {} ",new Object[]{authResp.getRequested_Action()});
LOG.info("WebClientRestContoller.getNoOfDaysOfApplicationBycellNo--End");
}
return authResp;
}
The above is my main code. I want to print the difference of days (no. of days between createdAt and updatedAt). Where do I write this logic? I remember in java we use System.out.println to display output, but here I don't know to display code on Postman.
Below is my DTO:
public class DaysOfApplicationResponseDTO extends BasicResponce{
private String createdAt;
private String updatedAt;
private String days;
public String getDays() {
return days;
}
public void setDays(String days) {
this.days = days;
}
private List<CustomerLoanSummaryResponseDTO> LoanApplicationDummyResponseList;
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
public String getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
public List<CustomerLoanSummaryResponseDTO> getLoanApplicationDummyResponseList() {
return LoanApplicationDummyResponseList;
}
public void setLoanApplicationDummyResponseList(
List<CustomerLoanSummaryResponseDTO> loanApplicationDummyResponseList) {
LoanApplicationDummyResponseList = loanApplicationDummyResponseList;
}
public DaysOfApplicationResponseDTO() {
super();
}
public DaysOfApplicationResponseDTO(String createdAt, String UpdatedAt, String days,
List<CustomerLoanSummaryResponseDTO> loanApplicationDummyResponseList) {
super();
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.days = days;
this.LoanApplicationDummyResponseList = loanApplicationDummyResponseList;
}
}
You can introduce service classes (#service annotation) to your project for domain logic. You have to break down and organize the project into a suitable project structure (so that your controllers, entities, services are in different packages for clarity). Better read on those for more information.
Here is a helpful stack-overflow question,
What is the recommended project structure for spring boot rest projects?

mongodb auditing in spring boot for saving createdDate, lastModifiedDate, createdBy, lastModifiedBy

I am using spring boot, therefore I am not using any xml files for configurations.
What I have to do is to EnableMongoAuditing for saving createdDate, lastModifiedDate etc while saving data using MongoRepositories.
My model class
#Component
#Document(collection = "CAPPING")
public class TemporaryCapping extends BaseEntity {
#Field("contract_id")
private BigInteger contractId;
#Field("period_id")
private BigInteger periodId;
#Field("user_id")
private BigInteger userId;
#Field("amount")
private Double amount;
#Field("type_of_capping")
private TypeOfCapping typeOfCapping;
public BigInteger getContractId() {
return contractId;
}
public void setContractId(BigInteger contractId) {
this.contractId = contractId;
}
public BigInteger getPeriodId() {
return periodId;
}
public void setPeriodId(BigInteger periodId) {
this.periodId = periodId;
}
public BigInteger getUserId() {
return userId;
}
public void setUserId(BigInteger userId) {
this.userId = userId;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public TypeOfCapping getTypeOfCapping() {
return typeOfCapping;
}
public void setTypeOfCapping(TypeOfCapping typeOfCapping) {
this.typeOfCapping = typeOfCapping;
}
}
public class BaseEntity implements Serializable{
#Id
#Indexed(unique = true)
private BigInteger id;
#CreatedDate
private DateTime createdDate;
#Field("modified_date")
private BigInteger modifiedDate;
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public DateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(DateTime createdDate) {
this.createdDate = createdDate;
}
public BigInteger getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(BigInteger modifiedDate) {
this.modifiedDate = modifiedDate;
}
I have used #CreateDate annotation for saving createDate.
and I have used jodatime dependency for DateTime
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.7</version>
</dependency>
spring-data-mongodb is also added in the dependencies.
This is my main application class
#SpringBootApplication
#EnableMongoAuditing
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Where I am wrong in this impelmentation as the date is not getting saved in database?
Also I know that for saving #createdBy you need to write AuditorAware bean but for now I am just trying to save createdBy.
Where should #EnableMongoAuditing be used?
In my application I configure through Java code. I use #EnableMongAuditing this way and also create convertes for ZonedDateTime.
#Configuration
#EnableMongoAuditing
#EnableMongoRepositories(basePackages = { BASE_PACKAGE })
public class MongoConfiguration extends AbstractMongoConfiguration {
public static final String BASE_PACKAGE = "package.with.aggregates";
#Value("${spring.data.mongodb.uri}")
private String mongoUri;
#Value("${spring.data.mongodb.database}")
private String databaseName;
#Override
protected String getDatabaseName() {
return databaseName;
}
#Override
public Mongo mongo() throws Exception {
return new MongoClient(new MongoClientURI(mongoUri));
}
// Here you must add converters to Joda datetypes. In my solution is ZonedDateTime
#Override
public CustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<>();
converterList.add(new DateToZonedDateTimeConverter());
converterList.add(new ZonedDateTimeToDateConverter());
return new CustomConversions(converterList);
}
#Override
protected String getMappingBasePackage() {
return BASE_PACKAGE;
}
}
#EnableMongoAuditing can actually be placed anywhere in configurations (next to #Configuration annotation)

HQL uses 'only' the property of first child in the where clause

I am using #Inheritance ( strategy = InheritanceType.TABLE_PER_CLASS)
The parent class is an abstract class and has two children. Device and System
public abstract class Parent {
#Id
#Column(name = "idParent")
protected int parentId;
abstract public String getManufacturer();
abstract public void setManufacturer(String manufacturer);
abstract public Date getUpdated();
abstract public void setUpdated(Date updated);
}
public class Device extends Parent {
#Column(name = "Device_Manufacturer")
private String manufacturer;
public String getManufacturer() {
return this.manufacturer;
}
public void setManufacturer(String Manufacturer) {
this.manufacturer = manufacturer;
}
#Column(name = "Device_Last_Edit")
private Date updated;
public Date getUpdated() {
return this.updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
}
public class System extends Parent {
#Column(name = "System_Manufacturer")
private String manufacturer;
public Date getUpdated() {
return this.updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
#Column(name = "System_Last_Edit")
private Date updated;
public Date getUpdated() {
return this.updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
}
When I query with where clause
Parent.manufacturer = 'ABC'
, the query generated by HQL queries both children by doing a union of System and Device.
But it applies the where clause to
Device_Manufacturer
only and not on
System.Manufacturer
I tried using
Type(Parent) = System
in the where clause but still HQL generates same SQL

Categories

Resources