#CreatedDate annotation does not work with spring-data-elasticsearch - java

I'm using spring-boot-data-elasticsearch to persist the documents into elasticsearch.
Below is my Document Object for it.
#Builder
#Document(indexName = "testidx", createIndex = false)
public class Book {
#Id
private final String _id;
#NotNull
#CreatedDate
#Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private final Instant regTime;
#NotNull
#Field(type = FieldType.Text)
private final String bookName;
}
However, when I saved the object into the ElasticSearchRepository, the regTime(which has to be created and saved automatically by #CreatedDate), has not been saved.
elasticRepository.save(
Book.builder()
.bookName("test")
.build()
);
GET /testidx/_search
{
"_index" : "testidx",
"_type" : "_doc",
"_id" : "M3IS_XgBGpcXDVxetXXn",
"_score" : 1.0,
"_source" : {
"_class" : "{test class Name}",
"bookName" : "test",
}
}
Could you please let me know what I have been missed?
I'm working on the spring-boot 2.4.5 (and spring-boot-starter-data-elasticsearch 2.4.5 also) and ElasticSearch 7.12.0.

Please check the documentation about auditing (https://docs.spring.io/spring-data/elasticsearch/docs/4.2.0/reference/html/#elasticsearch.auditing).
Your entity needs to implement the Persistable interface and needs to reimplement the isNew() method (see section 9.2.1).
Note: I just saw that in the documented entity sample the #CreatedDate and #CreatedBy annotations are missing. I will fix the docs, the annotation is needed of course.
Then do you have #EnableElasticsearchAuditing on your Spring Boot application or any other configuration class? This is needed as well.

Unfortunately spring-data-elasticsearch does not support all persistence related annotations, neither all of the mapping-annotations of JPA, nor the auditing-annotations of Spring-Data, where #CreatedDate belongs to.
Solution
So I would drop it and just use the basic #Field annotation.
Then your property regTime should show up in JSON request as sibling above bookName and be persisted as expected.
However, without the special feature of Spring-Data's #CreatedDate annotation (convenience for auditing), you have to care for initialization yourself, set the value before persisting, e.g. using regTime = Instant.now() (either in field definition, in constructor, or else).
Background
See the docs for Meta Model Object Mapping, 6.1.1. Mapping Annotation Overview which currently (version 4.2) only includes these field annotations:
#Id
#Field
#GeoPoint
See also the official guide 8. Elasticsearch Repositories.
Formerly there was an enum SUPPORTED_ID_PROPERTY_NAMES in SimpleElasticsearchPersistentProperty which listed these field-annotations.
See similar question: Does Spring Data Elasticsearch supports #Id annotation from JPA?
This tutorial helped me as introduction to elasticsearch-data:
Spring Boot Elastic Search Example - Java Developer Zone

Related

Can I use Java 16 record with JPA entity?

I am trying to do something similar like below.
#Entity
#Table(name="Sample")
public record Sample(Integer id, String name) {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="user_id")
private Integer id;
#Column(name="username")
private String name;
}
However, it gives me error "User declared non-static fields id are not permitted in a record"
and same for name field as well.
Is there a way to use new java feature "record" with JPA annotation?
See the article, Using Records as Projections in JPA by Billy Korando. The following is a brief summary.
Records cannot be Entities
Jakarta Persistence (JPA; formerly Java Persistence API) implementations such as Hibernate depend on features either forbidden or not recommended by the JEP 395: Records spec: no-arg constructors, non-final fields, setters, etc.
➥ So, no, records cannot be used as JPA Entity.
Other uses of records
You can use records with:
CriteriaBuilder
TypedQuery
NativeQuery
Mapping definition
Spring data has some support as well.
See that article linked above for details, and for links to two other articles.

Spring Boot Rest API Returns Empty JSON Used with Lombok

I have Spring Boot Application with Web, JPA, H2, Web, Lombok dependency.
I have entities as follows
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Getter
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private Integer pageCount;
#ManyToOne
#JoinColumn(name = "author_id")
private Author author;
}
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Getter
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String firstName;
#Column
private String lastName;
}
I am getting following JSON in repose for the /books REST endpoint
[
{},
{}
]
If I add getters and setters to entity it works fine. How can I get the actual values in response without adding getters setters as I am using #Data annotation
First of all , In spring it is not a good approach to return the entity object directly from a controller to be converted to json. Use the DTO approach so that you will always have a DTO object returned from the controller endpoint. This way you will have more control over the type and structure of data you want to retun from the endpoint.
Read here about the advantages of using a DTO .
Secondly, verify that your tables follow the basic spring jpa naming conventions or is same as the entity class names if not please add the #Table(name="") annotation to specify the table name. Check that data is being populated to your entity classes.
Remove the #Data annotation.
#Data should not be used on JPA Entities as Entity toString, equals, and hashCode methods need to be authored in a very specific manner. See here for details.
After searching a lot and looking on the answers to my question found a solution.
If I run my spring boot app using java -jar from cmd it works fine. But not with IDE so issue was with IDE configuration.
I am using Eclipse IDE
Right Click on your project -> Properties
Then from Properties window
Java Compiler -> Annotation Processing -> Enable Annotation Processing
This option should be checked then annotations gets processed. I followed https://www.baeldung.com/lombok-ide from Tinku's answer on this question.
need to add Lombok plugin in maven or gradle file link How to configure Lombok with maven-compiler-plugin?.
If you are using IDE then need to install Lombok plugin link https://www.baeldung.com/lombok-ide
If you are using Eclipse check to have Lombok installed:

