tags:

views:

832

answers:

2

I'm having trouble getting a hibernate select to return a correctly populated object graph, when the select contains joins across many collections.

Eg:

 String sql = "select distinct changeset " +
   "from Changeset changeset " +
   "join fetch changeset.changeEntries as changeEntry " +
   "join fetch changeEntry.repositoryEntity as repositoryEntity " +
   "join fetch repositoryEntity.repository as repository " +
   "where repository.connectionName = :connectionName";
  • A Changeset has many ChangeEntries
  • A ChangeEntry has one RepositoryEntity
  • A RepositoryEntity has one Repository

The above statement returns the correct data, but the object graph is not correctly populated -- ie., each Changeset contains every ChangeEntry, not just it's own children.

Here's the relevant snippets of those two classes:

public class Changeset {

    @NotNull
    @OneToMany(mappedBy="changeset", targetEntity=ChangeEntry.class, cascade={CascadeType.ALL }, fetch=FetchType.EAGER )
    private Set<IChangeEntry> changeEntries;

And...

public class ChangeEntry {

    @NotNull
    @ManyToOne(targetEntity=Changeset.class)
    @ForeignKey(name="FkChangeEntryChangeset")
    private IScmChangeset changeset;

    @NotNull
    @ManyToOne(targetEntity=RepositoryEntity.class, cascade=CascadeType.ALL,fetch=FetchType.EAGER)
    @ForeignKey(name="FkChangeEntryRepoEntity")
    private IRepositoryEntity repositoryEntity;

Any assistance is greatly appreciated

Regards

Marty

+1  A: 

Marty:

I had this same problem and I could not find a suitable solution. You can use simple result transformers to control the number of objects that are returned:

CriteriaSpecification.ROOT_ENTITY
CriteriaSpecification.DISTINCT_ROOT_ENTITY

But the objects would always contain the full results for their children. I did happen across an excellent article that uses a DTO pattern along with projections and the aliasToBean transformer.

Here it is: http://swik.net/Hibernate/Hibernate+GroupBlog/Hibernate+3.2:+Transformers+for+HQL+and+SQL/cmxs

This has worked well for me. The biggest problem is that you have to create these flattened out DTO objects. For me its not that big of an issue because my specialized DTO objects are used in search results and are very reusable, plus I won't be needing very many of them.

I know that this does not directly address your issue, if you do find a way to accomplish your goal please update the post as I would love to see the solution.

Ryan Cook
A: 

Does loading one entity by its ID work correctly? If not - read on. :-)

I'm not sure if using @ForeignKey is enough. Try adding a @JoinColumn to the many-side of your relation. E.g.

@NotNull
@ManyToOne(targetEntity=RepositoryEntity.class, cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@ForeignKey(name="FkChangeSet")
@JoinColumn(name = "CHANGESET_ID", referencedColumnName = "ID")
private Changeset changeset;

Note: I just saw, that you're showing the mapping info for Repository, but I assume the one for Changeset looks similar.

rudolfson
Thanks for the response ... yes, loading one entity by it's ID does work. The @JoinColumn shouldn't be neccessary, as Hibernate can work that out from the targetEntity of the @ManyToOne. The @ForeignKey annotation exists only to give the foreign key a name, so I can get meaningful errors when things go awry (as they frequently do! :)
Marty Pitt
Concerning @ForeignKey - that's what I guessed.Concering @JoinColumn - Don't you have to tell the foreign key column name. The referenced column can be resolved from the target entity, that's right. But the foreign key column?
rudolfson