3

In one of my classes, say Location, I have something like this:

private List<Magician> magicians;

...

@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name="location_id")
public List<Magician> getMagicians() {
    return magicians;
} 

public void setMagicians(List<Magician> magicians) {
    this.magicians = magicians;
}

where Magician has variables

private Integer id;
private Integer location_id;
private Boolean active;

Now I would like to modify the getter annotation so as to get only the magicians for which active is true.

How can I achieve this?

Thanks for your attention.

Andrea Alciato
  • 199
  • 1
  • 11
  • OK, thanks to JB and Kevin for the answers. The motivation for my question is that there are situations where I may want to restrict the dataset that the application accesses, without introducing any new methods in the code. So I would quickly prepare the data by setting active to true with a db query. – Andrea Alciato May 02 '13 at 09:31

3 Answers3

2

First of all, Magician should not have a location_id field. It should have a field of type Location. This would make a bidirectional association between the two entities:

@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "location")
public List<Magician> getMagicians() {
    return magicians;
} 

...

@ManyToOne
@JoinColumn(name = "location_id")
public Location getLocation() {
    return this.location;
}

Then, to answer your question, the state and associations of an entity are not used to implement a speciic use-case or query. Thy're used to model what the database contains. To get the active magicians of a given location, you should simply use a query:

select m from Magician m where m.active = true and m.location = :location
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks for your reply. As for the unidrectional vs. bidirectional point, I would agree with axtavt's comment (http://stackoverflow.com/a/5361587/1966869 )" that 'navigational access is not always good, especially for "one-to-very-many" and "many-to-very-many" relationships'. . I don't think one should use bidirectional unless one needs to, especially in an app where "one-to-very-many" is ubiquitous, like mine. I think this is a very relevant issue and any further comment you may have is welcome and will be appreciated. – Andrea Alciato May 02 '13 at 09:45
  • If it's a One-To-Very-Many, then there might be a good reason to model it as a unidirectional ManyToOne association. But you're modelling it as a unidirectional OneToMany. And you have the ID of the location anyway in your Magician (which makes your mapping incorrect, since this column is mapped twice), so why not have a lazy-loaded Location instead? – JB Nizet May 02 '13 at 12:13
  • Because Location is a big object, which I don't usually need (or I already have) when I work with a magician, whereas I usually need just the location_id. So I keep just the location_id id at hand. As for your observation that it is mapped-twice, as far as I understand it would be mapped twice if the relation were bidirectional, i.e. if I followed your advice (which I appreciate, so please correct me if I am wrong). – Andrea Alciato May 02 '13 at 14:02
  • If every bidirectional association resulted in a mpping error, bidirectional associations wouldn't be very useful. By mapping the ID as a basic column in Magician, and as the join column for the OneToMany association in Location, you're mapping it twice, which will cause a mapping exception. My opinion is that you're trying to optimize prematurely. I would use a bidirectional association. getting a location by ID should be extremely fast, even if it's a big object. And if you don't need the location, since it's lazy-loaded, no query will be executed to load it. – JB Nizet May 02 '13 at 14:21
  • My point is that I constantly need the location_id and I don't want load an obese Location every time I need a location_id. No mapping exception yet and it has been some time. By the way and as an illustration, the magician cound be an Atom and the location a Protein. Further comments always welcome. PS What I meant is, it would be mapped twice if I followed your advice AND left location_id in Magician. I did not mean that your suggestion is wrong. – Andrea Alciato May 02 '13 at 14:34
1

I wouldn't perform this work at the entity level, instead I would perform it using the EntityManager to create a query. As JB mentions you need to add location as a composite object instead of an Integer.

//In Method

List<Magician> magicians = 
   em.createQuery("select m from Location l join Magician m where m.active = true and
   l.[specify id here] = :locationId", Magician.class).setParameter("locationId",
   1).getResultList();
Kevin Bowersox
  • 93,289
  • 19
  • 159
  • 189
0

In Hibernate my issue can be solved using a filter, as explained here : https://stackoverflow.com/a/6920847/1966869. The xml version, which I have just tested and appears quite practical, is described here : http://www.mkyong.com/hibernate/hibernate-data-filter-example-xml-and-annotation/. There might be other solutions using @Where (How to use @Where in Hibernate) or @JoinFormula (@JoinFormula and @OneToMany definition - poor documentation), but I have not tested them.

Community
  • 1
  • 1
Andrea Alciato
  • 199
  • 1
  • 11