JPA Criteria API查询超类时匹配子类独有属性报错如何解决
解决方案
你之前的实现存在两处核心问题:
- 强转
Root仅为Java语法层面的类型转换,不会改变JPA元模型识别的根实体类型,因此依然会从Entity类中查找inheritedTags属性,触发属性不存在的报错。 - 你之前使用
criteriaBuilder.treat()的方式有误,treat本身不会过滤非目标子类的实体,只要配合逻辑判断即可同时返回所有符合条件的实体,包括非Artwork子类的实体。
注意:你提供的实体代码中
Album和Track直接继承了Entity而非Artwork,如果实际业务中这两个类是Artwork的子类,请先修正继承关系,否则以下代码中的类型判断逻辑需要对应调整。
方案1:Criteria API 正确实现
final CriteriaBuilder builder = em.getCriteriaBuilder(); final CriteriaQuery<Entity> query = builder.createQuery(Entity.class); Root<Entity> root = query.from(Entity.class); // 通用匹配条件:适配所有Entity子类 Predicate commonMatch = builder.or( builder.like(root.get("title"), "%a"), builder.like(root.get("tags"), "%a") ); // Artwork子类额外匹配条件 Root<Artwork> treatedRoot = builder.treat(root, Artwork.class); Predicate artworkMatch = builder.and( root.type().in(Album.class, Track.class), builder.or( commonMatch, builder.like(treatedRoot.get("inheritedTags"), "%a") ) ); // 合并所有条件 query.where(builder.or(commonMatch, artworkMatch)); query.select(root); List<Entity> resultList = em.createQuery(query).getResultList();
方案2:JPQL 实现(写法更简洁)
SELECT e FROM Entity e WHERE e.title LIKE :keyword OR e.tags LIKE :keyword OR (TYPE(e) IN (Album, Track) AND TREAT(e AS Artwork).inheritedTags LIKE :keyword)
代码中调用时传入参数即可:
List<Entity> resultList = em.createQuery("上面的JPQL语句", Entity.class) .setParameter("keyword", "%a") .getResultList();
方案3:原生SQL 实现(完全匹配你给出的等效SQL逻辑)
如果你不需要适配多数据库,直接写原生SQL是最稳妥的方案,无需处理JPA继承相关的语法限制:
String nativeSql = "SELECT * FROM jamschema.library_entities entities " + "WHERE entities.title LIKE :keyword " + "OR entities.tags LIKE :keyword " + "OR (entities.entity_type IN ('A', 'T') AND (" + "entities.inheritedTags LIKE :keyword " + "OR entities.title LIKE :keyword " + "OR entities.tags LIKE :keyword" + "))"; List<Entity> resultList = em.createNativeQuery(nativeSql, Entity.class) .setParameter("keyword", "%a") .getResultList();
内容的提问来源于stack exchange,提问作者Amirhosein Al
相关产品推荐
相关产品推荐

