Hibernate Search : The field () used for the spatial query is not configured as spatial field - hibernate-search

I have the classes Organization and Coordinates.
Coordinates is as follow :
#Entity
#Table(name = "COORDINATES")
#Indexed
#Spatial
public class Coordinates implements Serializable {
private static final long serialVersionUID = 1L;
public Coordinates() {}
#Id
#Column(name = "id", nullable = false)
#SequenceGenerator(name = "generator", sequenceName = "COORDINATES_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
private Long id;
#Field
#Latitude
#Column(name = "LATITUDE", nullable = false)
private Double latitude;
#Field
#Longitude
#Column(name = "LONGITUDE", nullable = false)
private Double longitude;
I put the annotation #IndexedEmbedded in the coordinates field in the Organization (which is #Indexed + #Entity + #Table).
How come the spatial feature is not recognized in my query ? I have the following exception :
org.hibernate.search.exception.SearchException: HSEARCH000131: The
field 'organization.Organization#coordinates' used for the spatial
query is not configured as spatial field. Check the proper use of
#Spatial respectively SpatialFieldBridge at
org.hibernate.search.query.dsl.impl.ConnectedSpatialQueryBuilder.createSpatialQuery(ConnectedSpatialQueryBuilder.java:63)
at
org.hibernate.search.query.dsl.impl.ConnectedSpatialQueryBuilder.createQuery(ConnectedSpatialQueryBuilder.java:38)
Edit 1
I tried adding a name to the Spatial annotation but I got the error saying #Latitude and #Longitude couldn't be found.
So I tried implementing Hibernate's Coordinates, changing #Entity to #Embeddable, changing #IndexedEmbedded for #Spatial in Organization class on the
coordinates attribute, removing #Latitude + #Longitude in my Coordinates class, overriding getLatitude() and getLongitude(). Now I have the following exception :
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on
com.(...).model.organization.Organization.coordinates
references an unknown entity:
com.(...).model.localization.Coordinates
I am using :
hibernate-core : 5.2.12.Final
hibernate-jpamodelgen : 5.2.12.Final
hibernate-search-orm : 5.8.2.Final
hibernate-search-elasticsearch : 5.8.2.Final
I talked with the other devs and it seems like they need features that changed in the more recent versions so we don't want to change for the moment.
Here is the interesting part in Organization class :
#Entity
#Table(name = "ORGANIZATION", uniqueConstraints = {#UniqueConstraint(columnNames = { "CODE" }) })
#Indexed
public class Organization implements Serializable, FileEntity {
// lots of attributes, getters, setters, constructor
#Spatial(name = "location", spatialMode = SpatialMode.RANGE)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COORDINATES_ID")
private Coordinates coordinates;
}
And the modified Coordinates class :
#Embeddable
#Table(name = "COORDINATES")
#Indexed
public class Coordinates implements org.hibernate.search.spatial.Coordinates, Serializable {
// serial, constructor, id get+set
#Field
#Column(name = "LATITUDE", nullable = false)
private Double latitude;
#Field
#Column(name = "LONGITUDE", nullable = false)
private Double longitude;
#Override
public Double getLatitude() {
return this.latitude;
}
#Override
public Double getLongitude() {
return this.longitude;
}
}
Edit 2
I added the "(of = "location")" in #Latitude and #Longitude.
I still have the error :
Error message:
{"root_cause":[{"type":"query_shard_exception","reason":"failed to
find geo_point field [coordinates.location]" (...)
Maybe it comes from the query ?
Query :
final BooleanJunction bool = queryBuilder.bool().must(queryBuilder.bool()
.should(...)
.should(...)
.should(queryBuilder
.spatial()
.onField("coordinates.location")
.within(12, Unit.KM)
.ofLatitude(form.getLatitude())
.andLongitude(form.getLongitude())
.createQuery())
.createQuery())
.must(...)
.must(...)
.must(...);
final FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(bool.createQuery(), Organization.class);
fullTextQuery.setMaxResults(form.getMaximumNumberOfResult());
fullTextQuery.setProjection(FullTextQuery.SPATIAL_DISTANCE, FullTextQuery.THIS);
fullTextQuery.setSpatialParameters(form.getLatitude(), form.getLongitude(), "coordinates.location");
fullTextQuery.setProjection(FullTextQuery.THIS, FullTextQuery.SCORE);
The field "coordinates" in Organization is #IndexedEmbedded and the Coordinates class is as you wrote it.

So, first, it's a bit odd that Hibernate Search managed to start up to begin with, because your mapping contains a mistake: you should set the name attribute on the #Spatial annotation. Then Hibernate Search will create a field in the Coordinates type with that name (let's say "location").
Once that is done, you will be able to query that field from the Organization type using the coordinates.location name.
I know, it sounds a bit silly in this case, but generally the entity you put the #Spatial annotation on is a business type, not something representing coordinates, so it makes more sense.
Another option would be to turn your Coordinates type into an embeddable instead of an entity, make it implement org.hibernate.search.spatial.Coordinates, and put the #Spatial annotation on the Organization#coordinates property instead of putting it on the Coordinates type. Then you wouldn't need the #IndexedEmbedded anymore nor the #Latitude/#Longitude, and you could actually just use the coordinates field name when querying.
If the above doesn't work, please add the versions of Hibernate Search and Hibernate ORM you are using, and the code of the Organization class.
EDIT: In answer to your edit...
About the #Latitude/#Longitude annotations not being found: you need to use the of attribute of these annotations to match the name of your field.
If you do not switch to #Embeddable, this would lead to something like this:
#Entity
#Table(name = "COORDINATES")
#Indexed
#Spatial(name = "location")
public class Coordinates implements Serializable {
private static final long serialVersionUID = 1L;
public Coordinates() {}
#Id
#Column(name = "id", nullable = false)
#SequenceGenerator(name = "generator", sequenceName = "COORDINATES_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
private Long id;
#Field
#Latitude(of = "location")
#Column(name = "LATITUDE", nullable = false)
private Double latitude;
#Field
#Longitude(of = "location")
#Column(name = "LONGITUDE", nullable = false)
private Double longitude;
}
About your "org.hibernate.AnnotationException" after switching to an embeddable: you need to remove the #ManyToOne on the coordinates attribute and replace it with #Embedded. Embedded attributes are very different from associations, please have a look at the Hibernate ORM documentation.

Related

Unable to create unique key constraint - Make sure that you use the correct column name which depends on the naming strategy in use

The full error message is:
Unable to create unique key constraint (aircraft_series_id, service_enum) on table aircraft_service: database column 'service_enum' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
My entity is specified as:
#Entity
#Table(uniqueConstraints = { #UniqueConstraint(columnNames = { "aircraft_series_id", "service_enum" }) })
#Getter
#Setter
#NoArgsConstructor
#ToString
public class AircraftService {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private Integer minimumQuantity;
#NotNull
private Integer maximumQuantity;
#NotNull
private Integer defaultQuantity;
#NotNull
#ManyToOne(optional = false)
#JsonIgnore
private AircraftSeries aircraftSeries;
#NotNull
#Enumerated(EnumType.STRING)
private ServiceEnum serviceEnum;
}
If I comment out the #Table(uniqueConstraints = { #UniqueConstraint(columnNames = { "aircraft_series_id", "service_enum" }) }) annotation then the columns are created and I can see the field names when opening the table under the SQL client.
service_enum
aircraft_series_id
For now I'm running the application against the H2 database.
I could have the application running not throw an exception if the class is boasting the column annotations, as in:
#Column(name = "service_enum")
#ManyToOne(optional = false)
#JoinColumn(name = "service_profile_id")
I don't see why this is the case, as by default, the column names are exactly the same, when attributed by the application itself.

Spring batch JdbcCursorItemReader : reading from tables having FK relation

Here's my Reader :
private static final String SELECT_ALL_BOOKS = "SELECT * FROM BOOKS WHERE COLOR = 'yellow'";
#Bean
public JdbcCursorItemReader<BookEntity> itemReader(final DataSource dataSource) {
return new JdbcCursorItemReaderBuilder<BookEntity>()
.name("book_reader")
.sql(SELECT_ALL_BOOKS)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(BookEntity.class))
.build();
}
And my entity :
#Entity
#Getter
#Setter
#Table(name = "book")
#AllArgsConstructor
#NoArgsConstructor
public class BookEntity implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_book")
private Integer idBook;
#Column(name = "color")
private String color;
#Column(name = "page_number")
private Integer pageNumber;
#Column(name = "read")
private Boolean read;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "id_author")
private Author author;
...
...
}
Problem is : with my job executing my step, I'm having the books but with an author = null. So the foreign key is not mapped correctly, all the other String/Integer/Boolean fields are retrieved correctly.
I'm new with Spring batch so maybe I'm using a wrong kind of reader.
Any one has an idea about the problem ? Thanks
Please refer this- You are using JDBC Item reader that is mapped to native columns and properties are binded by BeanPropertyRowMapper
https://docs.spring.io/spring-batch/docs/current/reference/html/readersAndWriters.html#JdbcCursorItemReader
Change the query join with AUTHOR tables as a native query or use JPA readers to support ORM
Below reference will give repository reader
https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/data/RepositoryItemReader.html
Below example give some idea to implement and its SO references
https://github.com/gpassini/Spring-Batch-RepositoryItemReader-NativeQuery-Example/tree/master/src/main

