I am using the MapStruct library for mapping objects, and I need to translate from an entity to a dto, but there is a problem, for example, that the entity stores animal types as a list of objects, and the dto stores an array with the ids of these types. how can this be done correctly? I did it, but only manually using loops. MapStruct is something new for me, so a lot of things are not clear, I hope for your help, thanks!
I have this class:
#FieldDefaults(level = AccessLevel.PRIVATE)
#Table(name = "animal")
public class Animal {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#NotEmpty
#MinCollectionSize
#ElementOfCollectionNotNull
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "animal_types",
joinColumns = {#JoinColumn(name = "animal_id",referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "type_id",referencedColumnName = "id")})
List<AnimalType> animalTypes;
#NotNull
#Min(1)
Float weight;
#NotNull
#Min(1)
Float length;
#NotNull
#Min(1)
Float height;
#NotNull
#NotGender
AnimalGender gender;
AnimalLifeStatus lifeStatus;
TimestampWithTimeZoneJdbcType chippingDateTime;
#NotNull
#ManyToOne
#JoinColumn(name = "chipper_id")
Account chipperId;
#NotNull
#ManyToOne
#JoinColumn(name = "chipping_location_id")
Location chippingLocationId;
#OneToMany(mappedBy = "animal")
List<AnimalLocation> visitedLocations;
TimestampWithTimeZoneJdbcType deathDateTime;
}
And i need this Entity mapping to this DTO:
#FieldDefaults(level = AccessLevel.PRIVATE)
public class AnimalResponse {
Long id;
Long[] animalTypes;
Float weight;
Float length;
Float height;
AnimalGender gender;
AnimalLifeStatus lifeStatus;
TimestampWithTimeZoneJdbcType chippingDateTime;
Integer chippedId;
Long chippingLocationId;
Long[] visitedLocations;
TimestampWithTimeZoneJdbcType deathDateTime;
}
This is my Mapper Interfaces:
public interface BaseMapper<ENTITY, DTO> {
DTO toDto(ENTITY entity);
ENTITY toEntity(DTO dto);
List<DTO> toDtoList(List<ENTITY> entityList);
List<ENTITY> toEntityList(List<DTO> dtoList);
}
#Mapper
public interface AnimalMapper extends BaseMapper<Animal, AnimalResponse> {
AnimalMapper INSTANCE = Mappers.getMapper(AnimalMapper.class);
}
#Mapper
public interface AnimalMapper extends BaseMapper<Animal, AnimalResponse> {
AnimalMapper INSTANCE = Mappers.getMapper(AnimalMapper.class);
#Override
#Named("toDto")
#Mapping(source = "animalTypes", target = "animalTypes", qualifiedByName = "toArray")
AnimalResponse toDto(Animal entity);
#Override
#IterableMapping(qualifiedByName = "toDto")
List<AnimalResponse> toDtoList(List<Animal> entityList);
#Override
#Named("toEntity")
#Mapping(source = "animalTypes", target = "animalTypes", qualifiedByName = "toArrayList")
Animal toEntity(AnimalResponse dto);
#IterableMapping(qualifiedByName = "toEntity")
List<Animal> toEntityList(List<AnimalResponse> dtoList);
#Named("toArray")
default Long[] toArray(List<AnimalType> animalTypes) {
return animalTypes.stream()
.map(AnimalType::getId)
.toArray(size -> new Long[size]);
}
#Named("toArrayList")
default List<AnimalType> toArrayList(Long[] animalTypes) {
return Arrays.stream(animalTypes)
.map(AnimalType::new) // new object just for example purposes, you can instead call the repository findById
.collect(Collectors.toList());
}
}
With the following animal:
Animal(id=1, animalTypes=[AnimalType(id=1), AnimalType(id=2), AnimalType(id=3)])
the generated response by calling toDto
AnimalResponse response = AnimalMapper.INSTANCE.toDto(animal);
will be:
AnimalResponse(id=1, animalTypes=[1, 2, 3])
Related
//Entities and DTOs
public class JourneyType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "journey_type_id")
private Long journeyTypeId;
private String type;
// bi-directional many-to-one association to JourneyRent
#OneToMany(mappedBy = "journeyType")
private Set<JourneyRent> journeyRents;
}
public class JourneyTypeTO implements Serializable {
private Long journeyTypeId;
private String type;
private Set<JourneyRentTO> journeyRents;
}
public class JourneyRent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "journey_rent_id")
private Long journeyRentId;
#Column(name = "min_max_travel_km")
private Double minMaxTravelKm;
#Column(name = "rent_charges")
private BigDecimal rentCharges;
// bi-directional many-to-one association to JourneyType
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "journey_type_id")
private JourneyType journeyType;
// bi-directional many-to-one association to VehicleType
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "vehicle_type_id")
private VehicleType vehicleType;
}
public class JourneyRentTO implements Serializable {
private Long journeyRentId;
private Double minMaxTravelKm;
private BigDecimal rentCharges;
private JourneyTypeTO journeyType;
private VehicleTypeTO vehicleType;
}
public class VehicleType implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="vehicle_type_id")
private Long vehicleTypeId;
private String type;
//bi-directional many-to-one association to JourneyRent
#OneToMany(mappedBy="vehicleType")
private Set<JourneyRent> journeyRents;
}
public class VehicleTypeTO implements Serializable {
private Long vehicleTypeId;
private String type;
private Set<JourneyRentTO> journeyRents;
}
// Mapper interfaces
#Mapper(uses = {JourneyRentMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface JourneyTypeMapper {
JourneyTypeMapper INSTANCE = Mappers.getMapper(JourneyTypeMapper.class);
#Mapping(target = "journeyBookings", ignore = true)
JourneyTypeTO toDTO(JourneyType journeyType, #Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
#Mapping(target = "journeyBookings", ignore = true)
JourneyType toEntity(JourneyTypeTO journeyType);
}
#Mapper(uses = {JourneyTypeMapper.class, VehicleTypeMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface JourneyRentMapper {
JourneyRentMapper INSTANCE = Mappers.getMapper(JourneyRentMapper.class);
#Mapping(target = "journeyType", ignore = true)
JourneyRentTO toDTO(JourneyRent journeyRent, #Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
#Mapping(target = "journeyBookingVehicles", ignore = true)
JourneyRent toEntity(JourneyRentTO journeyRentTo);
}
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface VehicleTypeMapper {
VehicleTypeMapper INSTANCE = Mappers.getMapper(VehicleTypeMapper.class);
VehicleTypeTO toDTO(VehicleType vehiType);
VehicleType toEntity(VehicleTypeTO vehiTypeTo);
}
public class CycleAvoidingMappingContext {
private final Map<Object, Object> knownInstances = new IdentityHashMap<>();
#BeforeMapping
public <T> T getMappedInstance(Object source, #TargetType Class<T> targetType) {
return targetType.cast(knownInstances.get(source));
}
#BeforeMapping
public void storeMappedInstance(Object source, #MappingTarget Object target) {
knownInstances.put(source, target);
}
}
There is a bi-directional relationship between JourneyType and JourneyRent. Now JPQL query returns JourneyType Object which has a reference of
JourneyRent object and in turn JourneyRent object has a reference of JourneyType Object.
Usage :
List<JourneyTypeTO> journeyTypeTos = new ArrayList<>();
for (JourneyType journeyType : journeyTypes) {
journeyTypeTos.add(JourneyTypeMapper.INSTANCE.toDTO(journeyType, new CycleAvoidingMappingContext()));
}
When I try to map Entity to TO, I'm getting out of memory error. How can I map the objects for this scenario ?
I see that you are using the example of the CycleAvoidingMappingContext. The reason why it isn't working to you is because you are not passing it in your VehicleMapper. MapStruct will use the VehicleMapper to map your vehicles and there is no context that would store the cycles.
Father.java
#Entity
#Table(name = ClinicManagementVariableUtils.TABLE_NAME_FOR_FATHER)
#JsonInclude(Include.NON_EMPTY)
#NamedQueries({
#NamedQuery(name = "father.findAll", query = "SELECT f FROM Father f")
})
#NamedEntityGraphs({
#NamedEntityGraph(
name = "graph.father.setOfChildrens",
attributeNodes = #NamedAttributeNode(value = "setOfChildrens")),
})
})
public class Father {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "fatherId", nullable = false, insertable = false, updatable = false)
private String id;
#Column(name = "name", columnDefinition = "varchar(50)")
private String name;
#Column(name = "firstName", columnDefinition = "varchar(50)")
private String firstName;
#Column(name = "lastName", columnDefinition = "varchar(50)")
private String lastName;
#Column(name = "degree", columnDefinition = "varchar(50)")
private String degree;
#OneToOne(mappedBy = "father")
#JsonIgnore
private Mother mother;
#OneToMany(mappedBy = "father") // children
private Set<Children> setOfChildrens;
getter()
setter()
}
Children.java
#Entity
#Table(name = ClinicManagementVariableUtils.TABLE_NAME_FOR_CHILDREN)
#JsonInclude(Include.NON_EMPTY)
public class Children {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "childrenId", nullable = false, insertable = false, updatable = false)
private String id;
#Column(name = "name", columnDefinition = "varchar(50)", nullable = false)
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JsonIgnore
private Father father;
getter()
setter()
}
Mother.java
#Entity
#Table(name = ClinicManagementVariableUtils.TABLE_NAME_FOR_MOTHER)
#JsonInclude(Include.NON_EMPTY)
public class Mother {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "motherId", nullable = false, insertable = false, updatable = false)
private String id;
#Column(name = "name", columnDefinition = "varchar(50)", nullable = false)
private String name;
#OneToOne
#JoinColumn(name = "fatherId")
private Father father;
getter()
setter()
}
FatherDao.java
public interface FatherDao extends GenericModelDao<Father> {
// Note : Return type is Mother instead of Father
public List<Mother> getFathersUsingNativeQueryAndEntityGraph();
}
FatherDaoImpl.java
#Named
public class FatherDaoImpl extends GenericModelDaoImpl<Father> implements FatherDao {
#PersistenceContext
EntityManager entityManager;
#Override
public List<Mother> getFathersUsingNativeQueryAndEntityGraph() {
EntityGraph graph = entityManager.getEntityGraph("graph.father.setOfChilrensAndAddresses");
List<Mother> list = entityManager.createNamedQuery("father.findAll").setHint("javax.persistence.fetchgraph", graph)
.getResultList();
return list;
}
}
FatherService.java
public interface FatherService {
// Note : Return type is Mother instead of Father
public List<Mother> getFathersUsingNativeQueryAndEntityGraph();
}
FatherServiceImpl.java
#Named
public class FatherServiceImpl implements FatherService {
#Inject
private FatherDao fatherDao;
#Override
public List<Mother> getFathersUsingNativeQueryAndEntityGraph() {
return fatherDao.getFathersUsingNativeQueryAndEntityGraph();
}
}
FatherController.java
#Controller
public class FatherController {
private static final Logger LOGGER = LoggerFactory.getLogger(FatherController.class);
#CrossOrigin
#RequestMapping(value = "/getFathersUsingNativeQueryAndEntityGraph", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Mother> getFathersUsingNativeQueryAndEntityGraph() {
List<Mother> list = new ArrayList<>();
try {
// Note : now list of father object is return as list of mother but it working..
list = fatherService.getFathersUsingNativeQueryAndEntityGraph();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
In Father.java, there are 2 type of annotations:
Native Query
Entity Graph
Now in DAO layer, I execute native query with graph query in getFathersUsingNativeQueryAndEntityGraph() method which will return list of fathers.
But when I fetch it in List<Mother> then also it returns all fields from Father.java even if those fields not exists in Mother.java.
Doubt:
If it returns all Fields of Father.java then how it is possible that it returns response in Mother.java?
1. Since you have no type checks anywhere this code will always compile.
2. As long as you never access an object in that list specifically as a Mother object, your code will run.
When you use createNamedQuery without a class parameter, you create an untyped Query object, which returns a raw list with whatever objects JPA returns.
In your code this list will contain Father objects, because that is what you asked for.
Writing List<Mother> list = ... is wrong, but your compiler doesn't know this. Since the type parameter is only checked at compile time, this code will run and during runtime list will be a generic list holding Father objects.
You should get a compiler warning here about turning that generic list into a List<Mother>, because the compiler cannot guarantee that this is correct. Listen to such warnings, they are there for a reason.
Only when you do this:
Mother mother = list.get(0);
will you get a runtime error, specifically a ClassCastException. However, your compiler will not complain about this, because it believes your list will contain Mother objects, because you lied to it above.
Fix your code
You should use a TypedQuery instead, by passing the class you expect to createNamedQuery:
entityManager.createNamedQuery("father.findAll", Father.class)
This will enforce the correct type during compile time, so doing List<Mother> list = ... will not compile anymore.
I have an enterprise project configured by spring mvc4 + hibernate5 that all of its relation are eager and its performance is very bad...So I am transforming all eager relations to lazy step by step...But I see many errors in each step...and it works sometimes properly and sometimes not....
in this example HeaderFromStore is an instnace of RequestHeaders and a child of RequestLine. DeliveryPoint is child of requestHeader and I don't want to fetch deliveryPoint of requestHeader...But if don't use it in select query it couldn't fetch HeaderFromStore !!
I used this query and I get error!
select m from MAMRequestLines m join fetch m.mamRequestHeaders r
left join fetch m.requestHeaderFromStore rr where m.id =:id
If I use this query I don't get error
select m from MAMRequestLines m join fetch m.mamRequestHeaders r
left join fetch m.requestHeaderFromStore rr
join fetch rr.mamDeliveryPoints
left join fetch r.mamDeliveryPoints
join fetch where m.id =:id
RequestLine.java
#Entity(name = "RequestLines")
#Table(name = "_REQUEST_LINES")
//#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property = "#id")
public class RequestLines extends Entity implements Serializable {
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private RequestHeaders requestHeaders;
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private RequestHeaders requestHeaderFromStore;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "_REQUEST_Line_SEQ")
#SequenceGenerator(name = "_REQUEST_Line_SEQ", sequenceName = "_REQUEST_Line_SEQ")
#Column(name = "REQUEST_LINE_ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "REQUEST_HEADER_ID", nullable = false)
public RequestHeaders getRequestHeaders() {
return RequestHeaders;
}
public void setRequestHeaders(RequestHeaders requestHeaders) {
this.RequestHeaders = requestHeaders;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "REQUEST_HEADER_FROM_STORE")
public RequestHeaders getRequestHeaderFromStore() {
return requestHeaderFromStore;
}
public void setRequestHeaderFromStore(RequestHeaders requestHeaderFromStore) {
this.requestHeaderFromStore = requestHeaderFromStore;
}
}
RequestHeader.java
#Entity(name = "RequestHeaders")
#Table(name = "REQUEST_HEADERS")
//#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class RequestHeaders extends Entity implements Serializable {
private long id;
// #JsonInclude(JsonInclude.Include.NON_EMPTY)
// #JsonIgnore
private DeliveryPoints DeliveryPoints;
#JsonIgnore
private Set<RequestLines> RequestLinesSet;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "_REQUEST_HEADERS_SEQ")
#SequenceGenerator(name = "_REQUEST_HEADERS_SEQ", sequenceName = "_REQUEST_HEADERS_SEQ")
#Column(name = "REQUEST_HEADER_ID")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "DELIVERY_POINT_ID", nullable = false)
public DeliveryPoints getDeliveryPoints() {
return DeliveryPoints;
}
public void setDeliveryPoints(DeliveryPoints DeliveryPoints) {
this.DeliveryPoints = DeliveryPoints;
}
#OneToMany(mappedBy = "RequestHeaders", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#OnDelete(action = OnDeleteAction.CASCADE)
public Set<RequestLines> getRequestLinesSet() {
return RequestLinesSet;
}
public void setRequestLinesSet(Set<RequestLines> RequestLinesSet) {
this.RequestLinesSet = RequestLinesSet;
}
}
exception:
No serializer found for class
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no
properties discovered to create BeanSerializer (to avoid exception,
disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference
chain:
domain.RequestLine["HeaderFromStore"]->.domain.RequestHeaders["DeliveryPoint"]->domain.DeliveryPoint_$$_jvst393_f["handler"])
notice that I used JsonIgnore and JsonInclude(on fields and class) but none of them doesn't work...
Edit:
I finally found this solution to avoid exception and ignoring unwanted properties.
I added this part of code to WebMvcConfig extends WebMvcConfigurerAdapter class:
{
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
//Registering Hibernate4Module to support lazy objects
mapper.registerModule(new Hibernate4Module());
messageConverter.setObjectMapper(mapper);
return messageConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//Here we add our custom-configured HttpMessageConverter
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
But I have another problem now...all post requests will receive with null properties in request body ....for example in this code all properties of "requestHeaders" in input is null or empty!
#RequestMapping(value = "/requestHeader/", method = RequestMethod.POST, consumes = {"application/json"})
public ResponseEntity<Void> createRequestHeaders(#RequestBody RequestHeaders requestHeaders, UriComponentsBuilder ucBuilder) {
requestHeaders.setDeliveryPoints(deliveryPointsService.find(requestHeaders.getDeliveryPointsId()));
requestHeadersService.add(requestHeaders);
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
you should add each entities which have relation this annotation at the top of class definition.
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
details are explained here and here
I hope these solves your problem.
I have 2 collections of different sub-entities in owning entity:
#Entity
#Table(name = "car")
public class Car {
#Id
private Long id;
#OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true, targetEntity = Headlight.class)
private Collection<Headlight> headlights = new ArrayList<Headlight>();
#OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true, targetEntity = Wheel.class)
private Collection<Wheel> wheels = new ArrayList<Wheel>();
}
#Entity
#Table(name = "part")
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING, length = 16)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Part {
#Id
private Long id;
private String manufacturer;
}
#Entity
#DiscriminatorValue("HEADLIGHT")
public class Headlight extends Part {
private Integer power;
#ManyToOne
#JoinColumn(name = "owner_id", referencedColumnName = "id")
private Car owner;
}
#Entity
#DiscriminatorValue("WHEEL")
public class Wheel extends Part {
private Integer size;
#ManyToOne
#JoinColumn(name = "owner_id", referencedColumnName = "id")
private Car owner;
}
I expect these two collections to be filled by instances of corresponding subclasses (2 headlights and 4 wheels):
#Test
public void testCar() {
Car car = new Car();
car.setId(1l);
Headlight light1 = new Headlight();
light1.setId(1l);
light1.setManufacturer("Osram");
light1.setPower(12);
light1.setOwner(car);
Headlight light2 = new Headlight();
light2.setId(2l);
light2.setManufacturer("Osram");
light2.setPower(12);
light2.setOwner(car);
car.getHeadlights().add(light1);
car.getHeadlights().add(light2);
Wheel wheel1 = new Wheel();
wheel1.setId(3l);
wheel1.setManufacturer("Bridgestone");
wheel1.setSize(16);
wheel1.setOwner(car);
Wheel wheel2 = new Wheel();
wheel2.setId(4l);
wheel2.setManufacturer("Bridgestone");
wheel2.setSize(16);
wheel2.setOwner(car);
Wheel wheel3 = new Wheel();
wheel3.setId(5l);
wheel3.setManufacturer("Bridgestone");
wheel3.setSize(16);
wheel3.setOwner(car);
Wheel wheel4 = new Wheel();
wheel4.setId(6l);
wheel4.setManufacturer("Bridgestone");
wheel4.setSize(16);
wheel4.setOwner(car);
car.getWheels().add(wheel1);
car.getWheels().add(wheel2);
car.getWheels().add(wheel3);
car.getWheels().add(wheel4);
entityManager.persist(car);
entityManager.flush();
entityManager.clear();
Car restoredCar = entityManager.find(Car.class, 1l);
Assert.assertEquals(2, restoredCar.getHeadlights().size());
Assert.assertEquals(4, restoredCar.getWheels().size());
}
Instead, first collection contains 6 headlights (partially filled) and second collection contains incorrect data:
org.hibernate.WrongClassException: Object with id: 1 was not of the specified subclass: com.commerzbank.tr.nonotc.repository.Wheel (loaded object was of wrong class class com.commerzbank.tr.nonotc.repository.Headlight)
Output SQL:
SELECT headlights0_.owner_id AS owner6_3_1_,
headlights0_.id AS id2_7_1_,
headlights0_.id AS id2_7_0_,
headlights0_.manufacturer AS manufact3_7_0_,
headlights0_.owner_id AS owner6_7_0_,
headlights0_.power AS power4_7_0_
FROM part headlights0_
WHERE headlights0_.owner_id=?
I expected discriminator column to be included into WHERE clause as well:
AND headlights0_.type = 'HEADLIGHT'
,but it is not there.
I could only fix this issue using Hibernate #Where(clause = "type = 'WHEEL') annotation.
Why isn't Hibernate work correctly in this case? I expect it has all necessary metadata information to be able to issue correct SQL.
You might want to try
#DiscriminatorOptions(force = true) on your abstract class "Part".
See also: About the use of #ForceDiscriminator/#DiscriminatorOptions(force=true)
I'm using ResultTransformer to select only particular properties from entity, just i don't need all properties from entity.
But the problem i faced is when a property is "one-to-many".
Here is a simple example.
#Entity
#Table(name = "STUDENT")
public class Student
{
private long studentId;
private String studentName;
private List<Phone> studentPhoneNumbers = new ArrayList<Phone>();
#Id
#GeneratedValue
#Column(name = "STUDENT_ID")
public long getStudentId()
{
return this.studentId;
}
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "STUDENT_PHONE", joinColumns = {#JoinColumn(name = "STUDENT_ID")}, inverseJoinColumns = {#JoinColumn(name = "PHONE_ID")})
public List<Phone> getStudentPhoneNumbers()
{
return this.studentPhoneNumbers;
}
#Column(name = "STUDENT_NAME", nullable = false, length = 100)
public String getStudentName()
{
return this.studentName;
}
Here is the class used by ResultTransformer for storing the selected properties.
public class StudentDTO
{
private long m_studentId;
private List<Phone> m_studentPhoneNumbers = new ArrayList<Phone>();
..
constructors and getters and setters..
And finally the Criteria code
Criteria criteria = session.createCriteria(Student.class)
.setProjection(Projections.projectionList()
.add(Projections.property("studentId"), "m_studentId")
.add(Projections.property("studentPhoneNumbers"), "m_studentPhoneNumbers"))
.setResultTransformer(Transformers.aliasToBean(StudentDTO.class));
List list = criteria.list();
StudentDTO p = (StudentDTO) list.get(0);
So, after i get StudentDTO object , only studenId is available, studentPhoneNumber is null ..
Does it mean ResultTransformer does not work with any relationships ? or my way is wrong
Any suggestions?
Thanks
I have run into this problem before. The transformers in hibernate generally lead to frustration. You can do a manual assignment as is suggested, or you can map the DTO class to the same table using hibernate. Just add the same annotation to your StudentDTO class as the Student class and then load it like normal. So student DTO would be:
#Entity
#Table(name = "STUDENT")
public class StudentDTO
{
private long studentId;
private String studentName;
private List<Phone> studentPhoneNumbers = new ArrayList<Phone>();
#Id
#GeneratedValue
#Column(name = "STUDENT_ID")
public long getStudentId()
{
return this.studentId;
}
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "STUDENT_PHONE", joinColumns = {#JoinColumn(name = "STUDENT_ID")}, inverseJoinColumns = {#JoinColumn(name = "PHONE_ID")})
public List<Phone> getStudentPhoneNumbers()
{
return this.studentPhoneNumbers;
}
Just leave out what you do not want to load.