views:

18

answers:

1

I have an ItemEntity class, which has a collection of ImageEntity's. I want to be able to fetch a specific ImageEntity, given its index in the collection. For example, for an ItemEntity with 10 ImageEntity's, I want the 4th ImageEntity in as few database hits as possible.

I've read Hibernate In Action and googled, but every piece of documentation I can find is very terse in describing @IndexColumn, with no detailed examples of how to use it.

I can use item.getImages() to fetch a List<ImageEntity>, and then call .get(4) on that.. but that involves loading the whole collection into memory to be able to call get() on it.

What I'd like to do is something like:

int itemId = 111; // id of the item I want to fetch the image for
int wantImageNum = 4; // index of the image I want, in the item's list
imageId = .. somehow get the id of the 4th ImageEntity ..;
ImageEntity img = session.load(ImageEntity.class, imageId);

I have used @IndexColumn to let Hibernate manage the ordering of ImageEntity's in the collection, i.e.:

public class ItemEntity {
...
 @ManyToMany(cascade={CascadeType.MERGE,
    CascadeType.PERSIST,
    CascadeType.REFRESH}, fetch=FetchType.LAZY)
 @JoinTable(name = "ItemImages",
   joinColumns = {
    @JoinColumn(name="item_id", referencedColumnName="id") // id in this class           
   },
   inverseJoinColumns = {
    @JoinColumn(name="image_id") // id in ImageEntity
   }
 )
 @org.hibernate.annotations.IndexColumn(name="idx", base=1)
 private List images = new ArrayList();

Thus, there is a 'join table' that looks like this:

table ItemImages (
    item_id
    image_id
    idx
)

I could do something very dirty with plain SQL, i.e.

select image_id from ItemImages where item_id = :itemId and idx = :wantImageNum;

That is clearly horrible. I can't pull a similar trick using either HQL or Criteria queries because ItemImages is not a mapped entity, it's a join table being managed by Hibernate.

+2  A: 

The index() HQL function is what you're looking for:

select image
from ItemEntity item
join item.images image
where index(image) = 4
and item.id = 111

It has been standardized in JPA 2.0 also as INDEX. From the specification:

4.6.17.2.2 Arithmetic Functions

functions_returning_numerics::=
        ABS(simple_arithmetic_expression) |
        SQRT(simple_arithmetic_expression) |
        MOD(simple_arithmetic_expression, simple_arithmetic_expression) |
        SIZE(collection_valued_path_expression) |
        INDEX(identification_variable)

(...)

The INDEX function returns an integer value corresponding to the position of its argument in an ordered list. The INDEX function can only be applied to identification variables denoting types for which an order column has been specified.

In the following example, studentWaitlist is a list of students for which an order column has been specified:

SELECT w.name
FROM Course c JOIN c.studentWaitlist w
WHERE c.name = ‘Calculus’
AND INDEX(w) = 0

References

  • Hibernate Core Reference Guide
  • JPA 2.0 Specification
    • Section 4.6.17.2.2 Arithmetic Functions
Pascal Thivent
Excellent, thank you Pascal, that answered it in a nicely object oriented way (no need for me to fetch the image_id and then fetch an object based on that). I had not been able to find that part of the Hibernate documentation.. if only the @IndexColumn referenced that section, where the index() and related functions are described. It's interesting to see such constructs as "from Order order where order.items[0].id = 1234", too.
Nick Fenwick
@Nick You're welcome. You're right, it's not always easy to link a particular annotation to the related query language features. That's probably an area of the documentation (and the spec) that could be improved. And yes, the "array like" syntax is also very interesting.
Pascal Thivent