【问题标题】:JPA- Joining two tables in non-entity classJPA-在非实体类中连接两个表
【发布时间】: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 ,为上面的哪个类? ) })

【问题讨论】:

    标签: java mysql sql jpa


    【解决方案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 nameAS street)必须与 @ColumnResults 中的名称匹配。 该示例针对 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]。有什么想法吗?
    猜你喜欢
    • 2013-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-29
    相关资源
    最近更新 更多