How to replace deprecated MultipleHiLoPerTableGenerator with TableGenerator in Hibernate

I use hibernate in an application with spring boot 1.4.0.RELEASE.
The Entity for the index looks something along the lines of:
#Entity(name = "SearchableTariffItem")
#Indexed
public class SearchableTariffItem {
public static final String ZIFFER_ANALYZER_NAME = "ZIFFER_ANALYZER";
#GeneratedValue(strategy = GenerationType.TABLE)
#Id
private Long id;
...
}
I now get the following warning when I save the entity for the first time:
2016-08-26 15:08:32.501 WARN 8476 — [apr-8080-exec-6] org.hibernate.orm.deprecation : HHH90000015: Found use of deprecated [org.hibernate.id.MultipleHiLoPerTableGenerator] table-based id generator; use org.hibernate.id.enhanced.TableGenerator instead. See Hibernate Domain Model Mapping Guide for details.
Unfortunately I don't know where I can configure my application (preferably in a the application.yml) to use the TableGenerator class.
I use the following dependency:
Hibernate core 5.0.9.Final
Hibernate search ORM 5.5.1.Final
Lucene 5.3.1
The property that controls this behaviour in Hibernate is hibernate.id.new_generator_mappings, which defaults to true for Hibernate 5 -> which means the new TableGenerator will be used instead of the deprecated MultipleHiLoPerTableGenerator .
Spring Boot however defaults this property to false, which means the old generator will be used, unless you explicitly tell it you want the new one.
You need to set the property spring.jpa.hibernate.use-new-id-generator-mappings to true to get the TableGenerator.
See https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes#generator-mappings

Mapping JSON object to Hibernate entity