Exception while persisting JPA object in DB having one to many relation

hi have two tables in picture table a and table b as follows :
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "JOURNAL_CATEGORY_ID_GENERATOR", allocationSize = 1, sequenceName = "clm_jounal_category_config_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JOURNAL_CATEGORY_ID_GENERATOR")
#Column(name = "CLAIM_ID")
private String claimId;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "country")
private List<ClaimDTLS> claimDetails;
}
B Primary Key:
#Embeddable
public class BPK implements Serializable {
#Column(name = "code")
private String code;
#Column(name = "CLAIM_ID")
private String claimId;
}
B Entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
protected BPK bpk;
#Column(name = "name")
private String name;
#MapsId("country_code")
#JoinColumn(name = "claimId", referencedColumnName = "claimId", insertable = false, updatable = false)
#ManyToOne
private A a;
}
when i try to persist object of A type in Db the value of table b claim id is not set and is intialized with zero.
Also primary key of table A is generated with a oracle sequence.
any help will be welcomed.
thanks in advance
Sequence values are numbers and when JPA use them as a generator it call the setter method of the entity PK. Now, you defined your PK as a string while you use a sequence and so no matching setter can be found. Change the type of you PK to be Long and things shall work

location_id cannot be null while trying save using a Spring JPA 2 repository

