RedisTemplate use GenericFastJsonRedisSerializer can't deserialize parent class attributes - java

First of all, I create a parent class implements Serializable, then create a child class to extends it. And I use RedisTemplate with GenericFastJsonRedisSerializer config to put data into redis, but when I get it back from Reids, the data is changed.Please tell me why and how to fix it?
#Data
public class People implements Serializable {
public Long id;
public String name;
}
#EqualsAndHashCode(callSuper = true)
#Data
public class Student extends People{
private String sName;
}
#Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
RedisSerializer keySerializer = new StringRedisSerializer();
RedisSerializer valueSerializer = new GenericFastJsonRedisSerializer();
redisTemplate.setDefaultSerializer(valueSerializer);
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashKeySerializer(keySerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
RedisUtil.init(redisTemplate);
return redisTemplate;
}
#Autowired
private RedisTemplate<String, Object> redisTemplate;
#Test
public void cacheDeserialize(){
Student student = new Student();
student.setId(11111111111111111L);
student.setName("parent name");
student.setSName("child name");
redisTemplate.opsForValue().set("parent-child-test", student);
Object value = redisTemplate.opsForValue().get("parent-child-test");
System.out.println(value); // print:Student(sName=child name), but I wish it print Student(id=111111111,name=parent name,sName=child name)
}
But:
Test result image

Solution one: use Lombok.annotation #ToString(callSuper = true) in chile class;
Solution two: take parent class attrs to child class;

You need to implement both classes with Serializable and define serialVersionUID like this
public class People implements Serializable {
private static final long serialVersionUID = 7156526077883281623L;
}

Related

Spring Data Mongo throw CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class java.lang.Class, types=null}

When save document that have a property Class<?> catch exception CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class java.lang.Class, types=null}
#Document("my_document")
public class MyDocument {
#Id
private String id;
#Field("jPt")
private JobParameter<?> jobParameter;
}
public class JobParameter<T> implements Serializable {
private T value;
private Class<T> type;
private boolean identifying;
}
Trying added Converter<JobParameter, Document> but it doesn't has effect
#Bean
public MongoCustomConversions customConversions(List<Converter<?, ?>> foreignConverters) {
List<Converter<?, ?>> converters = new ArrayList<>(List.of(
JSR310DateConverters.ZonedDateTimeToDateConverter.INSTANCE,
JSR310DateConverters.DateToZonedDateTimeConverter.INSTANCE,
JSR310DateConverters.LongToDurationConverter.INSTANCE,
JSR310DateConverters.DurationToLongConverter.INSTANCE,
**new JobParameterMongoConverter()**
));
converters.addAll(foreignConverters);
MongoCustomConversions mongoCustomConversions = new MongoCustomConversions(converters);
MongoCustomConversions.create(adapter -> adapter.useNativeDriverJavaTimeCodecs()
.registerConverters(converters));
return mongoCustomConversions;
I was not using the correct annotation on the converter. I used #ReadingConverter instead of #WritingConverter.

spring boot redis boolean value is not working

I am trying to get data from redis using crudRepository but it is not filtering by the boolean field. When I write repository.findAll() i am getting a list of objects and this object list's elements has a false field as I saved. But if I write a query for filtering just 'false' ones, it is returning only an empty array. Here is my code:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#RedisHash("StateData")
public class StateDataCache implements Serializable {
#Id String id;
private String deviceId;
private String topic;
private String message;
private String lat;
private String lon;
private boolean isActive;
}
#Repository
public interface StateDataCacheRepository extends CrudRepository<StateDataCache, String> {
List<StateDataCache> findAllByActiveFalseAndDeviceId(String deviceId);
List<StateDataCache> findAllByActiveIs(boolean sent);
List<StateDataCache> findAllByActiveFalse();
}
#Configuration
#EnableRedisRepositories
public class RedisConfig {
#Value("${spring.data.redis.host}")
private String redisHost;
#Value("${spring.data.redis.port}")
private int redisPort;
#Bean
public LettuceConnectionFactory redisStandAloneConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(redisHost, redisPort));
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisStandAloneConnectionFactory());
return template;
}
}

How can I use spring injection, A repository class in a mapstruct mapper?

