Consider below classes. How do I convert class A to B using Dozer without using custom converters?
interface IProperty<T> {
T getValue();
String getNilReason();
....
}
class Property<T> implements IProperty<T> {
T value;
String nilReasons();
...setters and geters
}
class X1 {
String a;
}
class X2 {
String a;
}
class A {
IProperty<X1> x1;
}
class B {
X2 x2;
}
Can someone provide a Dozer mapping XML for above classes?
I did a code review of Dozer and as far as I understood it doesn't support custom generics, but just the Java collection generics.
I'm thinking that I will take a stab and implement the support I need in Dozer by myself.
Related
I have a CXF generated class which has a collection as its only property. I need to map a single value from my DTO into an element of that collection, as in:
class DTO {
String dto;
}
class A {
String a;
}
class B {
List<A> b;
}
interface Mapper {
#Mappings({
#Mapping(source="dto", target="b.a")
})
B getBfromDTO(DTO dto);
}
Should this really work?
If so, how could I configure this mapping?
I couldn't find out a way to do this as a mapping, and from other answers from #Gunnar I now understand this is not possible, but I found a workaround using the annotation #AfterMapping.
#AfterMapping allow to specify methods to be run after a mapping occurs, so I created mappings for the non-collection properties of my objects and an #AfterMapping for filling collections in the objects.
class DTO {
String a;
String x;
}
class A {
String a;
}
class B {
String x;
List<A> b;
}
abstract class Mapper {
#Mappings({
#Mapping(source="x", target="x")
})
protected abstract B getBfromDTO(DTO dto);
#AfterMapping
protected void fillCollections(DTO dto, #MappingTarget B b) {
b.getB().add(dto.a);
}
}
The following scenario:
public enum ChildType
{
Type1,
Type2,
Type3
}
public abstract class MyParentClass
{
public abstract ChildType Id { get; }
}
public class Child1 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type1; } }
}
public class Child2 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type2; } }
}
public class Child3 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type3; } }
}
and i would like to use autofac to register all the subtypes using their id as a key, so something like:
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(type => type.IsSubclassOf(typeof(MyParentClass)))
.Keyed<MyParentClass>(c => c.Id)
.SingleInstance();
now obviously the above doesn't work, but is there some way to achieve that without registering each subclass separately? I want to then be able to access them by the enum, i.e. at runtime when i don't know what the value of the enum will be:
public static MyParentClass GetSubClassByEnum(ChildType id)
{
AutofacHostFactory.Container.ResolveKeyed<MyParentClass>(id);
}
Unfortunately you probably won't be able to get exactly this setup working because it's sort of a chicken/egg problem - you want to resolve an object based on information that won't be available... unless you resolve the object.
One way to get this working is to use attributes rather than properties. Attributes are available before the type is instantiated so you could store the info there and achieve the desired result.
Autofac has attribute metadata support that allows you to create custom attributes to provide this sort of information. You could create an attribute that gets inherited and only allows one instance per class. Apply it with the default value to your base class, then when you need to override apply a new attribute on the derived class.
There is plenty of documentation with examples on the Autofac doc site showing how to work with this.
My document structure is like:
{
_id: "A",
groups:[{
groupId: "someId",
groupName: "someName",
params: {
type1: ["a", "b"],
type2: ["c", d]
}
}],
config: {
person: {}
dataDetails: {
dataTypeDetails: {},
dataList: ["dt1", "dt2"]
}
}
}
My Spring Data MongoDB model types look like this:
// Imports etc.
#Document
public class Entity {
#Id
private String _id;
private List<Group> groups;
private Config config;
// setters and getters
public class Group {
private String groupId;
private String groupName;
private ParamData params;
// setter and getters
}
public class ParamData {
private List<String> type1;
private List<String> type2;
}
public class Config {
private Map person;
private DataConfig dataDetails;
}
public class DataConfig {
private Map dataTypeDetails;
private List<String> dataList;
}
}
Stacktrace:
org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate com.****.common.models.Entity$ParamData using constructor public com.****.common.models.Entity$ParamData(com.****.common.models.Entity) with arguments com.****.common.models.Entity$Group#2eb61a7b
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:78)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:257)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:237)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1136)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$100(MappingMongoConverter.java:78)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1085)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:816)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:270)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:263)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:261)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:263)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:237)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readCollectionOrArray(MappingMongoConverter.java:861)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1134)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$100(MappingMongoConverter.java:78)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1085)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:816)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:270)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:263)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:261)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:263)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:237)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:201)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:197)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:78)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:2016)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1700)
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1523)
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1507)
at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:532)
at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:497)
at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:489)
My in DAO I am trying to fetch the document by identifier, but it is failing only for the values at dataDetails and params. If I comment out the ParamData param, I get error for DataConfig. The data was added using command line/node scripts and was not added using this code. But it is getting retrieved properly by node/mongoose as well as from command line.
This seems to be an issue with doubly nested inner classes and the synthetically generated constructors created by the compiler. I could reproduce that issue locally and see if we can provide a fix. In the meantime you have two options:
Turn the inner class into static ones as this will remove the synthetic constructors and instantiation will work correctly.
Nest the type declarations in the same way you nest the properties. I.e. move the ParamData class into the Group class, DataConfig into Config as that will cause the synthetic constructors created in a way they match instantiation order Spring Data currently relies on.
I'd suggest the former approach as it doesn't artificially bind the classes to instances of the outer class.
Failed to instantiate ... using constructor public ... ReflectionEntityInstantiator
says it cannot create the objects using reflection.
Do you have getters and setters for all the fields in all your classes? Your code above does not have them for ParamData, Config and DataConfig.
Also, if you happen to have non-default constructors in any of your classes make sure you have an empty argument constructor, else reflection will fail.
Getting the error in the title when using Spring Data REST. How to resolve?
Party.java:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, property="#class")
#JsonSubTypes({ #JsonSubTypes.Type(value=Individual.class, name="Individual") })
public abstract class Party {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected Long id;
protected String name;
#Override
public String toString() {
return id + " " + name;
}
...getters, setters...
}
Individual.java:
#Entity
public class Individual extends Party {
private String gender;
#Override
public String toString() {
return gender + " " + super.toString();
}
...getters, setters...
}
PartyRepository.java:
public interface PartyRepository extends JpaRepository<Party,Long> {
}
If I POST, it saves to the db correctly:
POST /parties {"#class":"com.example.Individual", "name":"Neil", "gender":"MALE"}
But returns a 400 error:
{"cause":null,"message":"Cannot create self link for class com.example.Individual! No persistent entity found!"}
It looks like it's an Individual after retrieving from repository:
System.out.println(partyRepository.findOne(1L));
//output is MALE 1 Neil
Looks like Jackson can figure out that it's an individual:
System.out.println( new ObjectMapper().writeValueAsString( partyRepository.findOne(1L) ) );
//output is {"#class":"com.example.Individual", "id":1, "name":"Neil", "gender":"MALE"}
Why can SDR not figure it out?
How to fix? Preferably with XML config.
Versions:
SDR 2.2.0.RELEASE
SD JPA 1.7.0.RELEASE
Hibernate 4.3.6.Final
SDR repositories expect a non-abstract entity, in your case it would be Individual. You can google or search here for explaination on why SDR expects a non-abstract entity.
I tried your code myself and SDR won't even work for POST and I see below error message.
{
"cause": {
"cause": null,
"message": "Can not construct instance of com.spring.data.rest.test.Party, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information\n at [Source: org.apache.catalina.connector.CoyoteInputStream#30217e25; line: 1, column: 1]"
},
"message": "Could not read JSON: Can not construct instance of com.spring.data.rest.test.Party, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information\n at [Source: org.apache.catalina.connector.CoyoteInputStream#30217e25; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.spring.data.rest.test.Party, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information\n at [Source: org.apache.catalina.connector.CoyoteInputStream#30217e25; line: 1, column: 1]"
}
I suggest you change respository from PartyRepository to IndividualRepository
public interface IndividualRepository extends JpaRepository<Individual,Long> {
}
You are seeing that error since SDR could not find a repository refering Individual while constructing links. Simply adding Individual respository and not exporting it would solve your problem.
#RepositoryRestResource(exported = false)
public interface IndividualRepository extends JpaRepository<Individual,Long> {
}
Here is the situation:
ICategorized is used by ICategoryService to manage categories.
public interface ICategorized
{
ICategory Category { get; set; }
}
Then some class implements ICategorized.
public class Cart : ICategorized
{
...
ICategory Category {
get {
return _categoryService.GetItemCategory(...)
}
set {
_categoryService.SetCategoryForItem(...);
};
}
...
}
So, what is the best solution to set _categoryService implementation?
Through constructor or property injection?
Using of constructor can lead to very complex constructor like
public class Cart : ICategorized. ITagged, ISecured, IMediaSupport {
public Cart(ICategoryService cs, ITagService ts, ISecurityService ss, IMediaService ms) {...}
...
}
I doubt this is a good design. Any ideas?
What would be your suggestion?
I can give to CartService responsibility of ICategoryService but in that case I can't use Lazy loading. Something like
public class CartService : ICartService {
public CartService(ICategoryService cs) {...}
...
}
I favor constructor injection over property injection, mainly because it makes explicit what services the component requires. If you use an IoC-container you wouldn't have to worry about the constructor getting too complicated since you never have to use the constructor, only the container does.
Property injection could be used for non-required services.
By the way; generally speaking, very long constructor signatures probably means that your class does not adhere to the single responsibility principle and perhaps it should be refactored into two or more separate classes.