我有以下实体(示例):
@Entity
@Table(name = "person")
public class Person implements Serializable {
@Id
@Column(name = "person_id", columnDefinition = "UUID")
private UUID userId;
@Column(name = "name")
private String name;
@ElementCollection
@MapKeyColumn(name = "phonetype")
@Column(name = "number")
@CollectionTable(name = "person_phones", joinColumns = @JoinColumn(name = "userId"))
private Map<String, String> phoneNumbers;
}
现在,在这个例子中,phoneNumbers是String,String。我们假设密钥是类型(如“移动”,“主页”,“办公室”,“传真”,“寻呼机”......),值是任何文本格式的实际数字。
我想查询一个有两个电话号码的人:
Select * From person where
in his phone_numbers exists phonetype = 'home' and number = '0-123-456'
and also in his phone_numbers exists phonetype = 'mobile' and number = '9-876-421'
(and possibly, dynamically others)
and name = 'John'
我已经构建了一个有效的sql子查询:
select home.userId from
(
(SELECT userId from person_phones
where (phonetype = 'home' and number = '0-123-456'))
) as home,
(
(SELECT userId from person_phones
where (phonetype = 'mobile' and number = '9-876-421'))
) as mobile
where home.userId = mobile.userId
如上所述,这只是一个sql子查询。我正在我的项目中编写JPA 2.1条件查询。这看起来很奇怪。任何人都可以给我一个提示吗?
有一个类似的问题,使用多个内部联接而不是子查询解决它。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Person-Test");
EntityManager em = emf.createEntityManager();
Map<String, String> phoneNumbers = new HashMap<>();
phoneNumbers.put("home","0-123-456");
phoneNumbers.put("mobile","9-876-421");
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> personRoot = query.from(Person.class);
query.select(personRoot);
phoneNumbers.forEach((k, v) -> {
MapJoin<Person, String, String> phoneNrJoinJoin = personRoot.joinMap("phoneNumbers");
phoneNrJoinJoin.on(cb.equal(phoneNrJoinJoin.key(), k), cb.equal(phoneNrJoinJoin.value(), v));
});
query.where(cb.equal(personRoot.get("name"), "John"));
List<Person> people = em.createQuery(query).getResultList();
这导致以下hibernate查询(为清晰起见,重命名为别名)
SELECT person.person_id, person.name
FROM person
INNER JOIN person_phones a
ON person.person_id = a.userid
AND (a.phonetype = ? AND a.NUMBER = ?)
INNER JOIN person_phones b
on person.person_id=b.userId
and (b.phonetype=? and b.number = ? )
WHERE
person.name = ?;
返回所有提到的电话号码匹配的所有类型的元组。