JPA-在非实体类中连接两个表
Posted
技术标签:
【中文标题】JPA-在非实体类中连接两个表【英文标题】:JPA- Joining two tables in non-entity class 【发布时间】:2014-09-30 11:57:06 【问题描述】:我是新手,尝试谷歌,但我无法解决我的查询。 请帮忙。
我正在尝试映射两个实体:我的 POJO 类 PersonC 中的 PersonA 和 Person
@Entity
class PersonA
String sample_field;
@Entity
class Person
String id;
String name;
以上两个是jpa的实体。
现在我想将它们合并到一个 pojo 类中。
class PersonC
Strind id;
String address;
尝试了下面的代码,但是当我尝试获取地址/外键字段时它不起作用。
@SqlResultSetMapping(name="PersonC",
classes =
@ConstructorResult(targetClass = PersonC.class,
columns = @ColumnResult(name="name")
, @ColumnResult(name="address")
)
我应该在哪里定义 @SqlResultSetMapping ,为上面的哪个类? ) )
【问题讨论】:
【参考方案1】:@SqlResultSetMapping
可以放在 any 实体类中(不要注释 POJO - 它不起作用)。 JPA 2.1 版中添加了使用@ConstructorResult
映射到 POJO 类。与映射一起使用的 POJO 必须有正确的构造函数。
与预期构造函数的参数对应的所有列必须使用 ConstructorResult 注释的列元素指定,其顺序与构造函数的参数列表的顺序相同。
请参考以下查询使用示例并相应地解决您的情况。
@Entity
public class Address
@Id int id;
String street;
@SqlResultSetMapping(name="PersonDTOMapping",
classes =
@ConstructorResult(targetClass = PersonDTO.class,
columns = @ColumnResult(name="name"), @ColumnResult(name="street")
)
)
@Entity
public class Person
@Id int id;
String name;
Address address;
public class PersonDTO
String name;
String street;
public PersonDTO(String name, String street)
this.name = name;
this.street = street;
// usage
Query query = em.createNativeQuery(
"SELECT p.name AS name, a.street AS street FROM Person p, Address a WHERE p.address_id=a.id",
"PersonDTOMapping");
List<PersonDTO> result = query.getResultList();
请注意,别名(AS name
和 AS street
)必须与 @ColumnResult
s 中的名称匹配。
该示例针对 Ecliselink 2.5.1 进行了测试。
【讨论】:
那么将其映射到 pojo 有什么好处呢? 您能否举一个使用@EntityResult 而不是@ColumnResult 的JPA 2.0 示例? 与带有 NEW 关键字的 JPQL 非常相似。所以,有趣的部分是我们现在被困在旧的 JPA 中。如果您也将表 PAIRS_OF_SHOES 作为列表加入,您将如何映射这个有什么想法吗? 正如在下面的解决方案中指出的那样,您并不总是有一个实体类来添加注释,至少不是正确的。在调用存储过程以将结果映射到 DTO 并且您没有该 DTO 的实体版本时很常见(出于显而易见的原因)。下面的解决方案解决了这种情况。【参考方案2】:刚刚找到了一个使用 JPQL 的更简单的解决方案。 我从@zbig 的回答中窃取了部分示例:
@Entity
public class Address
@Id int id;
String street;
@Entity
public class Person
@Id int id;
String name;
Address address;
public class PersonDTO
String name;
String street;
public PersonDTO(String name, String street)
this.name = name;
this.street = street;
List<PersonDTO> listOfPersons = em.createQuery("select new com.example.PersonDTO(p.name, a.street) " +
"from Person p, Address a " +
"WHERE p.address.id=a.id", PersonDTO.class).getResultList();
这个解决方案的好处是你不需要使用@SqlResultSetMapping注解,它必须放在any实体类上,而不是DTO类上!这有时会让人感到困惑,因为实体类只能部分相关(例如,在连接多个表时)。
更多信息here
【讨论】:
你是英雄,我希望我可以为这个答案投票 100 次,并将你的答案打印在我的 T 恤上并一直穿着:)【参考方案3】:这篇文章涉及 Hibernate。
将 @SqlResultSetMapping 和 @NamedNativeQuery(或 @NamedQuery)放入 @Entity 的建议类定义不够优雅,显然没有遵循关注点分离原则。
更合适的解决方案是使用 @MappedSuperclass 注释,如下所示:
SingerExtended.java(类必须是abstract):
package pl.music.model.singer.extended;
import javax.persistence.ColumnResult;
import javax.persistence.ConstructorResult;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.SqlResultSetMapping;
@MappedSuperclass
@SqlResultSetMapping( // @formatter:off
name = "SingerExtendedMapping",
classes = @ConstructorResult(
targetClass = SingerExtendedDTO.class,
columns =
@ColumnResult(name = "singer_id", type = Long.class),
@ColumnResult(name = "first_name"),
@ColumnResult(name = "last_name"),
@ColumnResult(name = "count_albums", type = Long.class)
)
)
@NamedNativeQueries(
@NamedNativeQuery(
name = "SingerExtendedAsc",
query = "select"
+ " singer.singer_id,"
+ " singer.first_name,"
+ " singer.last_name,"
+ " (select count(*) from album where album.singer_id = singer.singer_id) as count_albums"
+ " from singer"
+ " group by singer.singer_id"
+ " order by last_name collate :collation asc, first_name collate :collation asc",
resultSetMapping = "SingerExtendedMapping"
)
) // @formatter:on
public abstract class SingerExtended
然后是DAO类SingerExtendedDAO.java:
package pl.music.model.singer.extended;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SingerExtendedDAO
@PersistenceContext
EntityManager entityManager;
@Autowired
private String collation;
public List<SingerExtendedDTO> getAll(Integer page, Integer count)
TypedQuery<SingerExtendedDTO> query = entityManager.createNamedQuery("SingerExtendedAsc", SingerExtendedDTO.class);
query.setParameter("collation", collation);
if ((count != null) && (count.intValue() > 0))
query.setMaxResults(count.intValue());
if ((page != null) && (page.intValue() >= 0))
query.setFirstResult(count.intValue() * page.intValue());
List<SingerExtendedDTO> singerExtendedDTOs = query.getResultList();
return singerExtendedDTOs;
最后是 DTO 类 SingerExtendedDTO.java(您必须提供“完整”构造函数):
package pl.music.model.singer.extended;
public class SingerExtendedDTO
private Long singerId;
private String firstName;
private String lastName;
private Long countAlbums;
// IMPORTANT: this constructor must be defined !!!
public SingerExtendedDTO(Long singerId, String firstName, String lastName, Long countAlbums)
this.singerId = singerId;
this.firstName = firstName;
this.lastName = lastName;
this.countAlbums = countAlbums;
... getters & setters ...
如果按照上面介绍的方式将所有这些放在一起,我们就会得到一个适当的解决方案:
一切都在一个包中, 查询声明不会污染任何无关实体, 保留关注点分离(分离查询+映射、DAO 和 DTO)。【讨论】:
这是一个比公认的恕我直言更好的解决方案。在许多情况下,当您将存储的过程结果映射到 DTO 时,您没有实体可以放置这些注释。这很容易解决这个问题。 我收到了No query defined for that name [SingerExtendedAsc]
。有什么想法吗?以上是关于JPA-在非实体类中连接两个表的主要内容,如果未能解决你的问题,请参考以下文章
如何使用类中的 setters 方法将数据插入 JPA 实体表?