Builder class does not have build method (name: 'build') - Jackson - spring

This error occurs when objectMapper.convertValue(cityEntity, City.class)) is called.
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Builder class com.example.PostgresApp.dto.City$Builder does not have build method (name: 'build')
package com.example.PostgresApp.dto;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.*;
import org.apache.commons.lang3.StringUtils;
#Value
#Builder(builderClassName = "Builder")
#JsonDeserialize(builder = City.Builder.class)
public class City {
String name;
String description;
#JsonPOJOBuilder(withPrefix = StringUtils.EMPTY)
public static class Builder {
}
}
Service calling repo seems to be where the exception is thrown
public List<City> getCities(){
return cityRepo.findAll().stream().map(cityEntity -> objectMapper
.convertValue(cityEntity, City.class))
.collect(Collectors.toList());
}

The problem is that Jackson cannot deserialize the object value.
My solution was to add the following annotations to my class:
// constructor with no args
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
// constructor with all args
#AllArgsConstructor
// ignore unknown properties during deserialization
#JsonIgnoreProperties(ignoreUnknown = true)
My class ended up looking like this:
#Getter
#Builder
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyClass {
private boolean flag;
private boolean flag2;
private MyClassA objectA;
private MyClassB objectB;
}
If you want to read more on why should we use #NoArgsConstructor and #AllArgsConstructor together, here is a good answer.

Are You sure You always pass name and description to the class Builder?
I got the same error and In my case I was trying to to use a generated Builder to create an Object but I did not pass all of the arguments, so the generated method was not the one spring was looking for. It was searching the N+1 arguments method, but I was passing only N arguments. In this case it will look for a different method signature that can not find.

Related

Spring can't find implementation

Here is my folder structure:
In my IAppUserMapper I have a method to convert every AppUser entity instance to Data Transfer Object Model. Here is the code in IAppUserMapper interface:
import com.server.ecommerceapp.dto.AppUserDTO;
import com.server.ecommerceapp.model.AppUser;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
#Mapper
public interface IAppUserMapper {
IAppUserMapper appUserMapper = Mappers.getMapper(IAppUserMapper.class);
#Mapping(target = "username")
#Mapping(target = "email")
#Mapping(target = "password")
#Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")
AppUserDTO toAppUserDTO(AppUser appUser);
default String getRoleName(Role role) {
return role.getRoleName();
}
}
And here is the MapperConfiguration class code where I configure both Product and user mappers:
import com.server.ecommerceapp.mapper.IAppUserMapper;
import com.server.ecommerceapp.mapper.IProductMapper;
import org.mapstruct.factory.Mappers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MapperConfiguration {
#Bean
public IAppUserMapper appUserMapper() {
return Mappers.getMapper(IAppUserMapper.class);
}
#Bean
public IProductMapper productMapper() {
return Mappers.getMapper(IProductMapper.class);
}
}
The error I get:
Error creating bean with name 'appUserMapper' defined in class path
resource
[com/server/ecommerceapp/configuration/MapperConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [com.server.ecommerceapp.mapper.IAppUserMapper]: Factory
method 'appUserMapper' threw exception; nested exception is
java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot
find implementation for com.server.ecommerceapp.mapper.IAppUserMapper
I was told I should make META-INF package in resources, with service package and the com.server.ecommerceapp.mapper.AppUserMapper txt with the content same as the name of the file, so that Spring can scan and find the package following the path:
src/main/resources/META-INF/service/com.server.ecommerceapp.mapper.AppUserMapper
but it didnt work. Any ideas how to solve this, and by the way, is it bad practise to start interface names with capital I cause Im coming from ASP?
Edit:
I added #Mapper(componentModel = "spring") to my interfaces and implemented them as DI with Autowired. I dont know if its related to that problem that I had but now I get error that it cant find collectors. Im trying to map a collection of Roles from AppUser to AppUserDTO. Here are both AppUser and AppUserDTO classes:
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Data
public class AppUser {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "email", nullable = false, unique = true)
private String email;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = EAGER)
private Collection<Role> roles;
}
And DTO:
#NoArgsConstructor
#AllArgsConstructor
#Data
public class AppUserDTO {
private String username;
private String email;
private String password;
private Collection<String> roles;
}
So you're using Spring, but you are trying to not use Spring.
You should make your mappers use Spring component model:
#Mapper(componentModel = "spring")
public interface MyMapper {
Target map(Source source);
}
Check docs for dependency injection: https://mapstruct.org/documentation/stable/reference/html/#using-dependency-injection
Or do it with shared configuration: https://mapstruct.org/documentation/stable/reference/html/#shared-configurations
After that you can just #Autowired MyMapper myMapper; as any other Spring bean. No need to create instance in interface (the "Mappers.getMapper" thing) and no need to create mappers in java configuration, bean creation will be handled by framework.
#Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")
now I get error that it cant find collectors
You are using an expression with Collectors class. As stated in the documentation https://mapstruct.org/documentation/stable/reference/html/#expressions:
Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the TimeAndFormat class (unless it’s used otherwise explicitly in the SourceTargetMapper). This can be resolved by defining imports on the #Mapper annotation.
So you either need to fully qualify java.util.stream.Collectors in your expression or set "imports" parameter in #Mapper annotation: #Mapper(imports = Collectors.class).
I would also say, you could just write a normal Java method for roles mapping and not be dealing with expressions. But that's up to your taste.
The file name of the service should be the interface and its content the implementation. You have named it by the implementation.

