Hibernate criteria query for Collection Table?
I have following Entity
@Entity
@Table(name = "rule")
public class Rule implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "rule_id")
private Long id;
@ElementCollection(targetClass = Action.class)
@CollectionTable(name = "rule_action", joinColumns = @JoinColumn(name = "rule_id"))
@Enumerated(value = EnumType.STRING)
@Column(name = "action")
private Set<Action> actions;
//After editing as per jbrookover's suggestion adding a new mapping
@OneToMany(cascade = 开发者_JAVA百科CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "rule_id")
private Set<RuleAction> ruleActions;
}
Following is my Action
public enum Action {
PHONE, EMAIL, POSTAL,PHONE_OR_EMAIL, SMS;
}
I want to fetch a List of rule having particular set of actions I am trying this
DetachedCriteria criteria = DetachedCriteria.forClass(Rule.class,"rule");
criteria = criteria.createAlias("rule.actions", "action");
criteria.add(Restrictions.in("action.name",actionSet));
return getHibernateTemplate().findByCriteria(criteria);
But getting org.hibernate.MappingException: collection was not an association: exception..
EDIT So after guidance from jbrookover I tried going for a wrapper class for Action named as RuleAction and was able to eshtablish the oneToMany relationship, Also I modified the query as follows
Set<Action> act = new HashSet<Action>();
act.add(Action.EMAIL);
act.add(Action.POSTAL);
DetachedCriteria criteria = DetachedCriteria.forClass(Rule.class);
criteria.add(Restrictions.eq(SUPPORT_LANG, Language.valueOf("EN")))
.createCriteria("ruleActions").add(Restrictions.in("action",act));
return getHibernateTemplate().findByCriteria(criteria);
But this is returning me all the rule which are having either EMAIL or POSTAL but what I want is all the rule having EMAIL and POSTAL both Please help me in modifying the query.
Sorry, what you are trying to do, specifically, is not supported in Hibernate. See this FAQ:
http://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Im_getting_orghibernateMappingException_collection_was_not_an_association_when_I_try_to_join_a_collection_of_components_with_Criteria_queries
I, too, was quite displeased with this. As you can see, though, they've tried to fix it an have been unable to do so and have put it on the community to deal with it. You have a few options:
- Use HQL to run the query.
- Re-write your collection association as an actual entity class with a single Enum field.
You can do something like this:
@Entity
public class ActionWrapper {
public Action action;
}
Then, update your associations and query accordingly so that Rule
has a Set<ActionWrapper>
. There are other workarounds, but you essentially cannot use Criteria
and @ElementCollection
together.
Update
In order to restrict the query further, to ensure that you get Rules that meet BOTH actions, you need to run the subquery and do a conjunction - 'and' - of the matched values. Something like this should work:
Criteria subCriteria = criteria.createCriteria("ruleActions");
Disjunction and = Restrictions.conjunction();
for (Action a : act)
and.add(Restrictions.eq("action", a)
subCriteria.add(and);
At the end, you may find duplicate results. This is common and can be eliminated by adding this:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
I can't speak for the efficiency of this code - HQL may be better in the long run. However, I've done something similar in other projects and haven't encountered any issues.
Another solution without wrapping your enums and using ResultTransformers is this:
@ElementCollection(fetch = FetchType.EAGER)
@Enumerated(value = EnumType.STRING)
@Column(name = " action")
@JoinTable(name = "rule_action", joinColumns = { @JoinColumn(name = "rule_id") })
private Set<Action> actions;
Query:
DetachedCriteria criteria = DetachedCriteria.forClass(Rule.class,"rule");
criteria = criteria.createAlias("rule.actions", "action");
criteria.add(Restrictions.in("action." + CollectionPropertyNames.COLLECTION_ELEMENTS, actionSet));
return getHibernateTemplate().findByCriteria(criteria);
That's what worked for me with Hibernate 4.1.6.Final. I found it here.
The criteria query looks fine. Suppose we have tables:
rule(id, name,...)
action(id, name,..)
rule_action(id, rule_id, action_id,...) -- (or rule_id+action_id as a composite pkey)
The mapping should be as follows:
public class Rule {
...
@ManyToMany(mappedBy = "rule")
private Set<Action> actions;
...
}
public class Action {
...
@JoinTable(
name="rule_action",
joinColumns={@JoinColumn(name="action_id")},
inverseJoinColumns={@JoinColumn(name="rule_id")}
)
private Set<Rule> rules;
...
}
This way your criteria query should work.
精彩评论