3

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

brakebg
  • 414
  • 3
  • 11
  • 24
  • It looks like you've got the property names correct etc. Have you examined the SQL being run to ensure that the phoneNumber field is being alised correctly? Also have you tried debugging into the AliasToBeanResultTransformer#transformTuple() method to ensure that there is a value to set on your DTO? – Alex Barnes Jun 21 '11 at 11:27
  • What's that? AliasToBeanResultTransformer#transformTuple(). I will google it and check if it could be useful to me. Thanks Alex. – brakebg Jun 21 '11 at 11:35
  • The AliasToBeanResultTransformer is what is returned by the aliasToBean method on Transformers. The transform tuple is what performs the transform of your domain class to your DTO. i.e. it finds suitable setters on your DTO and sets them from alised fields in the query object array! Which explains why it won't work for your mapped collection! It only has access to the result set for the domain entity being loaded not the result set which is used to populate the mapped collection. – Alex Barnes Jun 21 '11 at 11:41
  • Ok,I've added this line in my code : .setResultTransformer(new AliasToBeanResultTransformer(StudentDTO.class)); But should't have any exception for that property which is null in the result? – brakebg Jun 21 '11 at 11:50
  • You didn't need to make that change, because that's what it was doing anyway. I don't think that this will work because if you look at what the transformTuple method has access to it's an Object[] and a String[] containing the results of the query and their aliases. You don't have access to the collection of Phone instances and so they can't be set on your DTO. – Alex Barnes Jun 21 '11 at 12:01
  • This is weird because ResultTransformer actually do transforming the result from one entity to another enity/map/... depend on what you want. For example if i do a regular select with no ResultTransformer the result will contains Phone instances because it's mapped. So why it doesn't work with ResultTransformer. Do you know any fix for this? Thanks. – brakebg Jun 21 '11 at 12:05
  • I would suggest that you write a simple service which loads Student instances from the database without using a result transformer. This service can then create a populate instances of your DTO and assign the properties as you wanted. – Alex Barnes Jun 21 '11 at 12:47
  • Ok, but is there anything i can use for populating DTO or i should do it manual? Thanks – brakebg Jun 21 '11 at 13:02

1 Answers1

2

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.

OTrain
  • 86
  • 4