2

I have an entity named PetOwner. PetOwner has a list of Pets. And each Pet has a Store (ie: the store this pet came from). Pet is an abstract class and getting a concrete version does lots of joins (@Inheritance(strategy = InheritanceType.JOINED)). I want to have a method on PetOwner with this signature:

Set<Store> getAllStores();

The problem is that loading the list of Pets is very slow and in fact unnecessary to get the list of Stores. When I call getAllStores, I'd like it to run SQL like this:

SELECT DISTINCT store_id
FROM pet
WHERE petowner_id = x;

But if I implement it like this:

Set<Stores> stores = //
for (pet : pets)
   stores.add(pet.getStore());
return stores;

It'll pull in all the Pets and be very slow. So how can I prevent it from pulling in all the Pets? Ideally, I do want this method on the PetOwner object because I think the code is more object oriented that way.

Also, there are other methods inside PetOwner where I do need to load the list of Pets even though it's slow. But I don't always call it, that's why I'd like just this method to avoid loading the pet list.

Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356

2 Answers2

2

There is no standard way to do this because you are not mapping this data. Different options I see are

  1. To use a named query on the EM directly
  2. Add the method to PetOwner that takes an ENntityManager and do the query directly, or
  3. add a transient list to the pet owner and populate it in postload events that run the query. This won't be able to see updates though unless the PetOwner is reloaded.
Chris
  • 20,138
  • 2
  • 29
  • 43
1

I assume you have a service that you use to get your PetOwners. Rather than add your method to the PetOwner, I would suggest you create a new method on your service.

public Set<Stores> getStoresForOwner( PetOwner owner );

And have that use whatever you are using for your data access layer (hibernate template, Spring-data-jpa repository, EntityManager directly) to make the call with a named query.

@NamedQuery(name="PetOwnerStore.findAll", query="SELECT distinct p.Store from Pet p where p.Owner.id = :petOwnerId");

It could be argued that since you are selecting from Pet that JPA is probably going to have to do all the joins anyway...

All of the above assumes (among others since you didn't give any entity mapping) that your relation between Pet and Store is lazy fetch and therefore not already populated.

digitaljoel
  • 26,265
  • 15
  • 89
  • 115
  • So there's no way to do this as a method inside the PetOwner? – Daniel Kaplan Feb 08 '13 at 00:40
  • 3
    @tieTYT: Correct. One idea behind JPA is that the entities live in ignorance of the workings of the JPA machinery, and should be usable as plain objects. Giving them the ability to do what you want would couple them to the JPA machinery and defeat this. – Tom Anderson Feb 08 '13 at 13:12
  • That said, there are already things in JPA which also allow that kind of coupling, such as [`@PostLoad`](http://docs.oracle.com/javaee/6/api/javax/persistence/PostLoad.html) and friends, so the situation is not as pure as was originally hoped. – Tom Anderson Feb 08 '13 at 13:13