jHipster use enum in specification to find only entites that have one concrete value of Enum

There is entity:
#Getter
#Setter
#ToString()
#Entity
#Table
#Builder
#NoArgsConstructor
#AllArgsConstructor
class DocumentEntity implements Serializable {
(...)
#Enumerated(EnumType.STRING)
public DocumentStatus documentStatus;
}
I have serializable class:
#Data
#NoArgsConstructor
public class DocumentCriteria implements Serializable, Criteria {
private StringFilter documentStatus;
(...)
}
and auto generated class:
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(DocumentEntity.class)
public abstract class DocumentEntity_ {
public static volatile SingularAttribute<DocumentEntity, DocumentStatus> documentStatus;
public static final String DOCUMENT_STATUS = "documentStatus";
(...)
}
DocumentStatus is simple enum:
public enum DocumentStatus {
A,
B
}
I want to add specification that I search only entites with DocumentStatus set to A:
private Specification<DocumentEntity> createSpecification(DocumentCriteria criteria) {
Specification<DocumentEntity> specification = Specification.where(null);
if (criteria != null) {
StringFilter globalStringFilter = new StringFilter();
globalStringFilter.setContains(DocumentStatus.A.name());
specification.and(buildStringSpecification(globalStringFilter, DocumentEntity_.documentStatus));
I have an error here saying:
Required type:
SingularAttribute
<? super DocumentEntity,
String>
Provided:
SingularAttribute
<DocumentEntity,
DocumentStatus>
How can I search for it?
I tried also:
specification = specification.and(buildSpecification(criteria.getDocumentStatus(), DocumentStatus.A.name());
but it says:
Cannot resolve method 'buildSpecification(StringFilter, String)
Should i use other type than StringFilter even though database type is varchar ?
Does trying using String like: DocumentStatus.A.name() does not help here ?
Another option that comes in my head is writing something like that:
RangeFilter<DocumentStatus> globalStringFilter = new RangeFilter<DocumentStatus>();
List<DocumentStatus> documentStatuses = new ArrayList<>();
documentStatuses.add(DocumentStatus.A);
globalStringFilter.setIn(documentStatuses);
specification.and(buildRangeSpecification(globalStringFilter, DocumentEntity_.documentStatus));
And changing DocumentCriteria documentStatus field type to RangeFilter.
Above option does not seem to take effect when running application.
Solution:
RangeFilter<DocumentStatus> globalStringFilter = new RangeFilter<DocumentStatus>();
List<DocumentStatus> documentStatuses = new ArrayList<>();
documentStatuses.add(DocumentStatus.A);
globalStringFilter.setIn(documentStatuses);
specification=specification.and(buildRangeSpecification(globalStringFilter, DocumentEntity_.documentStatus));
And changing DocumentCriteria documentStatus field type to RangeFilter.
Make sure that specification= is present, so that result is consumed.

How to make Set using spring-data-aerospike

Environment:
spring-boot v2.0.4 RELEASE
spring-data-aerospike v2.0.1.RELEASE
java - 8
Here are my application code and properties.
// application.properties
aerospike.hosts=xxx:3000
aerospike.namespace=test
// aerospike configuration class
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(AerospikeConfiguration.AerospikeConfigurationProperties.class)
#EnableAerospikeRepositories(basePackageClassses = TestAeroRepository.class)
public class AerospikeConfiguration extends AbstractAerospikeDataConfiguration {
private final AerospikeConfigurationProperties aerospikeConfigurationProperties;
#Override
protected Collection<Host> getHosts() {
return Host.parseServiceHosts(aerospikeConfigurationProperties.getHosts());
}
#Override
protected String nameSpace() {
return aerospikeConfigurationProperties.getNamespace();
}
#Data
#Validate
#ConfigurationProperties("aerospike")
public static class AerospikeConfigurationProperties {
#NotEmpty
String hsots;
#NotEmpty
String namespace;
}
}
# Entity class
#Value
#Document
#Builder(toBuilder = true)
#AllArgsConstructor
public class testEntity() {
#Id
int id;
#Field
String name;
#Field
String timestamp;
}
#Repository
public interface TestAeroRepository extends AerospikeRepository<TestEntity, Integer> {
}
public interface TestAeroService {
void save();
}
#Service
#RequiredArgsConstructor
public class TestAeroServiceImpl implements TestAeroService {
private final TestAeroRepository testAeroRepository;
#Override
public void save(TestEntity entity) {
testAeroRepository.save(entity);
}
}
I checked Aerospike client connection has no problem.
But error is occurred when save() method is executed.
org.springframework.cglib.core.ReflectUtils.defineClass(Ljava/lang/String;[BLjava/lang/ClassLoader;Ljava/security/ProtectionDomain;Ljava/lang/Class;)Ljava/lang/Class;
Have to make sets before execute the application? I didn't make sets.
Any problem with my code?
You’re using an old version of spring-data-aerospike (2.0.1.RELEASE was released on April 2019) is there any chance you can upgrade to the latest version? 2.4.2.RELEASE
You can see how to setup a simple spring data aerospike application here: https://medium.com/aerospike-developer-blog/simple-web-application-using-java-spring-boot-aerospike-database-and-docker-ad13795e0089
Please share the entire project’s code and the entire exception.
I would look into:
The configuration class (The Aerospike Beans creation).
The content of the testEntity class - are you using #Id annotation on the primary key field?
Extending the repository class with specifying the testEntity object (… extends AerospikeRepository<testEntity, Object> {) you can see an example in the link I added.
The set is automatically created and takes the name of your object class, which is testEntity in your case. For example, based on your code, if you do not specify a collection in the #Document annotation a set named "testEntity" will automatically be created. I added the #Document(collection = "testEntitys") annotation and all I did was create two set. Once you insert your first record, run the "SHOW SETS" aql command and it will be there. So that's one way to do it.

Facing issue while deserialization of JSON to object in spring boot

I am facing issue while deserialization the JSON to object in my springboot application.
I am having code as below :-
public interface IStatus{
}
#JsonTypeName("internal")
public enum InternalStatus implements IStatus{
INTERNAL_1,
INTERNAL_2
}
#JsonTypeName("external")
public enum ExternalStatus implements IStatus{
EXTERNAL_1,
EXTERNAL_2
}
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = As.PROPERTY,
property = "status")
#JsonSubTypes({
#JsonSubTypes.Type(value = InternalStatus.class, name = "internal"),
#JsonSubTypes.Type(value = ExternalStatus.class, name = "external")
})
#Setter
#Getter
public class Response{
IStatus status;
}
and my json corresponding to Response class is {status:"EXTERNAL_1"}
error details as below:-
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class IStatus]: missing type id property 'status'

Jackson java.util.Optional serialization does not include type ID

I got the following classes:
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "oid"
)
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "clazz")
#JsonSubTypes({
#JsonSubTypes.Type(value = MySubEntity.class, name = "MySubEntity"),
})
public abstract class Entity {
...
}
public class MySubEntity extends Entity {
...
}
Now when I serialize that MySubEntity wrapped in an Optional then JSON does not contain the clazz attribute containing the type ID. Bug? When I serialize to List<MySubEntity> or just to MySubEntity it works fine.
Setup: jackson-databind 2.9.4, jackson-datatype-jdk8 2.9.4, serialization is done in Spring Boot application providing a RESTful web service.
EDIT: Here is the Spring REST method that returns the Optional:
#RequestMapping(method = RequestMethod.GET, value = "/{uuid}", produces = "application/json")
public Optional<MySubEntity> findByUuid(#PathVariable("uuid") String uuid) {
...
}
EDIT:
I made a SSCCE with a simple Spring REST controller and two tests. The first test is using ObjectMapper directly which is successful in deserialization although the clazz is missing. The second test calls the REST controller and fails with an error because clazz is missing:
Error while extracting response for type [class com.example.demo.MySubEntity] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Missing type id when trying to resolve subtype of [simple type, class com.example.demo.MySubEntity]: missing type id property 'clazz'; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class com.example.demo.MySubEntity]: missing type id property 'clazz'
This, indeed, looks like a bug. There is one workaround that I can suggest for this case, is to use JsonTypeInfo.As.EXISTING_PROPERTY and add field clazz to your Entity. There only one case with this approach is that the clazz must be set in java code manually. However this is easy to overcome.
Here is the full code for suggested workaround:
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "oid"
)
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY, //field must be present in the POJO
property = "clazz")
#JsonSubTypes({
#JsonSubTypes.Type(value = MySubEntity.class, name = "MySubEntity"),
})
public abstract class Entity {
#JsonProperty
private String uuid;
//Here we have to initialize this field manually.
//Here is the simple workaround to initialize in automatically
#JsonProperty
private String clazz = this.getClass().getSimpleName();
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}

Resources