#Data
public class FilesDTO {
private int issue;
private String uniqueStr;
private StorageDomain xml;
private StorageDomain pdf;
private StorageDomain stop;
}
#Data
public class BackHalfDomain {
private int articleId;
private String uniqueStrr;
private long xmlContentId;
private long pdfContentId;
private long stopId;
private int issueNumber;
}
Using a repository class I have to fetch a StorageDomain object from the ID in BackHalfDomain. So I have to map StorageDomain object with respective fields.
like StorgeDomain sd = repo.findById(id).get(); and set this sd object in FilesDTO's xml field and so on.
This is my mapper
#Mapper(componentModel = "spring")
public interface FilesDTOMapper {
public static final FilesDTOMapper fileDTOMapper = Mappers.getMapper(FilesDTOMapper.class);
#Mapping(target = "issue", source = "domain.issueNumber")
#Mapping(target = "DOI", source = "domain.doi")
public FilesDTO map(BackHalfDomain domain);
}
I have used uses but wasn't successful.
I have used #Mapping(target="xyz", expression="java(repo.findById(id))")"
but all I got was NullPointerException
Spring injection isin't working.
Can someone have a solution for this? I am just started with mapstruct
Since mapstruct 1.2 you can use a combination of #AfterMapping and #Context.
#Mapper(componentModel="spring")
public interface FilesDTOMapper {
#Mapping(target = "xyz", ignore = true)
#Mapping(target = "issue", source = "domain.issueNumber")
#Mapping(target = "DOI", source = "domain.doi")
FilesDTO map( BackHalfDomain domain, #Context MyRepo repo);
#AfterMapping
default void map( #MappingTarget FilesDTO target, BackHalfDomain domain, #Context MyRepo repo) {
target.setXYZ(repo.findById(domain.getId()));
}
}
In 1.1 you would need to transform the mapper to a abstract class
#Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring",
uses = {})
public abstract class FilesDTOMapper {
#Autowired
protected MyRepo repo;
#Mapping(target = "issue", source = "domain.issueNumber")
#Mapping(target = "DOI", source = "domain.doi")
#Mapping(target="xyz", expression="java(repo.findById(domain.getId())))")
public FilesDTO map(BackHalfDomain domain);
}
I ran into this same problem. The solution was to use a Decorator as suggested in this answer. Following your code, the solution would be something like the following.
First, we have to specifiy the Decorator in the Mapper:
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
#Primary
#DecoratedWith(FilesDTOMapperDecorator.class)
public interface FilesDTOMapper {
// basic mapping here
public FilesDTO map(BackHalfDomain domain);
}
Then, implement the Decorator:
public abstract class FilesDTOMapperDecorator implements FilesDTOMapper {
#Autowired
#Qualifier("delegate")
private FilesDTOMapper delegate;
#Autowired
private SomeRepository someRepository;
#Override
public FilesDTO map(BackHalfDomain domain) {
// Delegate basic mapping
FilesDTO filesDTO = delegate.map(domain);
// Use the repository as needed to set additional mapping
filesDTO.setSomeValue(repo.findById(id).getSomeValue());
return filesDTO;
}
}

Can't bind properties with #Bean and #ConfigurationProperties

I am reading configuration from properties file. Now I am having an error that I think is related to sequence of initialization of spring bean.
If I do private Map name = new HashMap<>(); It can be successfully load from properties file.
But now I am having Could not bind properties to ServiceNameConfig
I don't know why this happen and how to deal with it.
#ConfigurationProperties(prefix = "amazon.service")
#Configuration
#EnableConfigurationProperties(ServiceNameConfig.class)
public class ServiceNameConfig {
//If I do private Map<String, String> name = new HashMap<>(); It can be successfully load from properties file.
private Map<String, String> name;
#Bean(value = "serviceName")
public Map<String, String> getName() {
return name;
}
public void setName(Map<String, String> name) {
this.name = name;
}
}
its usage;
#Autowired
#Qualifier("serviceName")
Map<String, String> serviceNameMap;
You can replace your config class to be like this (simpler);
#Configuration
public class Config {
#Bean
#ConfigurationProperties(prefix = "amazon.service")
public Map<String, String> serviceName() {
return new HashMap<>();
}
}
For #ConfigurationProperties injection, you'd need to supply an empty instance of the bean object. Check more about it on baeldung
Or an alternative way, you can use a pojo class to handle the configuration. For example;
You have properties like;
amazon:
service:
valueA: 1
valueB: 2
details:
valueC: 3
valueD: 10
and you can use a pojo like the following;
class Pojo {
private Integer valueA;
private Integer valueB;
private Pojo2 details;
// getter,setters
public static class Pojo2 {
private Integer valueC;
private Integer valueD;
// getter,setters
}
}
and use it in the config class like;
#Configuration
public class Config {
#Bean
#ConfigurationProperties(prefix = "amazon.service")
public Pojo serviceName() {
return new Pojo();
}
}

org.springframework.core.convert.converter.Converter convert some classes to one(or class+parameter to one class)

I have org.springframework.core.convert.converter.Converter
#Component
public class CatalogConverter implements Converter<ServiceCatalogType, Catalog> {
#Override
public Catalog convert(ServiceCatalogType source) {
Catalog catalog = new Catalog();
//convert
return catalog;
}
}
And I register this converter:
#Configuration
public class ConvertersConfig {
private final CatalogConverter catalogConverter;
#Autowired
public ConvertersConfig(CatalogConverter catalogConverter) {
this.catalogConverter = catalogConverter;
}
#Bean(name="conversionService")
ConversionService conversionService() {
ConversionServiceFactoryBean factoryBean = new ConversionServiceFactoryBean();
HashSet<Converter> converters = new HashSet<>();
converters.add(catalogConverter);
factoryBean.setConverters(converters);
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
}
But I need pass some parameter to my custom converter. I have some ways:
Pass it in constructor - but how can register this converter?
Use wrapper
class Wrapper{
private ServiceCatalogType catalog;
private String uuid;
}
and change converter like this:
implements Converter<ServiceCatalogType, Wrapper>
Or maybe Spring has another way?
EDIT
I need next.
in service:
pulic void send() {
ServiceCatalogType cs = getServiceCatalogType();//get from net
User user = getUser();//get from db
//convert cs+user to Catalog(all cs fields+ some user fields to catalog)
Catalog catalog = conversionService.convert(cs, user, Catalog.class);
}
EDIT2
Wrapper implementation:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class CatalogWrapper {
private ServiceCatalogType serviceCatalogType;
private User user;
}
CatalogWrapper wrapper = new CatalogWrapper(getServiceCatalog(), getUser);
catalog = conversionService.convert(wrapper, Catalog.class);

Categories