My elasticsearch version is: "2.4.2",
spring boot version is: "1.4.2.RELEASE".
My repository:
public interface StockDetailsEsRepository extends ElasticsearchRepository<StockDetails, String> {
StockDetails findTopByOrderByDateDesc();
}
StockDetails Class:
#Document(indexName = "stock_details", type = "daily", replicas = 0)
public class StockDetails {
#Id
private String id;
#Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd")
#JsonDeserialize(using = CustomLocalDateDeserializer.class)
#JsonSerialize(using = CustomLocalDateSerializer.class)
private LocalDate date;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
#JsonDeserialize(using = BigDecimalDeserializer.class)
#JsonSerialize(using = BigDecimalSerializer.class)
private BigDecimal openPrice;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
#JsonDeserialize(using = BigDecimalDeserializer.class)
#JsonSerialize(using = BigDecimalSerializer.class)
private BigDecimal maxPrice;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
#JsonDeserialize(using = BigDecimalDeserializer.class)
#JsonSerialize(using = BigDecimalSerializer.class)
private BigDecimal minPrice;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
#JsonDeserialize(using = BigDecimalDeserializer.class)
#JsonSerialize(using = BigDecimalSerializer.class)
private BigDecimal closePrice;
#Field(type = FieldType.Long)
private Long transactionsNumber = 0L;
#Field(type = FieldType.Long)
private Long volume;
private Stock stock;
The problem is when I use the query from my repository I get following exception:
java.lang.NullPointerException: null
at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForPage(ElasticsearchTemplate.java:308)
at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForObject(ElasticsearchTemplate.java:252)
I debug the ElasticsearchTemplate.queryForPage method and the argument CriteriaQuery criteriaQuery is null. The Same problem occurs when I change the method name in my repository to findTopByOrderByStockTickerDesc. It's wired because in official spring data elasticsearch documentation is almost the same example http://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#repositories.limit-query-result
findTopByOrderByAgeDesc();
Ofcourse I can achieve my goal in other way for example:
Sort sort = new Sort(new Sort.Order(Sort.Direction.DESC, "date"));
StockDetails topByOrderByDateDesc = stockDetailsEsRepository.findAll(new PageRequest(0, 1, sort))
.getContent()
.stream()
.findFirst()
.get();
But I would like use method which is describe in official documentation. Has someone similar problem?
Related
I would like to mapping a model object to dto model. I already have mapper for one of the object.
How can I reuse this mapper in another mapper which is in another class?
I have below as model
#Getter
#AllArgsConstructor
#ToString
public class History {
#JsonProperty("identifier")
private final Identifier identifier;
#JsonProperty("submitTime")
private final ZonedDateTime submitTime;
#JsonProperty("method")
private final String method;
#JsonProperty("reason")
private final String reason;
#JsonProperty("dataList")
private final List<Data> dataList;
}
#DynamoDBTable(tableName = "history")
#Data
#NoArgsConstructor
public class HistoryDynamo {
#DynamoDBRangeKey(attributeName = "submitTime")
#DynamoDBTypeConverted(converter = ZonedDateTimeType.Converter.class)
private ZonedDateTime submitTime;
#DynamoDBAttribute(attributeName = "identifier")
#NonNull
private Identifier identifier;
#DynamoDBAttribute(attributeName = "method")
private String method;
#DynamoDBAttribute(attributeName = "reason")
private String reason;
#DynamoDBAttribute(attributeName = "dataList")
private List<Data> dataList;
}
#Data
#DynamoDBDocument
#NoArgsConstructor
public class Identifier implements Serializable {
#DynamoDBAttribute(attributeName = "number")
private String number;
#DynamoDBAttribute(attributeName = "cityCode")
#NonNull
private String cityCode;
#DynamoDBAttribute(attributeName = "countryCode")
#NonNull
private String countryCode;
#DynamoDBTypeConverted(converter = LocalDateType.Converter.class)
private LocalDate mydate;
}
#Data
#EqualsAndHashCode
#NoArgsConstructor
#RequiredArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Identifier implements Serializable {
#NonNull
#lombok.NonNull
#NotNull
private String number;
#NonNull
#lombok.NonNull
#NotNull
private City city;
#NonNull
#lombok.NonNull
#NotNull
private Country country;
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'Z'")
#DateTimeFormat(pattern = "yyyy-MM-dd'Z'")
#NonNull
#lombok.NonNull
#NotNull
private LocalDate mydate;
}
And here is my mapping
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN, injectionStrategy = InjectionStrategy.CONSTRUCTOR, nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL)
public interface IdentifierMapper {
IdentifierMapper MAPPER = Mappers.getMapper(IdentifierMapper.class);
#Mappings({#Mapping(source = "identifier.number", target = "number"),
#Mapping(source = "identifier.city.code", target = "cityCode"),
#Mapping(source = "identifier.country.code", target = "countryCode"),
#Mapping(source = "identifier.mydate", target = "mydate")})
#Named("toIdentifierDynamo")
myproject.entity.dynamo.Identifier toIdentifierDynamo(myproject.model.Identifier identifier);
}
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN, injectionStrategy = InjectionStrategy.CONSTRUCTOR,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL, uses = {IdentifierMapper.class})
public interface HistoryMapper {
HistoryMapper MAPPER = Mappers.getMapper(HistoryMapper.class);
#Mappings({#Mapping(source = "identifier", target = "identifier", qualifiedByName = "toIdentifierDynamo"),
#Mapping(source = "method", target = "method"),
#Mapping(source = "reason", target = "reason"),
#Mapping(source = "timestamp", target = "timestamp")})
HistoryDynamo toHistoryDynamo(History history);
}
I would like to map History to HistoryDynamo and reuse IdentifierMapper to map one of the object in HistoryDynamo.
How can I use toIdentifierDynamo in toHistoryDynamo?
First of all you don't have to create instance in Spring. You could
just Autowire your Mapper.
Second of all you don't have to provide #Mapping annotation for
each field if it has the same name. Mapstruct will do it for you.
Your issue could be done using uses parameter of MapStruct mapper
HistoryMapper could have in #Mapper annotation parameter uses = IdentifierMapper.class. It will autowire IdentifierMapper into
HistoryMapper. By default it will do via field. You could change it
also in parameters: injectionStrategy = InjectionStrategy.CONSTRUCTOR and probably it will be enough as you
have the same name of field (identifier) and MapStruct should realize
that should be use IdentifierMapper
Using spring dependency can be injected easily as
private final HistoryMapper
historyMapper;
Also for fields with same name in target and source no need to use #Mapping, so in above case below mapper definition is enough to acheive desired result.
#Mapper(
componentModel = "spring",
injectionStrategy = InjectionStrategy.CONSTRUCTOR,
uses = {IdentifierMapper.class})
public interface HistoryMapper {
HistoryDynamo toHistoryDynamo(History history);
}
Refer github sample here https://github.com/rakesh-singh-samples/map-struct-samples/tree/stack-question-60523230/src/sample/mapstruct/mapper
I've searched a lot in this forum and other websites, but I'm still stuck with my problem.
I'm actually using modelmapper to convert an entity to a DTO.
Here is the Entity :
#Entity
public class Candidate implements Serializable {
#Id
#GeneratedValue (strategy=GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column (name = "name")
private String lastname;
#Column (name = "firstname")
private String firstname;
#Column (name = "phone")
private String phoneNumber;
#Column (name = "mail")
private String email;
#Column (name = "title")
private int title;
#OneToMany (mappedBy = "candidateId")
private Collection<Candidature> Interviews;
Here is Candidature Entity (that you find in the first Entity's collection):
public class Candidature implements Serializable {
#Id
#NotBlank
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn (name = "candidat_id")
private Candidate candidateId;
#Column(name = "interview")
#Temporal (TemporalType.DATE)
private Date dateInterview;
#Column(name ="status")
private String status;
And here is the DTO :
public class CandidateDTO {
private Long id;
private String lastname;
private String firstname;
private String phoneNumber;
private String email;
private String title;
private String dateLastInterview;
As you can see, there are some differences.
The problem I face is that the last attribute of DTO (dateLastInterview) comes from the Collection<Candidature> and more precisely it must be the last dateInterview converted into String.
Convert a Date into String is not a problem. Getting the last item of a Collection neither.
But I can't make it work with modelMapper.
Here is a sample code I tried :
modelMapper = new ModelMapper();
Converter<Candidate, CandidateDTO> converter = new Converter<Candidate, CandidateDTO>()
{
#Override
public CandidateDTO convert(MappingContext<Candidate, CandidateDTO> mappingContext) {
Candidate candidate = mappingContext.getSource();
CandidateDTO cdto = new CandidateDTO();
List<Candidature> list = (List) candidate.getInterviews();
Date date = list.get(list.size()-1).getDateInterview();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String dateInterviewConverted = df.format(date);
mappingContext.getDestination().setTitle(mappingContext.getSource().getTitle());
mappingContext.getDestination().setDateLastInterview(dateInterviewConverted);
return cdto;
}
};
modelMapper.createTypeMap(Candidate.class, CandidateDTO.class).setConverter(converter);
(and I tried, instead of the last line above : modelMapper.addConverter(converter); but same result)
But it doesn't work, I get all attributes at null.
I previously succeded using
map().setTitle(source.getTitle());
map().setDateLastInterview(dateInterviewConverted);
And then converting the Date to String in my DTO "set" method, but it seems that it shouldn't be here, but into the ModelMapper class or the class that is using it.
Do you have an idea ? I'm new with modelMapper, and I keep browsing google and I can't find (or maybe understand ?) any response that might help me.
Thanks
Ok I think I succeded.
Using the converter was the right thing, but I wasn't using it correctly. For the converter, the two objets that you put inside <> are the ones of the attributes concerned by the converter.
For example, for the first converter, I wanted to parameter the conversion of the Collection (coming from an object Candidate) to become a String (to match the attribute of the DTO).
So then you only have to create a PropertyMap with the Class and ClassDTO, and in the configure() method you only mention the attributes that will use special parameters (the other ones are correct since they respect the standard mapping).
Converter<Collection<Candidature>, String> convertLastDateToString = new Converter<Collection<Candidature>, String>() {
public String convert(MappingContext<Collection<Candidature>, String> context) {
List<Candidature> candidatureList = (List)context.getSource();
String dateInterviewConverted = "";
if (candidatureList.size() > 0) {
Date lastInterview = candidatureList.get(0).getDateInterview();
for (int i = 0; i < candidatureList.size(); i++) {
if (candidatureList.get(i).getDateInterview().after(lastInterview)) {
lastInterview = candidatureList.get(i).getDateInterview();
}
}
// converts the Date to String
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
dateInterviewConverted = df.format(lastInterview);
}
return dateInterviewConverted;
}
};
// allows custom conversion for Title attribute
// the source (Candidate) has a title attribute in int type
// the destination (CandidateDTO) has a title attributes in String type
Converter<Integer, String> convertTitleToString = new Converter<Integer, String>(){
public String convert(MappingContext<Integer, String> context){
return Title.values()[context.getSource()].toString();
}
};
// define explicit mappings between source and destination properties
// does only concernes the attributes that will need custom mapping
PropertyMap<Candidate, CandidateDTO> candidateMapping = new PropertyMap<Candidate, CandidateDTO>()
{
protected void configure()
{
// to map these two attributes, they will use the corresponding converters
using(convertTitleToString).map(source.getTitle()).setTitle(null);
using(convertLastDateToString).map(source.getCandidatures()).setDateLastInterview(null);
}
};
// add the mapping settings to the ModelMapper
modelMapper.addMappings(candidateMapping);
In my application, I am using Spring Data and hibernate as JPA provider to persist and read data.
I have top level Entity class:
#Entity
#Getter #Setter
#Table(name = "operation")
#Inheritance(strategy = InheritanceType.JOINED)
#EqualsAndHashCode(of = {"operationId"})
public abstract class Operation implements Serializable {
public static final int OPERATION_ID_LENGTH = 20;
#Id
#Column(name = "operation_id", length = OPERATION_ID_LENGTH, nullable = false, columnDefinition = "char")
private String operationId;
#Column(name = "operation_type_code")
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private String operationTypeCode;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "begin_timestamp", nullable = false)
private Date beginTimestamp = new Date();
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "end_timestamp")
private Date endTimestamp;
#Column(name = "operation_number", length = 6, columnDefinition = "char")
private String operationNumber;
#Enumerated(EnumType.STRING)
#Column(name = "operation_status", length = 32, nullable = false)
private OperationStatus status;
#ManyToOne(optional = false)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "terminal_id")
private Terminal terminal;
#Column(name = "training_mode", nullable = false)
private boolean trainingMode;
}
For inherited class I have corresponding repository:
public interface ConcreteOperationRepository extends JpaRepository<ConcreteOperation, String> {
#Query("SELECT o FROM ConcreteOperation o WHERE o.beginTimestamp BETWEEN :from AND :to AND o.status = :status AND o.terminal.deviceId = :deviceId AND o.trainingMode = :trainingMode")
Collection<ConcreteOperation> findOperations(#Param("from") Date startDay,
#Param("to") Date endDay,
#Param("status") OperationStatus status,
#Param("deviceId") String deviceId,
#Param("trainingMode") boolean trainingMode);
}
And I have integration test with following method:
#Transactional
#Test
public void shouldFindOperationByPeriodAndStatusAndWorkstationId() {
Date from = new Date(Calendar.getInstance().getTime().getTime());
List<String> terminalIds = loadTerminalIds();
List<OperationStatus> typeForUse = Arrays.asList(OperationStatus.COMPLETED,
OperationStatus.LOCKED, OperationStatus.OPEN);
int countRowsForEachType = 3;
int id = 100001;
for (String terminalId : terminalIds) {
for (OperationStatus status : typeForUse) {
for (int i = 0; i < countRowsForEachType; i++) {
concreteOperationRepository.save(createConcreteOperation(status, terminalId,
String.valueOf(++id)));
}
}
}
Date to = new Date(Calendar.getInstance().getTime().getTime());
for (String terminalId : terminalIds) {
for (OperationStatus status : typeForUse) {
Collection<ConcreteOperation> operations =
concreteOperationRepository.findOperations(from, to, status, terminalId, false);
assertEquals(countRowsForEachType, operations.size());
}
}
}
But this test fails when I using MySql database due to empty result (but passes when I switch to HSQLDB)
Also, this test passes if I put delay "Thread.sleep(1000)" for one second at the beginning of the test, just after the first line.
When I execute SQL from Hibernate log it gives me right result. What's wrong with my code?
In JPA, the Date requires a temporal hint. Normally, you could set the TemporalType when setting the JPA Query parameter:
query.setParameter("from", from), TemporalType.TIMESTAMP);
With Spring Data you need to use the #Temporal annotation, so your query becomes:
#Query("SELECT o FROM ConcreteOperation o WHERE o.beginTimestamp BETWEEN :from AND :to AND o.status = :status AND o.terminal.deviceId = :deviceId AND o.trainingMode = :trainingMode")
Collection<ConcreteOperation> findOperations(
#Param("from") #Temporal(TemporalType.TIMESTAMP) Date startDay,
#Param("to") #Temporal(TemporalType.TIMESTAMP) Date endDay,
#Param("status") OperationStatus status,
#Param("deviceId") String deviceId,
#Param("trainingMode") boolean trainingMode
);
I realized my problem. The problem was due to difference of precision between type of field in MySql (default timestamp precision cut of milliseconds) and Java date (with milliseconds)
I've altered my table:
ALTER TABLE transaction modify end_timestamp TIMESTAMP(6)
and that's solved my problem.
I need information how to store the best way a Document (Java POJO) with the Spring-Data-Elasticsearch #Document Annotation which includes a Map
#Document(indexName = "downloadclienterrors", type = "downloadclienterror")
public class DownloadClientErrorLogElasticsearch {
#Id
private Long id;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String host;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String shortMessage;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String fullMessage;
#Field(type = FieldType.Date)
private String clientTimestamp;
private Integer level;
private Map<String, String> additionalFieldList;
...
}
Like the POJO is created in this 1st class I can store it via my repository in the elastic search instance.
This is the way how I add then data to it, I wanna be flexible which JSON fields I add, because that's flexible from my client software.
additionalFieldList.put("url", "http://www.google.de");
additionalFieldList.put("user_agent", "Browser/1.0.0 Windows");
My problem is that I need also the fields in the additionalFieldList marked as .not_analyzed. (f.e additionalFieldList.url, additionalFieldList.user_agent).
I would like to have the same behaviour like with the FieldIndex.not_analyzed annotation on a String also on my Map but of course only for the value in the map.
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private Map<String, String> additionalFieldList;
But that doesn't work when I try to store the document. I receive a ugly Exception.
When someone knows a way, or how it would be better to design such a document in elasticsearch, because I am quit fresh and new in this area I would love to hear some comments.
Thanks before and grey greetings from Hamburg,
Tommy Ziegler
You can use #Mapping annotation to configure dynamic_templates.
Just put your mapping file in your classpath and annotate your POJO with #Mapping
Mapping example
JSON
{
"downloadclienterrors": {
"dynamic_templates": [
{
"additionalFieldList": {
"path_match": "additionalFieldList.*",
"mapping": {
"type": "string",
"index": "not_analyzed"
}
}
}
]
...
}
}
POJO
#Mapping(mappingPath = "/downloadclienterrors.json")
#Document(indexName = "downloadclienterrors", type = "downloadclienterror")
public class DownloadClientErrorLogElasticsearch {
...
}
What you have to do is to create a another class additional and add additionalFieldList there.
something like this-
public class additional {
private Map<String, String> additionalFieldList;
}
and then use this class in your pojo
#Document(indexName = "downloadclienterrors", type = "downloadclienterror")
public class DownloadClientErrorLogElasticsearch {
#Id
private Long id;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String host;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String shortMessage;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String fullMessage;
#Field(type = FieldType.Date)
private String clientTimestamp;
private Integer level;
#Field(type = FieldType.Nested)
private additional additional;
...
}
I fresh on spring technology and hibernate. Some days ago i create query getting all rows from table using repository. Today i was try get 2 fields from database. When i try read data form result list i getting Ljava.lang.Object; cannot be cast. This is my enity
#Entity
#Table(name = "cms")
public class Cms implements Serializable{
private static final long serialVersionUID = 1759832392332242809L;
#Id
#GeneratedValue
private Long id_page;
#Column(nullable = false)
private String title;
private String content;
#Temporal(TemporalType.DATE)
private Date createDate;
#Temporal(TemporalType.DATE)
private Date modifyDate;
#Column(nullable = true)
private int createBy;
#Column(nullable = true)
private int modifedBy;
#Column(nullable = false)
private Boolean inMenu;
public Cms(Long id_page, String title, String content, Date createDate,
Date modifyDate) {
this.id_page = id_page;
this.title = title;
this.content = content;
this.createDate = createDate;
this.modifyDate = modifyDate;
this.createBy = 1;
this.modifedBy = 1;
this.inMenu = true;
}
//getters setters to string
}
Repository
public interface CmsRepository extends Repository<Cms, Long>{
#Query("Select u.id_page,u.title from Cms u")
List<Cms> getMenu();
}
And takie this on controller
List<Cms> menus= cmsservice.menuAll();
System.out.println(menus.get(0).toString()); //error
Some one can explain me on example what is bad and how can fix this, this will helpfull for me.
Thats because you are retrieving individual properties - not the entire Cms Object.
I would use an instance of Query for this:
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("Select u.id_page,u.title from Cms u");
List<Object[]> results = query.getResultList();
for(Object[] elements: results){
Long id = Long.valueOf(String.valueOf(elements[0]));
String title = String.valueOf(elements[1]);
}
SELECT NEW org.agoncal.javaee7.CustomerDTO(c.firstName, c.lastName, c.address.
street1)
FROM Customer c