I have two classes. RequestDTO and Entity. I want to map RequestDTO to the Entity. In that case, I want to insert one of the Entity property manually which means that property is not in the Request DTO. How to achieve this using modelmapper.
public class RequestDTO {
private String priceType;
private String batchType;
}
public class Entity {
private long id;
private String priceType;
private String batchType;
}
Entity newEntity = modelMapper.map(requestDto, Entity.class);
But this does not work, it says it can't convert string to long. I request a solution to this or a better approach to this.
If you want to perform the mapping manually (ideally for dissimilar objects)
You can check the documentation for dissimilar object mapping Property Mapping,
You can define a property mapping by using method references to match
a source getter and destination setter.
typeMap.addMapping(Source::getFirstName, Destination::setName);
The source and destination types do not need to match.
typeMap.addMapping(Source::getAge, Destination::setAgeString);
If you don't want to do the mapping field by field to avoid boilerplate code
you can configure a skip mapper, to avoid mapping certain fields to your destination model:
modelMapper.addMappings(mapper -> mapper.skip(Entity::setId));
I've created a test for your case and the mapping works for both side without configuring anything :
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Before;
import org.junit.Test;
import org.modelmapper.ModelMapper;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
public class ModelMapperTest {
private ModelMapper modelMapper;
#Before
public void beforeTest() {
this.modelMapper = new ModelMapper();
}
#Test
public void fromSourceToDestination() {
Source source = new Source(1L, "Hello");
Destination destination = modelMapper.map(source, Destination.class);
assertNotNull(destination);
assertEquals("Hello", destination.getName());
}
#Test
public void fromDestinationToSource() {
Destination destination = new Destination("olleH");
Source source = modelMapper.map(destination, Source.class);
assertNotNull(source);
assertEquals("olleH", destination.getName());
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Source {
private Long id;
private String name;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Destination {
private String name;
}
Related
Before going to actual issue let me brief what i am looking for.
I am looking for encrypt and decrypt the fields inside entity. in JPA, we can use Attribute converter and achieve this. but in spring data jdbc its not supported it seems.
So, i am trying to use customconverstions feature of spring data jdbc. here i am creating one type like below
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
public class EncryptionDataType {
private String value;
#Override public String toString() {
return value ;
}
}
in Pojo i will use this type as field.
#Table("EMPLOYEE")
#Builder
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#Data
public class Employee
{
#Column("name")
private EncryptionDataType name;
#Id
private Integer id;
#Version
private Long version;
}
so from this i am expecting to save the 'EncryptionDataType' as normal string column in mysql for this i have created converter for read and write
#WritingConverter
public class EncryptionDataTypeWriteConverter implements Converter<EncryptionDataType, String> {
#Override public String convert(EncryptionDataType source) {
return source.toString()+"add";
}
}
#ReadingConverter
public class EncryptionDataTypeReadConverter implements Converter<String, EncryptionDataType> {
#Override public EncryptionDataType convert(String source) {
return new EncryptionDataType(source);
}
}
configuring these converts in configuration file.
#Configuration
public class MyConfig {
#Bean
protected JdbcCustomConversions JdbcConversion(Dialect dialect) {
return new JdbcCustomConversions(
Arrays.asList(new EncryptionDataTypeReadConverter(),new EncryptionDataTypeWriteConverter()));
}
}
This configurations seems not working. i am getting below error.
PreparedStatementCallback; bad SQL grammar [INSERT INTO `encryption_data_type` (`name`, `value`) VALUES (?, ?)]; nested exception is java.sql.SQLSyntaxErrorException: Table 'testschema.encryption_data_type' doesn't exist
seems instead of converting my encryptionDataType to string its trying to insert into new table. Please help me. am i missing anything ?
Updated configuration code:
#Configuration
#EnableJdbcRepositories(transactionManagerRef = "CustomJdbcTranasactionManager", jdbcOperationsRef = "CustomJdbcOperationsReference", repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class, basePackages = {
"com.java.testy"
})
#EnableAutoConfiguration(exclude = {
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class,
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration.class
})
public class MyConfig {
#Bean
protected JdbcCustomConversions JdbcConversion(Dialect dialect) {
return new JdbcCustomConversions(
Arrays.asList(new EncryptionDataTypeReadConverter(),new EncryptionDataTypeWriteConverter()));
}
// creating beans for datasource,JdbcOperationsReference,JdbcTranasactionManager,JdbcConverter,JdbcMappingContext,DataAccessStrategy,JdbcAggregateTemplate
}
Make your configuration extend AbstractJdcbcConfiguration and overwrite jdbcConfiguration().
Just updating Jens Schauder's answer (I think it's just a typo - I would comment but don't have the rep):
Make your configuration extend AbstractJdcbcConfiguration and overwrite jdbcCustomConversions() (or possibly userConverters(), if that suits the purpose).
I want to create a yaml file from which I get my constants
constantsConfiguration.yml
constants:
myList:
-
id: 11
name: foo1
firstName: bar1
allowed: true
-
id: 22
name: foo2
firstName: bar2
allowed: false
the configuration class looks like this:
#Data
#Component
#PropertySource("classpath:constantsConfiguration.yml")
#ConfigurationProperties(prefix = "constants")
public class ConstantProperties {
private List<User> myList;
#Data
public static class User{
private String id;
private String name;
private String firstName;
private Boolean allowed;
}
}
and this is a dummy example of how I want use it
#Service
#RequiredArgsConstructor
public class MyService{
private final ConstantProperties constantProperties;
public Boolean isEmptyList(){
return CollectionUtils.isEmpty(constantProperties.getMyList());
}
}
constantProperties.getMyList() is always null
I am using spring boot : 2.5.12 and java 11
The root cause is that the new SpringBoot will not parse the properties file as yaml properties.
You need add a Yaml PropertiesSourceFactory class first. Like below:
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
Then in the class of: ConstantsProperties, you need specify the Factory class explicitly. like:
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import lombok.Data;
#Data
#Component
#PropertySource(value = "classpath:constantsConfiguration.yml", factory = YamlPropertySourceFactory.class)
#ConfigurationProperties(prefix = "constants")
public class ConstantProperties {
private List<User> myList;
#Data
public static class User {
private String id;
private String name;
private String firstName;
private Boolean allowed;
}
}
Finally, Please pay attention to your yaml file format.
Each separator should be 2 ' ' blank chars.
Please try it , it should work now.
I would like that my ParentClass has final fields, 'brokenChildList' list is wrapped xml element and list items have different tag than the list (<brokenChildList><brokenChild/></brokenChildList>).
Here is a snippet of code to reproduce my issues (imports are partially truncated, setters and getters omitted)
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
public class Main {
public static void main(String... args) throws IOException {
ObjectMapper xmlMapper = new XmlMapper();
String xmlString = "<ParentClass><childClass name=\"name1\" value=\"val1\"/><brokenChildList><brokenChild name=\"bc1\" reason=\"bc-val1\"/><brokenChild name=\"bc2\" reason=\"bc-val2\"/></brokenChildList></ParentClass>";
ParentClass parentClass = xmlMapper.readValue(xmlString, ParentClass.class);
StringWriter stringWriter = new StringWriter();
xmlMapper.writeValue(stringWriter, parentClass);
String serialised = stringWriter.toString();
System.out.println(serialised);
System.out.println(xmlString.equals(serialised));
}
public static class ChildClass {
#JacksonXmlProperty(isAttribute = true)
private String name;
#JacksonXmlProperty(isAttribute = true)
private String value;
//getters & setters
}
public static class BrokenChild {
#JacksonXmlProperty(isAttribute = true)
private String name;
#JacksonXmlProperty(isAttribute = true)
private String reason;
//getters & setters
}
public static class ParentClass {
private final ChildClass childClass;
private final List<BrokenChild> brokenChildList;
#JsonCreator
public ParentClass(
#JsonProperty("childClass") ChildClass childClass,
#JsonProperty("brokenChildList") List<BrokenChild> brokenChildList
) {
this.childClass = childClass;
this.brokenChildList = brokenChildList;
}
#JacksonXmlProperty(localName = "childClass")
public ChildClass getChildClass() {
return childClass;
}
#JacksonXmlElementWrapper(localName = "brokenChildList")
#JacksonXmlProperty(localName = "brokenChild")
public List<BrokenChild> getBrokenChildList() {
return brokenChildList;
}
}
}
The above code gives output with Jackson version 2.8.10:
<ParentClass><childClass name="name1" value="val1"/><brokenChildList><brokenChild name="bc1" reason="bc-val1"/><brokenChild name="bc2" reason="bc-val2"/></brokenChildList></ParentClass>
true
With Jackson version 2.9.0 it gives:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Duplicate property 'brokenChildList' for [simple type, class org.test.Main$ParentClass]
at [Source: (StringReader); line: 1, column: 1]
I would like to find a solution (and any version after 2.9.0) that will give same output with the attached code.
My failed attempts include:
Replacing #JacksonXmlElementWrapper(localName = "brokenChildList") with #JacksonXmlElementWrapper will rename wrapper element as 'brokenChild' which is undesirable.
Removing #JacksonXmlElementWrapper(localName = "brokenChildList") will rename wrapper element as 'brokenChild' which is undesirable.
This problem is really tricky because Jackson collects metadata from different places: fields, getters, setters, constructor parameters. Also, you can use MixIn but in your case it does not appear.
#JacksonXmlElementWrapper annotation can be attached to FIELD and METHOD type elements and this forces you to declare it on getter. Because ParentClass is immutable and you want to build it with constructor we need to annotate constructor parameters as well. And this is where collision appears: you have a constructor parameter with #JsonProperty("brokenChildList") annotation and getter with #JacksonXmlElementWrapper(localName = "brokenChildList") which reuses the same name. If you would changed localName to #JacksonXmlElementWrapper(localName = "brokenChildListXYZ") (added XYZ) everything would be deserialised and serialised but output would be different then input.
To solve this problem, we can use com.fasterxml.jackson.databind.deser.BeanDeserializerModifier class which allows to filter out fields we do not want to use for deserialisation and which creates collision. Example usage:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.stream.Collectors;
public class XmlMapperApp {
public static void main(String... args) throws IOException {
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config, BeanDescription beanDesc, List<BeanPropertyDefinition> propDefs) {
if (beanDesc.getBeanClass() == ParentClass.class) {
return propDefs.stream().filter(p -> p.getConstructorParameter() != null).collect(Collectors.toList());
}
return super.updateProperties(config, beanDesc, propDefs);
}
});
XmlMapper xmlMapper = XmlMapper.xmlBuilder()
.addModule(module)
.build();
//yours code
}
}
To create this example I used version 2.10.0.
See also:
Jackson 2.10 features
Jackson Release 2.10
I have written oauth2 social client but could not fetch authorized user's friends list
Please have a look at my code to see what's missing/
regards
please look at #RequestMapping("vkontakte/friends")
java 1.8 spring security
#SpringBootApplication
#RestController
#EnableOAuth2Client
public class SocialApplication extends WebSecurityConfigurerAdapter {
#Autowired
OAuth2ClientContext oAuth2ClientContext;
#RequestMapping({ "/user", "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
//TODO как это оформить на фронтенде?
#RequestMapping("/vkontakte/friends")
public Map<String,String> friends() {
OAuth2RestTemplate vkTemplate = new OAuth2RestTemplate(vk(), oAuth2ClientContext);
UserInfoTokenServices tokenServicesvk = new UserInfoTokenServices(vkResource().getUserInfoUri(), vk().getClientId());
tokenServicesvk.setRestTemplate(vkTemplate);
ObjectNode resultNode = vkTemplate.getForObject(vkResource().getUserFriendsInfoUri(), ObjectNode.class);
ArrayNode data = (ArrayNode) resultNode.get("data");
Map<String, String> map = new LinkedHashMap<>();
for (JsonNode dataNode : data) {
//TODO надо как то правильно все получить?
}
return map;
In order to fetch friends from Vkontakte, you must declare a friend object, which will contain all the fields JSON structure of a friend has.
According to documentation, every friend has an id, first name, and last name, however, the response object is a little bit more complex than we need, so you might remove what you do not need.
Assuming we need all of the attributes of the response we can come up with two objects: result and friend.
Vkontakte friend object
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class VkontakteFriend {
private Long id;
#JsonProperty("first_name")
private String firstName;
#JsonProperty("last_name")
private String lastName;
}
Generic result object
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class FriendResponse {
private Long count;
private List<VkontakteFriend> items;
}
The only thing is left is to call API and get your response mapped to Java objects.
vkTemplate.getForObject(vkResource().getUserFriendsInfoUri(), FriendResponse.class);
I am trying out the objectify(version 2.2.3) embedded classes example (wiki) on google app engine. I am getting this error:
java.lang.IllegalArgumentException: one: com.mypkg.LevelOne is not a supported property type.
at com.google.appengine.api.datastore.DataTypeUtils.checkSupportedSingleValue(DataTypeUtils.java:184)
The code I have is the same as the one in Wiki. The section in the controller:
EntityWithEmbedded ent = new EntityWithEmbedded();
ent.one = new LevelOne();
ent.one.foo = "Foo Value";
ent.one.two = new LevelTwo();
ent.one.two.bar = "Bar Value";
The EntityWithEmbedded class:
import javax.jdo.annotations.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class EntityWithEmbedded {
#Id public Long id;
#Embedded public LevelOne one;
//getter & setters here
}
Class levelOne:
import javax.persistence.Embedded;
public class LevelOne {
public String foo;
public #Embedded LevelTwo two;
//getter & setters here
}
Class LevelTwo:
public class LevelTwo {
public String bar;
//getter & setters here
}
So it is the basic example that I am trying out. Any ideas on what is missing?
You're using the wrong #Embedded annotation in EntityWithEmbedded.
Use javax.persistence.Embedded rather than javax.jdo.annotations.Embedded