Spring data elastic search long to localdatetime conversion error - spring-data-elasticsearch

I have the following mapping
#Document(indexName = "some-index")
#Data
public class ElasticDocument {
#Id
#Field(type = FieldType.Text)
private String id;
#Field(type = FieldType.Date, format = DateFormat.custom)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSS")
private LocalDateTime issuedTimestamp;
}
The following repository
#Repository
public interface DocumentRepository extends ElasticsearchRepository<ElasticDocument, String> {
}
But the following query from spring data elastic search 4.0.3.RELEASE throws a conversion error:
Page<ElasticDocument> elasticDocuments = documentRepository.findAll(PageRequest.of(0, 10));
[MappingElasticsearchConverter.java:290] [Type LocalDateTime of
property ElasticDocument.issuedTimestamp is a TemporalAccessor
class but has neither a #Field annotation defining the date type nor a
registered converter for reading! It cannot be mapped from a complex
object in Elasticsearch!
[No converter found capable of converting from type [java.lang.Long]
to type [java.time.LocalDateTime]]
[org.springframework.core.convert.ConverterNotFoundException: No
converter found capable of converting from type [java.lang.Long] to
type [java.time.LocalDateTime]
I'm using elasticsearch 7.9.1 and spring data elasticsearch 4.0.3.RELEASE and from what i understood starting with spring data elasticsearch 4.x we don't need to create a custom conversion as long as i added the Field annotation at mapping

You need to add the pattern for your custom format in the #Field annotation
#Field(type = FieldType.Date, format = DateFormat.custom, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSS")

Since spring-data-elastic version 4.2, dateformat.custom is deprecated. You can use any of the enum date_hour_minute_second_xxx in org.springframework.data.elasticsearch.annotations.DateFormat. E.g
#Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second_millis)
private LocalDateTime createdDate;

Related

String constant reference as value of Annotation attribute causes compile error

I am using my properties file to get the values for #Scheduled annotation attributes.I am able to get the values from properties file but when I tries to pass String constant reference to Annotation attribute then compile time exception is raised.
#Slf4j
#CompileStatic
class TestJobService {
static lazyInit = false
public static String jobInterval = getSomePropertiesFileValues?.fixedRateInMS instanceof String? getSomePropertiesFileValues.fixedRateInMS:'10000'
#Scheduled(fixedDelayString = TestJobService.jobInterval)
void executeEveryTenSeconds() {
def date = new Date()
println date.format('yyyy/MM/dd HH:mm', TimeZone.getTimeZone('IST'))
}
}
Attribute 'fixedDelayString' should have type 'java.lang.String'; but
found type 'java.lang.Object' in
#org.springframework.scheduling.annotation.Scheduled
Then I tried to use String to pass like:
#Slf4j
#CompileStatic
class TestJobService {
static lazyInit = false
#Scheduled(fixedDelayString = '${getSomePropertiesFileValues.fixedRateInMS}')
void executeEveryTenSeconds() {
def date = new Date()
println date.format('yyyy/MM/dd HH:mm', TimeZone.getTimeZone('IST'))
}
}
OR
public static final String jobInterval = getSomePropertiesFileValues?.fixedRateInMS instanceof String? getSomePropertiesFileValues.fixedRateInMS:'10000'
prevents the variable from being treated as an inline constant and compiler complains of not being inline constant.
I understand that using single quote '${getSomePropertiesFileValues.fixedRateInMS}'we can get compiler to know that I want GString behaviour. But I don't know is this a bug in Groovy or its a functionality which I need to implement in some other way to pass the string values as annotation attributes. Any lead or any help is highly appreciable.

spring data Elasticsearch dynamic mapping

Given using annotation on both Class and fields levels to set my mappings settings and use settings file like this
#Setting(settingPath = "settings.json")
to define custom analyzers, how would I set mappings "dynamic": false ?
Setting the dynamic mapping type on document and objects was introduced in Spring Data Elasticsearch version 4.0.0. It can be defined like this (code from the tests):
#Document(indexName = "test-index-configure-dynamic-mapping")
#DynamicMapping(DynamicMappingValue.False)
class ConfigureDynamicMappingEntity {
#Nullable
#DynamicMapping(DynamicMappingValue.Strict)
#Field(type = FieldType.Object)
private Author author;
#Nullable
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}

Spring data neo4j - replacing provided Date converters with custom converter?

I initially had my #NodeEntity class having a #GraphProperty of java.util.Date type. The graph repository APIs automatically convert that to long / string value and stores it based on my property type.
As I wanted to import huge chunk of data from csv, I wanted this date field in my custom or pre-defined string format, say "yyyyMMdd". So I wrote my spring converter factories for both String -> Date and DAte -> String and registered them in the spring xml for conversion service. But unfortunately my converter was never called. Only the standard converter was called.
Later, when I wrap the date object inside MyDate class and changed the converter to String -> MyDAte and vice versa instead of java.util.Date, converter was called and the required serialization was done.
I dont want this MyDate wrapper around Date just for the sake of converter. Now, my question is, how to override the default date converter with my converters?
try this:
#Configuration
#EnableNeo4jRepositories
static class TestConfig extends Neo4jConfiguration {
#Bean
GraphDatabaseService graphDatabaseService() {
return new ImpermanentGraphDatabase();
}
#Bean
protected ConversionService neo4jConversionService() throws Exception {
ConversionService conversionService = super.neo4jConversionService();
ConverterRegistry registry = (ConverterRegistry) conversionService;
registry.removeConvertible(Date.class, String.class);
registry.removeConvertible(String.class, Date.class);
//add your own converters like this
registry.addConverter(new MyDateToStringConverter());
registry.addConverter(new MyStringToDateConverter());
return conversionService;
}
}

JAXB annotation need to be known to bind xml with my class

I have the following classes
#XmlRootElement(name = "ExecutionRequest")
#XmlAccessorType(XmlAccessType.FIELD)
public class ExecutionRequest {
#XmlElement(name="Command")
private String command;
#XmlElementWrapper(name="ExecutionParameters")
#XmlElement(name="ExecutionParameter")
private ArrayList<ExecutionParameter> ExecutionParameters;
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ExecutionParameter {
#XmlElement(name = "Key")
private String key;
#XmlElement(name = "Value")
private String value;
}
and when I marshall the ExecutionRequest object, I get the following XML -
<ExecutionRequest>
<Command>RetrieveHeader</Command>
<ExecutionParameters>
<ExecutionParameter>
<Key>tid</Key>
<Value>ASTLGA-ALTE010220101</Value>
</ExecutionParameter>
<ExecutionParameter>
<Key>ctag</Key>
<Value>dq</Value>
</ExecutionParameter>
</ExecutionParameters>
</ExecutionRequest>
It is working fine as per JAXB binding.
But I want the XML to have all key value collection within one ExecutionParameter like -
<ExecutionRequest>
<Command>RetrieveHeader</Command>
<ExecutionParameters>
<ExecutionParameter>
<Key>tid</Key>
<Value>ASTLGA-ALTE010220101</Value>
<Key>ctag</Key>
<Value>dq</Value>
</ExecutionParameter>
</ExecutionParameters>
</ExecutionRequest>
Is there any way to obtain xml like this by changing annotation.
Let me know in case of clarifications.
Thanks in advance.
There isn't metadata for that. You could get a compact XML representation (that is easily parseable) by mapping key and value with #XmlAttribute.
<ExecutionParameters>
<ExecutionParameter Key="a" Value="b"/>
<ExecutionParameter Key="c" Value="d"/>
</ExecutionParameters>
UPDATE
If you have to support this XML format, then you could use JAXB with XSLT to get the desired result:
// Create Transformer
TransformerFactory tf = TransformerFactory.newInstance();
StreamSource xslt = new StreamSource(
"src/example/stylesheet.xsl");
Transformer transformer = tf.newTransformer(xslt);
// Source
JAXBContext jc = JAXBContext.newInstance(ExecutionRequest.class);
JAXBSource source = new JAXBSource(jc, request);
// Result
StreamResult result = new StreamResult(System.out);
// Transform
transformer.transform(source, result);
For More Information
http://blog.bdoughan.com/2012/11/using-jaxb-with-xslt-to-produce-html.html

Why do I get a "Null value was assigned to a property of primitive type setter of" error message when using HibernateCriteriaBuilder in Grails

I get the following error when using a primitive attribute in my grails domain object:
Null value was assigned to a property of primitive type setter of MyDomain.myAttribute
org.hibernate.PropertyAccessException: Null value was assigned to a property of primitive type setter of MyDomain.myAttribute
at grails.orm.HibernateCriteriaBuilder.invokeMethod(HibernateCriteriaBuilder.java:1077)
According to this SO thread, the solution is to use the non-primitive wrapper types; e.g., Integer instead of int.
A null value cannot be assigned to a primitive type, like int, long, boolean, etc. If the database column that corresponds to the field in your object can be null, then your field should be a wrapper class, like Integer, Long, Boolean, etc.
The danger is that your code will run fine if there are no nulls in the DB, but will fail once nulls are inserted.
And you can always return the primitive type from the getter. Ex:
private Integer num;
public void setNum(Integer i) {
this.num = i;
}
public int getNum() {
return this.num;
}
But in most cases you will want to return the wrapper class.
So either set your DB column to not allow nulls, or use a wrapper class.
A primitive type cannot be null. So the solution is replace primitive type with primitive wrapper class in your tableName.java file.
Such as:
#Column(nullable=true, name="client_os_id")
private Integer client_os_id;
public int getClient_os_id() {
return client_os_id;
}
public void setClient_os_id(int clientOsId) {
client_os_id = clientOsId;
}
reference http://en.wikipedia.org/wiki/Primitive_wrapper_class to find wrapper class of a primivite type.
I'll try to make you understand with the help of an example. Suppose you had a relational table (STUDENT) with two columns and ID(int) and NAME(String). Now as ORM you would've made an entity class somewhat like as follows:-
package com.kashyap.default;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* #author vaibhav.kashyap
*
*/
#Entity
#Table(name = "STUDENT")
public class Student implements Serializable {
/**
*
*/
private static final long serialVersionUID = -1354919370115428781L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "NAME")
private String name;
public Student(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Lets assume table already had entries. Now if somebody asks you add another column of "AGE" (int)
ALTER TABLE STUDENT ADD AGE int NULL
You'll have to set default values as NULL to add another column in a pre-filled table. This makes you add another field in the class. Now the question arises whether you'll be using a primitive data type or non primitive wrapper data type for declaring the field.
#Column(name = "AGE")
private int age;
or
#Column(name = "AGE")
private INTEGER age;
you'll have to declare the field as non primitive wrapper data type because the container will try to map the table with the entity. Hence it wouldn't able to map NULL values (default) if you won't declare field as wrapper & would eventually throw "Null value was assigned to a property of primitive type setter" Exception.
use Integer as the type and provide setter/getter accordingly..
private Integer num;
public Integer getNum()...
public void setNum(Integer num)...
#Column(name ="LEAD_ID")
private int leadId;
Change to
#Column(name ="LEAD_ID")
private Integer leadId;
There are two way
Make sure that db column is not allowed null
User Wrapper classes for the primitive type variable like private int var; can be initialized as private Integer var;
Do not use primitives in your Entity classes, use instead their respective wrappers. That will fix this problem.
Out of your Entity classes you can use the != null validation for the rest of your code flow.
Either fully avoid null in DB via NOT NULL and in Hibernate entity via #Column(nullable = false) accordingly or use Long wrapper instead of you long primitives.
A primitive is not an Object, therefore u can't assign null to it.
#Dinh Nhat, your setter method looks wrong because you put a primitive type there again and it should be:
public void setClient_os_id(Integer clientOsId) {
client_os_id = clientOsId;
}
Change the parameter type from primitive to Object and put a null check in the setter. See example below
public void setPhoneNumber(Long phoneNumber) {
if (phoneNumber != null)
this.phoneNumber = phoneNumber;
else
this.extension = 0l;
}
Make sure your database myAttribute field contains null instead of zero.

Resources