JPA CriteriaQuery 包括 2 个子查询
JPA CriteriaQuery including 2 Subqueries
我在使用包含两个子查询的 JPA 构建工作复杂的 CriteriaQuery 时遇到问题。
在 SQL 中,查询将如下所示:
SELECT * FROM photos p WHERE ((p.userID = 1) AND ((p.privacy = 0)
OR (p.privacy = 1 AND EXISTS (SELECT * FROM friendships fs WHERE ((fs.userID = p.userID AND fs.userID2 = 2) OR (fs.userID = 2 AND fs.userID2 = p.userID))))
OR (p.privacy = 2 AND EXISTS (SELECT * FROM photo_privacy_users ppu WHERE (ppu.photoID = p.photoID AND ppu.userID = 2)))
));
澄清 sql 查询:
photos.privacy = 0 表示 PrivacyType.PUBLIC
photos.privacy = 1 表示 PrivacyType.PRIVATE
photos.privacy = 2 表示 PrivacyType.COSTUM
目前我收到以下错误:
org.hibernate.engine.jdbc.spi.SqlExceptionHelper - 操作数应包含 1 列
org.hibernate.exception.DataException: 无法提取结果集
以下代码生成查询:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Photo> cq = cb.createQuery(Photo.class);
Root<Photo> photo = cq.from(Photo.class);
cq.select(photo);
Subquery<Friendship> sq1 = cq.subquery(Friendship.class);
Root<Friendship> friendship = sq1.from(Friendship.class);
sq1.select(friendship);
sq1.where(cb.or(cb.and(cb.equal(friendship.get(Friendship_.userID), photo.get(Photo_.userID)), cb.equal(friendship.get(Friendship_.userID2), loggedinUserID)), cb.and(cb.equal(friendship.get(Friendship_.userID), loggedinUserID), cb.equal(friendship.get(Friendship_.userID2), photo.get(Photo_.userID)))));
Subquery<PhotoPrivacyUser> sq2 = cq.subquery(PhotoPrivacyUser.class);
Root<PhotoPrivacyUser> privacyUser = sq2.from(PhotoPrivacyUser.class);
sq2.select(privacyUser);
sq2.where(cb.and(cb.equal(privacyUser.get(PhotoPrivacyUser_.photoID), photo.get(Photo_.photoID)), cb.equal(privacyUser.get(PhotoPrivacyUser_.userID), loggedinUserID)));
Predicate predicate1 = cb.equal(photo.get(Photo_.userID), userID);
Predicate predicate2 = cb.equal(photo.get(Photo_.privacy), PrivacyType.PUBLIC);
Predicate predicate3 = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.PRIVATE), cb.exists(sq1));
Predicate predicate4 = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.COSTUM), cb.exists(sq2));
cq.where(cb.and(predicate1, cb.or(predicate2, predicate3, predicate4)));
return em.createQuery(cq).getResultList();
变量 userID 和 loggedInUserID 是 Long 类型的方法参数。
元模型照片_:
@StaticMetamodel(Photo.class)
public abstract class Photo_ {
public static volatile SingularAttribute<Photo, Long> photoID;
public static volatile SingularAttribute<Photo, PrivacyType> privacy;
public static volatile ListAttribute<Photo, PhotoPrivacyUser> photoPrivacyUsers;
public static volatile SingularAttribute<Photo, String> name;
public static volatile SingularAttribute<Photo, User> user;
}
元模型友谊_:
@StaticMetamodel(Friendship.class)
public abstract class Friendship_ {
public static volatile SingularAttribute<Friendship, User> user2;
public static volatile SingularAttribute<Friendship, Date> createdAt;
public static volatile SingularAttribute<Friendship, Long> userID2;
public static volatile SingularAttribute<Friendship, Long> userID;
public static volatile SingularAttribute<Friendship, User> user;
}
元模型 PhotoPrivacyUser_:
@StaticMetamodel(PhotoPrivacyUser.class)
public abstract class PhotoPrivacyUser_ {
public static volatile SingularAttribute<PhotoPrivacyUser, Date> createdAt;
public static volatile SingularAttribute<PhotoPrivacyUser, Long> photoID;
public static volatile SingularAttribute<PhotoPrivacyUser, Photo> photo;
public static volatile SingularAttribute<PhotoPrivacyUser, Long> userID;
public static volatile SingularAttribute<PhotoPrivacyUser, User> user;
}
元模型用户_:
@StaticMetamodel(User.class)
public abstract class User_ {
public static volatile SingularAttribute<User, Long> userID;
public static volatile ListAttribute<User, Photo> photos;
public static volatile ListAttribute<User, Friendship> friends;
public static volatile SingularAttribute<User, Date> createdAt;
public static volatile ListAttribute<User, PhotoPrivacyUser> photoPrivacyUsers;
public static volatile SingularAttribute<User, String> name;
public static volatile SingularAttribute<User, String> email;
public static volatile SingularAttribute<User, String> username;
}
如果我从查询中删除 predicate3 和 predicate4,我的查询工作正常。
我不确定我是应该使用子查询还是应该使用连接。
我解决了我的问题。
因为我怀疑正确的方法是使用连接。
使用带有 exists 的子查询并没有像怀疑的那样工作,因为 exists returns 多于 true 或 false。如果存在为真,结果可以是多行。
以下代码生成具有三个联接的查询:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Photo> cq = cb.createQuery(Photo.class);
Root<Photo> photo = cq.from(Photo.class);
Join<Photo, User> userJoin = photo.join(Photo_.user, JoinType.LEFT);
Join<Photo, PhotoPrivacyUser> ppuJoin = photo.join(Photo_.photoPrivacyUsers, JoinType.LEFT);
Join<User, Friendship> fsJoin = userJoin.join(User_.friends, JoinType.LEFT);
cq.select(photo);
Predicate predicateUserID = cb.equal(photo.get(Photo_.userID), userID);
Predicate predicatePublic = cb.equal(photo.get(Photo_.privacy), PrivacyType.PUBLIC);
Predicate predicateFriendship = cb.or(cb.and(cb.equal(fsJoin.get(Friendship_.userID), photo.get(Photo_.userID)), cb.equal(fsJoin.get(Friendship_.userID2), loggedinUserID)), cb.equal(fsJoin.get(Friendship_.userID), loggedinUserID), cb.equal(fsJoin.get(Friendship_.userID2), photo.get(Photo_.userID)));
Predicate predicatePrivate = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.PRIVATE), predicateFriendship);
Predicate predicatePpu = cb.and(cb.equal(ppuJoin.get(PhotoPrivacyUser_.photoID), photo.get(Photo_.photoID)), cb.equal(ppuJoin.get(PhotoPrivacyUser_.userID), loggedinUserID));
Predicate predicateCostum = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.COSTUM), predicatePpu);
cq.where(cb.and(predicateUserID, cb.or(predicatePublic, predicatePrivate, predicateCostum)));
return entityManager.createQuery(cq).getResultList();
也许这会帮助遇到同样问题或疑问如何做类似事情的人。 :)
我在使用包含两个子查询的 JPA 构建工作复杂的 CriteriaQuery 时遇到问题。
在 SQL 中,查询将如下所示:
SELECT * FROM photos p WHERE ((p.userID = 1) AND ((p.privacy = 0)
OR (p.privacy = 1 AND EXISTS (SELECT * FROM friendships fs WHERE ((fs.userID = p.userID AND fs.userID2 = 2) OR (fs.userID = 2 AND fs.userID2 = p.userID))))
OR (p.privacy = 2 AND EXISTS (SELECT * FROM photo_privacy_users ppu WHERE (ppu.photoID = p.photoID AND ppu.userID = 2)))
));
澄清 sql 查询:
photos.privacy = 0 表示 PrivacyType.PUBLIC
photos.privacy = 1 表示 PrivacyType.PRIVATE
photos.privacy = 2 表示 PrivacyType.COSTUM
目前我收到以下错误:
org.hibernate.engine.jdbc.spi.SqlExceptionHelper - 操作数应包含 1 列
org.hibernate.exception.DataException: 无法提取结果集
以下代码生成查询:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Photo> cq = cb.createQuery(Photo.class);
Root<Photo> photo = cq.from(Photo.class);
cq.select(photo);
Subquery<Friendship> sq1 = cq.subquery(Friendship.class);
Root<Friendship> friendship = sq1.from(Friendship.class);
sq1.select(friendship);
sq1.where(cb.or(cb.and(cb.equal(friendship.get(Friendship_.userID), photo.get(Photo_.userID)), cb.equal(friendship.get(Friendship_.userID2), loggedinUserID)), cb.and(cb.equal(friendship.get(Friendship_.userID), loggedinUserID), cb.equal(friendship.get(Friendship_.userID2), photo.get(Photo_.userID)))));
Subquery<PhotoPrivacyUser> sq2 = cq.subquery(PhotoPrivacyUser.class);
Root<PhotoPrivacyUser> privacyUser = sq2.from(PhotoPrivacyUser.class);
sq2.select(privacyUser);
sq2.where(cb.and(cb.equal(privacyUser.get(PhotoPrivacyUser_.photoID), photo.get(Photo_.photoID)), cb.equal(privacyUser.get(PhotoPrivacyUser_.userID), loggedinUserID)));
Predicate predicate1 = cb.equal(photo.get(Photo_.userID), userID);
Predicate predicate2 = cb.equal(photo.get(Photo_.privacy), PrivacyType.PUBLIC);
Predicate predicate3 = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.PRIVATE), cb.exists(sq1));
Predicate predicate4 = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.COSTUM), cb.exists(sq2));
cq.where(cb.and(predicate1, cb.or(predicate2, predicate3, predicate4)));
return em.createQuery(cq).getResultList();
变量 userID 和 loggedInUserID 是 Long 类型的方法参数。
元模型照片_:
@StaticMetamodel(Photo.class)
public abstract class Photo_ {
public static volatile SingularAttribute<Photo, Long> photoID;
public static volatile SingularAttribute<Photo, PrivacyType> privacy;
public static volatile ListAttribute<Photo, PhotoPrivacyUser> photoPrivacyUsers;
public static volatile SingularAttribute<Photo, String> name;
public static volatile SingularAttribute<Photo, User> user;
}
元模型友谊_:
@StaticMetamodel(Friendship.class)
public abstract class Friendship_ {
public static volatile SingularAttribute<Friendship, User> user2;
public static volatile SingularAttribute<Friendship, Date> createdAt;
public static volatile SingularAttribute<Friendship, Long> userID2;
public static volatile SingularAttribute<Friendship, Long> userID;
public static volatile SingularAttribute<Friendship, User> user;
}
元模型 PhotoPrivacyUser_:
@StaticMetamodel(PhotoPrivacyUser.class)
public abstract class PhotoPrivacyUser_ {
public static volatile SingularAttribute<PhotoPrivacyUser, Date> createdAt;
public static volatile SingularAttribute<PhotoPrivacyUser, Long> photoID;
public static volatile SingularAttribute<PhotoPrivacyUser, Photo> photo;
public static volatile SingularAttribute<PhotoPrivacyUser, Long> userID;
public static volatile SingularAttribute<PhotoPrivacyUser, User> user;
}
元模型用户_:
@StaticMetamodel(User.class)
public abstract class User_ {
public static volatile SingularAttribute<User, Long> userID;
public static volatile ListAttribute<User, Photo> photos;
public static volatile ListAttribute<User, Friendship> friends;
public static volatile SingularAttribute<User, Date> createdAt;
public static volatile ListAttribute<User, PhotoPrivacyUser> photoPrivacyUsers;
public static volatile SingularAttribute<User, String> name;
public static volatile SingularAttribute<User, String> email;
public static volatile SingularAttribute<User, String> username;
}
如果我从查询中删除 predicate3 和 predicate4,我的查询工作正常。 我不确定我是应该使用子查询还是应该使用连接。
我解决了我的问题。
因为我怀疑正确的方法是使用连接。
使用带有 exists 的子查询并没有像怀疑的那样工作,因为 exists returns 多于 true 或 false。如果存在为真,结果可以是多行。
以下代码生成具有三个联接的查询:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Photo> cq = cb.createQuery(Photo.class);
Root<Photo> photo = cq.from(Photo.class);
Join<Photo, User> userJoin = photo.join(Photo_.user, JoinType.LEFT);
Join<Photo, PhotoPrivacyUser> ppuJoin = photo.join(Photo_.photoPrivacyUsers, JoinType.LEFT);
Join<User, Friendship> fsJoin = userJoin.join(User_.friends, JoinType.LEFT);
cq.select(photo);
Predicate predicateUserID = cb.equal(photo.get(Photo_.userID), userID);
Predicate predicatePublic = cb.equal(photo.get(Photo_.privacy), PrivacyType.PUBLIC);
Predicate predicateFriendship = cb.or(cb.and(cb.equal(fsJoin.get(Friendship_.userID), photo.get(Photo_.userID)), cb.equal(fsJoin.get(Friendship_.userID2), loggedinUserID)), cb.equal(fsJoin.get(Friendship_.userID), loggedinUserID), cb.equal(fsJoin.get(Friendship_.userID2), photo.get(Photo_.userID)));
Predicate predicatePrivate = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.PRIVATE), predicateFriendship);
Predicate predicatePpu = cb.and(cb.equal(ppuJoin.get(PhotoPrivacyUser_.photoID), photo.get(Photo_.photoID)), cb.equal(ppuJoin.get(PhotoPrivacyUser_.userID), loggedinUserID));
Predicate predicateCostum = cb.and(cb.equal(photo.get(Photo_.privacy), PrivacyType.COSTUM), predicatePpu);
cq.where(cb.and(predicateUserID, cb.or(predicatePublic, predicatePrivate, predicateCostum)));
return entityManager.createQuery(cq).getResultList();
也许这会帮助遇到同样问题或疑问如何做类似事情的人。 :)