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
Related
I have the following Pojo:
#Entity
#Table(name = "USER")
class User {
#Id
private long id;
private String name;
private int age;
private long lastVisited;
private long lastPlayed;
private long lastPayed;
...
}
I would like somehow if possible to map the Pojo like this:
#Entity
#Table(name = "USER")
class User {
#Id
private long id;
private String name;
private int age;
#Embedded
private UserStatistics statistics;
...
}
#Embeddable
class UserStatistics {
private long lastVisited;
private long lastPlayed;
private long lastPayed;
}
BUT, I DON'T want to move the statistics columns into a new
USER_STATISTICS table and do #OneToOne mapping.
Is there a Hibernate trick I can use here?
Thanks!
What you did is already enough, Hibernate does not require you to define fields for all columns in your table. It's rather the other way around - all non-transient fields should be reflected as columns in the corresponding table either using name defined in #Column annotation or generated using a naming convention used in hibernate configuration.
The example you presented is sufficient and will work, but I wouldn't recommend it as you can have two entities mapping single row at the same time.
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 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.
I am working on JPA project and I need your help.
I have two classes, “Person” and “Leader” which inherits from Person.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
}
And
#Entity
public class Leader extends Person implements Serializable {
private List < Person > listTeam;
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
My question Is, do I need to have JPA annotations #OneToMany or something else before private List listTeam in class Leader?
Thank you very much
You need to specify a mapping between the two classes because for Hibernate the association is not relevant here, you have to use annotations in both sides and I guess you will need a OneToMany mapping here :
Here's the mapping that you are seraching for:
In Person class:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
#ManyToOne
#JoinColumn(name="leader_id")
private Leader leader;
//getter and setter
}
In Leader class:
#Entity
public class Leader extends Person implements Serializable {
#OneToMany(mappedBy = "leader")
private List <Person> listTeam;
//getter and setter
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
For further information you can see these links:
Hibernate – One-to-Many example (Annotation).
Hibernate One To Many Annotation tutorial.
Note:
I don't see the use of the field personId in the Person class, there's no need to use two differents ids.
EDIT:
To answer your questions:
The #JoinColumn(name="leader_id") is not mandatory, but it's used to specify the foreign key name.
If the relation is ManyToMany the mappedBy property is used to specify the owner of the relationship, you can see this answer for more details.
I've 3 tables:
- lots: has column gen_id
- onhand_quantities: has column gen_id,sub_inventory_code, quantity
- subinventory: has column sub_inventory_code
I map to 2 objects: Lots, SubInventory and I would like to mapping in object Lots with property is: Map<SubInventory, Integer> getOnhandQuantity()
Integer type in this Map is value of quantity column.
Please help me.
Just a brief search in Hibernate document give me information on using an associated entity as key of a Map (aka Ternary Association)
Quoted from the manual:
There are three possible approaches to mapping a ternary association.
One approach is to use a Map with an association as its index:
Example 7.31. Ternary association mapping
#Entity
public class Company {
#Id
int id;
...
#OneToMany // unidirectional
#MapKeyJoinColumn(name="employee_id")
Map<Employee, Contract> contracts;
}
// or
<map name="contracts">
<key column="employer_id" not-null="true"/>
<map-key-many-to-many column="employee_id" class="Employee"/>
<one-to-many class="Contract"/>
</map>
A second approach is to remodel the association as an entity class.
This is the most common approach. A final alternative is to use
composite elements, which will be discussed later.
To map it back to your original example: Company -> Lots, Employee -> SubInventory, Contract -> OnhandQuantity
Personally I would rather make them as a simple relationship, and construct Map etc on the fly whenever it is needed.
Haven't tried, but a further look get me to an example in #MapKeyColumn, which in combination with the above sample, should give something reasonable like:
class Lot {
#ElementCollection
#MapKeyJoinColumn(name="sub_inventory_id")
#CollectionTable(name="onhand_quantities")
#Column(name="quantity")
private Map<SubInventory, Integer> onhandQuantities;
}
The below code in the Lots entity should do.
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "ONHAND_QUANTITIES", joinColumns= #JoinColumn(name="GEN_ID"))
#MapKeyJoinColumn(name="SUBINVENTORY_CODE")
#Column(name="QUANTITY")
private Map<SubInventory, Integer> onHandQuantity;
I am trying to test this.
I got this solution from the below link.
Reference key issue while doing many to many relationship using #ElementCollection, #MapKeyJoinColumn
#Entity
#Table(name = "LOTS")
public class Lots implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "GEN_ID")
private Long id;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "ONHAND_QUANTITIES", joinColumns= #JoinColumn(name="GEN_ID"))
#MapKeyJoinColumn(name="SUBINVENTORY_CODE")
#Column(name="QUANTITY")
private Map<SubInventory, Integer> onHandQuantity;
......// getters and setters
}
#Entity
#Table(name = "SUBINVENTORY")
public class SubInventory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="SUBINVENTORY_CODE")
private Long id;
......// getters and setters
}