I have a domain object called User:
public class User {
private long id;
private String username;
private String email;
private List<Profile> profiles;
// getters & setters
}
And I have the related DTO (UserDTO) which is
public class UserDTO {
private long id;
private String username;
private String email;
private List<Long> profilesId;
// getters & setters
}
I'd like to use Dozer to convert from domain object to DTO. The Profile class has a property
Long id;
What I want is that Dozer takes the profile's id for each profile in the list and save it in the DTO's list. Can I do something like that? Do I have to use custom converters?
Here's my actual mapping file
<mapping>
<class-a>common.model.User</class-a>
<class-b>common.model.dto.UserDTO</class-b>
<field>
<a>legalEntity.id</a>
<b>legalEntityId</b>
</field>
<field type="one-way">
<a>profiles.id</a>
<b>profilesId</b>
</field>
</mapping>
Solved
just add to the source class this method
public List<Long> getProfilesId() {
List<Long> profilesId = new ArrayList<Long>();
for(Profile p : this.profiles) {
profilesId.add(p.getId());
}
return profilesId;
}
and to the mapping file
<field type="one-way">
<a get-method="getProfilesId">profiles</a>
<b>profilesId</b>
</field>
which says Dozer which method use to make the conversion.
Related
consider an example, I have two entity as below
Attribute.java
#Entity
#Table(name = "attribute")
public class Attribute {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Getter private Long id;
#Column(name="name")
#Getter private String name;
#Column(name = "source")
#Getter private String source;
protected Attribute(){}
public Attribute(final String name, final String source) {
this.name = name;
this.source = source;
}
}
AttributeGroup.java
#Entity
#Table(name = "attribute_group")
public class AttributeGroup {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Getter private Long id;
#Column(name="name")
#Getter private String name;
#Column(name = "value")
#Getter private String value;
#Getter
#Column(name="attribute_id", nullable=false)
protected Long attributeId;
#Getter
#OneToOne(optional=true, fetch = FetchType.EAGER)
#JoinColumn(name="attribute_id", updatable=false, insertable=false, referencedColumnName="id")
private Attribute attribute;
protected AttributeGroup(){}
}
Two repository
AttributeRepository.java
public interface AttributeRepository{}
AttributeGroupRepository.java
public interface AttributeGroupRepository {
/**
* find list of Groups by attribute name
* #param attribute
* #return
*/
List<AttributeGroup> findByAttribute(Attribute attribute);
}
AttributeGroupRepositoryTest.java
<pre><code>#RunWith(SpringRunner.class)
#DataJpaTest
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class})
#DatabaseSetup(AttributeGroupRepositoryTest.DATASET)
#DatabaseTearDown(type = DatabaseOperation.CLEAN_INSERT, value = { AttributeGroupRepositoryTest.DATASET })
public class AttributeGroupRepositoryTest {
protected static final String DATASET = "classpath:/attribute-group-test-data.xml";
#Autowired
private AttributeGroupRepository groupRepository;
#Test
public void findByAttributes(){
Attribute attribute=new Attribute("Data","abc");
List<AttributeGroup> groups = groupRepository.findByAttribute(attribute);
assertThat(groups.isEmpty(), Matchers.is(false));
assertThat(groups.size(), Matchers.equalTo(2));
assertThat(groups.stream().findFirst().get().getId(), Matchers.equalTo(5L));
assertThat(groups.stream().findFirst().get().getName(), Matchers.equalTo("GROUP1"));
assertThat(groups.stream().findFirst().get().getValue(), Matchers.equalTo("HW"));
assertThat(groups.stream().findFirst().get().getAttribute().getId(), Matchers.equalTo(3L));
assertThat(groups.stream().findFirst().get().getAttribute().getName(), Matchers.equalTo("Data"));
assertThat(groups.stream().findFirst().get().getAttribute().getSource(), Matchers.equalTo("abc"));
}
}
xml file
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<lima_attribute id="1" name="Issuer Ultimate Parent Name" source="VENTURE"/>
<lima_attribute id="2" name="Currency" source="abc"/>
<lima_attribute id="3" name="Data" source="abc"/>
<lima_attribute_group id="1" name="CurrencyGroup" value="AUS" attribute_id="2"/>
<lima_attribute_group id="2" name="CurrencyGroup" value="GBP" attribute_id="2"/>
<lima_attribute_group id="3" name="CurrencyGroup" value="BHD" attribute_id="2"/>
<lima_attribute_group id="4" name="CurrencyGroup" value="AFA" attribute_id="2"/>
<lima_attribute_group id="5" name="GROUP1" value="HW" attribute_id="3"/>
<lima_attribute_group id="6" name="GROUP1" value="VOL" attribute_id="3"/>
</dataset>
The above test case throws an exception
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.entity.attribute.Attribute; nested exception is java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.entity.attribute.Attribute
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:381)
Caused by: java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.entity.attribute.Attribute
As this test is dependent on Attribute repository and as Junit's are unit test cases and I don't want to use attribute repository.
Is any other way to mock other repository data?
I found the way without using other repository how we can execute the test case.
replacing below line:
List<AttributeGroup> groups = groupRepository.findByAttribute(attribute);
with
List<AttributeGroup> groups = groupRepository.findByAttributeNameAndAttributeSource("Data","abc");
you are searching a object which is not persisted in the repository,please find the attribute from the AttributeRepository first,and then search the returned attribute in the attribute group repository!
Attribute attribute=attributeRepository.findByNameAndSource("Data",Source);
if(attribute!=null){
List<AttributeGroup> groups = groupRepository.findByAttribute(attribute);
}
I'm using Spring with hibernate.
The object I'd like to fetch is of class A, which has attribute - a set of object of class B, like
public class A {
private Integer aID;
private Set<B> bs;
private String fieldA1;
private String fieldA2;
// setters and getters
}
public class B {
private Integer bID;
private String fieldB1;
private String fieldB2;
// setters and getters
}
In the mapping file, within the class A mapping tag, I include,
<set name="bs" table="TABLE_B">
<key column="A_ID" />
<one-to-many class="com.proj.test.B"/>
</set>
Now I want to fetch the A object with the bs inside filtered with criteria that depends on value of fieldB1 and fieldB2. (not to fetch all B objects)
Any suggestions / answers?
Try out the following :
#Query(value = "Select a from A a where a.bs.fieldB1 YOUR_CONDITION")
List<A> findAWithFilteredB();
I have two hibernate entities:
#Entity
#Table(name = DBConstants.TABLE_PARTNER)
public class Partner extends XWeedEntity {
private static final long serialVersionUID = 5692151244956513381L;
#Id
#Column(name = DBConstants.PARTNER_COL_PARTNER_NUMBER)
private Integer partnerNumber;
#OneToMany(mappedBy = DBConstants.VISIT_PROP_VISITOR)
private List<Visit> visits;
// More fields and properties...
}
#Entity
#Table(name = DBConstants.TABLE_VISIT)
public class Visit extends XWeedEntity {
private static final long serialVersionUID = -8324746049334117579L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = DBConstants.VISIT_COL_ID)
private Integer id;
#ManyToOne
#JoinColumn(name = DBConstants.VISIT_COL_VISITOR, nullable = false)
private Partner visitor;
// More fields and properties...
}
And two DTO entities:
public class PartnerDto extends XWeedEntity {
private static final long serialVersionUID = 5692151244956513381L;
private Integer partnerNumber;
private List<VisitDto> visits;
// More fields and properties...
}
public class VisitDto extends XWeedEntity {
private static final long serialVersionUID = -8324746049334117579L;
private Integer id;
private PartnerDto visitor;
// More fields and properties...
}
And I've got the following dozer mappings:
<mapping map-id="partnerWithCollections" map-empty-string="false" map-null="false">
<class-a>org.xweed.model.app.domain.dbo.Partner</class-a>
<class-b>org.xweed.model.app.domain.dto.PartnerDto</class-b>
<field map-id="visitWithPartner">
<a>visits</a>
<b>visits</b>
</field>
</mapping>
<mapping map-id="partnerBasic" wildcard="false" map-empty-string="false" map-null="false">
<class-a>org.xweed.model.app.domain.dbo.Partner</class-a>
<class-b>org.xweed.model.app.domain.dto.PartnerDto</class-b>
<field>
<a>partnerNumber</a>
<b>partnerNumber</b>
</field>
</mapping>
<mapping map-id="visitWithPartner" map-empty-string="false" map-null="false">
<class-a>org.xweed.model.app.domain.dbo.Visit</class-a>
<class-b>org.xweed.model.app.domain.dto.VisitDto</class-b>
<field map-id="partnerBasic">
<a>visitor</a>
<b>visitor</b>
</field>
</mapping>
The problem is that when I call dozer using "partnerWithCollections" mapping, dozer is mapping all Visit objects from the Partner visits, but every single visit has it's visitor with it's visits collection and so on, when the visitor attribute of each visit should only contain the partnerNumber.
If I try to exclude visitor field from visit, then works, and each visitor's visit is null, but for some reason it is not working using map-id to use some concrete Partner mapping.
Any ideas?
Thanks in advance.
This happens because by default, dozer always maps properties with same name:
Partner.visits
PartnerDto.visits
You can disable this setting the <mapping> atribute "wildcard=false".
From de oficial documentation:
Does Dozer automatically map fields with matching property names?
Yes. All fields with matching property names are implicitly mapped. It would be atypical usage, but you could suppress this behavior by setting wilcard="false".
http://dozer.sourceforge.net/documentation/faq.html#auto-property-name
You also can do that to all mappings, using the global configuration:
<configuration>
<wildcard>false</wildcard>
</configuration>
http://dozer.sourceforge.net/documentation/globalConfiguration.html
I ve a confusion regarding how the dozer maps the source object to destination object. I have the following scenario:
Source Object:
public class Rule {
private String id;
private String name;
private String group;
private String content;
private RuleType ruleType;
private String drlContent;
private boolean enabled;
private Strategy strategy;
// getters and setters
}
Destination Object:
public class RuleActivity {
private String id;
private String name;
private Strategy strategy;
// getters and setters
}
XML Mapping:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<stop-on-errors>true</stop-on-errors>
<date-format>MM/dd/yyyy HH:mm</date-format>
</configuration>
<mapping wildcard="false">
<class-a>com.magick.models.shared.Rule</class-a>
<class-b>com.magick.models.shared.log.RuleActivity</class-b>
<field>
<a>id</a>
<b>ruleId</b>
</field>
<field>
<a>strategy.name</a>
<b>strategy.name</b>
</field>
<field>
<a>name</a>
<b>name</b>
</field>
</mapping>
Now How these would be mapped ? I mean , does the destination Object contains the Complete Strategy Object or only the strategy.name field of it.
First of all, by default dozer mappings are bi-directional. So,
mapping from class-a to class-b and vice-versa is permitted.
As you have done your mapping as follows:
<field>
<a>strategy.name</a>
<b>strategy.name</b>
</field>
If the source object is having a Strategy object which is not null and have all the relevant field's value. Then dozer will create a new Strategy object for destination as well and will only populate the name field of newly created Strategy object.
Further, dozer also works on retrospection so suppose the name and type of all fields of Strategy object in source and destination is same. Dozer will map or copy all the fields automatically. So you don't have to map each field individually. you just have to write as below.
<field>
<a>strategy</a>
<b>strategy</b>
</field>
But if your field names or type is not same, you need to define mapping for each field as you did for id field for Rule class and ruleId field for RuleActivity class.
<field>
<a>id</a>
<b>ruleId</b>
</field>
Hope this clarifies your doubts.
I have the following scenario :
Class A{
private List<Long> longList;
//getter and setter
}
Class B{
private List<C> listC;
//getter and setter
}
Class C{
private Long id;
//getter and setter
}
Now, I want to convert between longList and C. I found the following mapping :
<mapping>
<class-a>A</class-a>
<class-b>B</class-b>
<field>
<a>longList</a>
<b>listC</b>
<a-hint>java.lang.Long</a-hint>
<b-hint>C</b-hint>
</field>
</mapping>
I am not sure whether the above mapping is the proper solution or not.
Can I set up a mapping between long and C , such that long gets mapped to C.id ?
You could try mapping Long to C like so:
<mapping>
<class-a>A</class-a>
<class-b>B</class-b>
<field>
<a>longList</a>
<b>listC</b>
<field>
</mapping>
<mapping>
<class-a>java.lang.Long</class-a>
<class-b>C</class-b>
<field>
<a>this</a>
<b>id</b>
<field>
</mapping>