I'm going to start a project of a REST application managed with Spring and with Hibernate for my model.
I know that Spring allows you to get Java object from the HTTP Request (with #Consumes(JSON) annotation). Is there any conflict if this Java object is also a Hibernate entities? And is nested object working (like #ManyToOne relation)?
Maven dependency
The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
Domain model
Now, if you are using PostgreSQL, you need to use the JsonType from Hibernate Types.
In order to use it in your entities, you will have to declare it on either class level or in a package-info.java package-level descriptor, like this:
#TypeDef(name = "json", typeClass = JsonType.class)
And, the entity mapping will look like this:
#Type(type = "json")
#Column(columnDefinition = "json")
private Location location;
If you're using Hibernate 5 or later, then the JSON type is registered automatically by the Postgre92Dialect.
Otherwise, you need to register it yourself:
public class PostgreSQLDialect extends PostgreSQL91Dialect {
public PostgreSQL92Dialect() {
super();
this.registerColumnType( Types.JAVA_OBJECT, "json" );
}
}
The JsonType works with Oracle, SQL Server, PostgreSQL, MySQL, and H2 as well. Check out the project page for more details about how you can map JSON column types on various relational database systems.
Yes, this wouldn't be a problem and is actually a fairly common practice.
In the recent years I have come to realize that sometimes, however, it is not a good idea to always build your views based on your domain directly. You can take a look at this post:
http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/
It is also known as "Presentation Model":
http://martinfowler.com/eaaDev/PresentationModel.html
The idea behind that is basically the following:
Imagine you have the domain entry User, who looks like that :
#Entity
#Data
public class User {
#Id private UUID userId;
private String username;
#OneToMany private List<Permission> permissions;
}
Let's now imagine you have a view where you wanna display that user's name, and you totally don't care about the permissions. If you use your approach of immediately returning the User to the view, Hibernate will make an additional join from the Permissions table because event though the permissions are lazily loaded by default, there is no easy way to signal to the jackson serializer or whatever you are using, that you don't care about them in this particular occasion, so jackson will try to unproxy them (if your transaction is still alive by the time your object is put for json serialization, otherwise you get a nasty exception). Yes, you can add a #JsonIgnore annotation on the permissions field, but then if you need it in some other view, you are screwed.
That a very basic example, but you should get the idea that sometimes your domain model can't be immediately used to be returned to the presentation layer, due to both code maintainability and performance issues.
We were using such approach to simplify design and get rid of many dtos (we were abusing them too much). Basically, it worked for us.
However, in our REST model we were trying to do not expose other relations for an object as you can always create another REST resources to access them.
So we just put #JsonIgnore annotations to relations mappings like #OneToMany or #ManyToOnemaking them transient.
Another problem I see that if you still like to return these relations you would have to use Join.FETCH strategy for them or move transaction management higher so that transaction still exists when a response is serialized to JSON (Open Session In View Pattern).
On my opinion these two solutions are not so good.
You can map the json request without using any library at REST web-services (Jersy)
this sample of code:
This hibernate entity called book:
#Entity
#Table(name = "book", schema = "cashcall")
public class Book implements java.io.Serializable {
private int id;
private Author author; // another hibernate entity
private String bookName;
//setters and getters
}
This web-services function
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String addBook(Book book) {
String bookName=book.getName();
return bookName;
}
This is sample json request:
{
"bookName" : "Head First Java",
"author" : {
"id" : 1
}
}
Since you are just starting, perhaps you could use Spring Data REST?
This is the project: http://projects.spring.io/spring-data-rest/
And here are some simple examples:
https://github.com/spring-projects/spring-data-book/tree/master/rest
https://github.com/olivergierke/spring-restbucks
As you can see in the examples, there are no extra DTOs beyond the #Entity annotated POJOs.

Specifying an Index (Non-Unique Key) Using JPA

How do you define a field, eg email as having an index using JPA annotations. We need a non-unique key on email because there are literally millions of queries on this field per day, and its a bit slow without the key.
#Entity
#Table(name="person",
uniqueConstraints=#UniqueConstraint(columnNames={"code", "uid"}))
public class Person {
// Unique on code and uid
public String code;
public String uid;
public String username;
public String name;
public String email;
}
I have seen a hibernate specific annotation but I am trying to avoid vendor specific solutions as we are still deciding between hibernate and datanucleus.
UPDATE:
As of JPA 2.1, you can do this. See: The annotation #Index is disallowed for this location
With JPA 2.1 you should be able to do it.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
#Entity
#Table(name = "region",
indexes = {#Index(name = "my_index_name", columnList="iso_code", unique = true),
#Index(name = "my_index_name2", columnList="name", unique = false)})
public class Region{
#Column(name = "iso_code", nullable = false)
private String isoCode;
#Column(name = "name", nullable = false)
private String name;
}
Update: If you ever need to create and index with two or more columns you may use commas. For example:
#Entity
#Table(name = "company__activity",
indexes = {#Index(name = "i_company_activity", columnList = "activity_id,company_id")})
public class CompanyActivity{
A unique hand-picked collection of Index annotations
= Specifications =
JPA 2.1+: javax.persistence.Index (or see JSR-000338 PDF, p. 452, item 11.1.23)
The JPA #Index annotation can only be used as part of another annotation like #Table, #SecondaryTable, etc.:
#Table(indexes = { #Index(...) })
JDO 2.1+: javax.jdo.annotations.Index
= ORM Frameworks =
♥ Hibernate ORM: org.hibernate.annotations.Index;
OpenJPA: org.apache.openjpa.persistence.jdbc.Index and org.apache.openjpa.persistence.jdbc.ElementIndex (see Reference Guide);
EclipseLink: org.eclipse.persistence.annotations.Index;
DataNucleus: org.datanucleus.api.jpa.annotations.Index;
Carbonado (GitHub): com.amazon.carbonado.Index;
EBean: com.avaje.ebean.annotation.Index or io.ebean.annotation.Index ?
Ujorm: Annotation org.ujorm.orm.annot.Column, index and uniqueIndex properties;
requery (GitHub. Java, Kotlin, Android): Annotation io.requery.Index;
Exposed (Kotlin SQL Library): org.jetbrains.exposed.sql.Index, org.jetbrains.exposed.sql.Table#index(). Example:
object Persons : IdTable() {
val code = varchar("code", 50).index()
}
= ORM for Android =
♥ ActiveAndroid: Annotation com.activeandroid.annotation.Column has index, indexGroups, unique, and uniqueGroups properties;
UPDATE [2018]: ActiveAndroid was a nice ORM 4 years ago, but unfortunately, the author of the library stopped maintaining it, so someone forked, fixed bugs, and rebranded it as ReActiveAndroid - use this if you're starting a new project or refer to Migration Guide if you want to replace ActiveAndroid in a legacy project.
ReActiveAndroid: Annotation com.reactiveandroid.annotation.Column has index, indexGroups, unique, and uniqueGroups properties;
ORMLite: Annotation com.j256.ormlite.field.DatabaseField has an index property;
greenDAO: org.greenrobot.greendao.annotation.Index;
ORMAN (GitHub): org.orman.mapper.annotation.Index;
★ DBFlow (GitHub): com.raizlabs.android.dbflow.sql.index.Index (example of usage);
other (lots of ORM libraries at the Android Arsenal).
= Other (difficult to categorize) =
Realm - Alternative DB for iOS / Android: Annotation io.realm.annotations.Index;
Empire-db - a lightweight yet powerful relational DB abstraction layer based on JDBC. It has no schema definition through annotations;
Kotlin NoSQL (GitHub) - a reactive and type-safe DSL for working with NoSQL databases (PoC): ???
Slick - Reactive Functional Relational Mapping for Scala. It has no schema definition through annotations.
Just go for one of them.
JPA 2.1 (finally) adds support for indexes and foreign keys! See this blog for details. JPA 2.1 is a part of Java EE 7, which is out .
If you like living on the edge, you can get the latest snapshot for eclipselink from their maven repository (groupId:org.eclipse.persistence, artifactId:eclipselink, version:2.5.0-SNAPSHOT). For just the JPA annotations (which should work with any provider once they support 2.1) use artifactID:javax.persistence, version:2.1.0-SNAPSHOT.
I'm using it for a project which won't be finished until after its release, and I haven't noticed any horrible problems (although I'm not doing anything too complex with it).
UPDATE (26 Sep 2013): Nowadays release and release candidate versions of eclipselink are available in the central (main) repository, so you no longer have to add the eclipselink repository in Maven projects. The latest release version is 2.5.0 but 2.5.1-RC3 is also present. I'd switch over to 2.5.1 ASAP because of issues with the 2.5.0 release (the modelgen stuff doesn't work).
In JPA 2.1 you need to do the following
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
#Entity(name="TEST_PERSON")
#Table(
name="TEST_PERSON",
indexes = {
#Index(name = "PERSON_INDX_0", columnList = "age"),
#Index(name = "PERSON_INDX_1", columnList = "fName"),
#Index(name = "PERSON_INDX_1", columnList = "sName") })
public class TestPerson {
#Column(name = "age", nullable = false)
private int age;
#Column(name = "fName", nullable = false)
private String firstName;
#Column(name = "sName", nullable = false)
private String secondName;
#Id
private long id;
public TestPerson() {
}
}
In the above example the table TEST_PERSON will have 3 indexes:
unique index on the primary key ID
index on AGE
compound index on FNAME, SNAME
Note 1: You get the compound index by having two #Index annotations with the same name
Note 2: You specify the column name in the columnList not the fieldName
I'd really like to be able to specify database indexes in a standardized way but, sadly, this is not part of the JPA specification (maybe because DDL generation support is not required by the JPA specification, which is a kind of road block for such a feature).
So you'll have to rely on a provider specific extension for that. Hibernate, OpenJPA and EclipseLink clearly do offer such an extension. I can't confirm for DataNucleus but since indexes definition is part of JDO, I guess it does.
I really hope index support will get standardized in next versions of the specification and thus somehow disagree with other answers, I don't see any good reason to not include such a thing in JPA (especially since the database is not always under your control) for optimal DDL generation support.
By the way, I suggest downloading the JPA 2.0 spec.
As far as I know, there isn't a cross-JPA-Provider way to specify indexes. However, you can always create them by hand directly in the database, most databases will pick them up automatically during query planning.
EclipseLink provided an annotation (e.g. #Index) to define an index on columns. There is an example of its use. Part of the example is included...
The firstName and lastName fields are indexed, together and individually.
#Entity
#Index(name="EMP_NAME_INDEX", columnNames={"F_NAME","L_NAME"}) // Columns indexed together
public class Employee{
#Id
private long id;
#Index // F_NAME column indexed
#Column(name="F_NAME")
private String firstName;
#Index // L_NAME column indexed
#Column(name="L_NAME")
private String lastName;
...
}
OpenJPA allows you to specify non-standard annotation to define index on property.
Details are here.
To sum up the other answers:
Hibernate: org.hibernate.annotations.Index
OpenJPA: org.apache.openjpa.persistence.jdbc.Index
EclipseLink: org.eclipse.persistence.annotations.Index
I would just go for one of them. It will come with JPA 2.1 anyway and should not be too hard to change in the case that you really want to switch your JPA provider.
It's not possible to do that using JPA annotation. And this make sense: where a UniqueConstraint clearly define a business rules, an index is just a way to make search faster. So this should really be done by a DBA.
This solution is for EclipseLink 2.5, and it works (tested):
#Table(indexes = {#Index(columnList="mycol1"), #Index(columnList="mycol2")})
#Entity
public class myclass implements Serializable{
private String mycol1;
private String mycol2;
}
This assumes ascendant order.

Categories

Resources