I've a Spring JPA 2 entity class as
#Entity
#Table(name = "hotels")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Hotel {
#Id
#Column(name = "hotelId")
#JsonProperty("hotel_id")
private Integer hotelId;
#OneToOne(targetEntity = Location.class, cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "location_id")
#JsonProperty("location")
private Location location;
....
}
and the Location class is given as
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer location_id;
#Column
#JsonProperty("latitude")
private Double latitude;
#Column
#JsonProperty("longitude")
private Double longitude;
}
When I'm doing an hotelRepository.save(hotel) I'm getting Column 'location_id' cannot be null. I get the populated Hotel by consuming a REST web-service.
Can anyone please help me! Appreciated!

QueryDSL / JPQL : how to build a join query?

I've tried to read through the QueryDSL docs but I am still very confused. I'm accustomed to writing a lot of SQL, but this is my first real crack at using QueryDSL w/ JPQL (JPA2).
I have the following entity:
#Entity
public class Provider implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
private String name;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "provider_contact", joinColumns = #JoinColumn(name = "contact_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "provider_id", referencedColumnName = "id"))
#OrderColumn
private Collection<Contact> contact;
}
where Contact is a simple entity with an id for a pk.
#Entity
public class Contact {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
/**
* User first name
*/
#NotNull
private String firstName;
/**
* User last name
*/
#NotNull
private String lastName;
}
I'm trying to write a query which returns a Contact object given a specific Contact.id and Provider.id. If the Contact object is not a part of the Provider's Contact collection, I'm looking for a null value.
I've tried the following:
public Contact getContact( long providerId, long contactId ){
Predicate p = QProvider.provider.id.eq(providerId).and(QContact.contact.id.eq(contactId));
JPQLQuery query = new JPAQuery(em);
return query.from(QProvider.provider).innerJoin(QProvider.provider.contact).where(p).singleResult(QContact.contact);
}
but I'm getting the following error:
Caused by: java.lang.IllegalArgumentException: Undeclared path 'contact'. Add this path as a source to the query to be able to reference it.
at com.mysema.query.types.ValidatingVisitor.visit(ValidatingVisitor.java:78)
at com.mysema.query.types.ValidatingVisitor.visit(ValidatingVisitor.java:30)
at com.mysema.query.types.PathImpl.accept(PathImpl.java:94)
I'm presuming it has something to do with the fact that my predicate references QContact.contact direction and not part of the QProvider.provider.contact object, but I'm really at a loss as to figure out how this should be done.
Am I even on the right track? I'm not even sure my join is correct either.
This should work
public Contact getContact(long providerId, long contactId) {
QProvider provider = QProvider.provider;
QContact contact = QContact.contact;
return new JPAQuery(em).from(provider)
.innerJoin(provider.contact, contact)
.where(provider.id.eq(providerId), contact.id.eq(contactId))
.singleResult(contact);
}