简体   繁体   English

JPA-在非实体类中连接两个表

[英]JPA- Joining two tables in non-entity class

I am a newbie ,tried to google but I am unable to solve my query. 我是新手,试图谷歌,但我无法解决我的问题。 Please help. 请帮忙。

I am trying to map two entities : PersonA and Person in my POJO class PersonC 我试图在我的POJO类PersonC中映射两个实体:PersonA和Person

@Entity
class PersonA{
     String sample_field;
}

@Entity
class Person{
     String id;
     String name;

}

Above two are entities of jpa. 以上两个是jpa的实体。

Now I want to merge them into one pojo class. 现在我想将它们合并到一个pojo类中。

class PersonC
{
   Strind id;
   String address;
}

Tried below code but when I try to fetch Address/Foreign key field it does not work. 尝试下面的代码,但当我尝试获取地址/外键字段时,它不起作用。

@SqlResultSetMapping(name="PersonC", 
classes = {
   @ConstructorResult(targetClass = PersonC.class, 
    columns = {@ColumnResult(name="name")
              , @ColumnResult(name="address")
    )}

where should I define @SqlResultSetMapping ,for which class from the above? 我应该在哪里定义@SqlResultSetMapping,从上面的哪个类? ) }) )})

@SqlResultSetMapping can be placed at any entity class (don't annotate POJOs - it won't work). @SqlResultSetMapping可以放在任何实体类中(不要注释POJO - 它不起作用)。 Mapping to POJO class with @ConstructorResult was added in version 2.1 of JPA. 在JPA 2.1版中添加了使用@ConstructorResult映射到POJO类。 POJO used with the mapping has to have correct constructor. 与映射一起使用的POJO必须具有正确的构造函数。

All columns corresponding to arguments of the intended constructor must be specified using the columns element of the ConstructorResult annotation in the same order as that of the argument list of the constructor. 必须使用ConstructorResult批注的columns元素以与构造函数的参数列表相同的顺序指定与预期构造函数的参数对应的所有列。

Please consult following example with query usage and work out your case accordingly. 请参考以下示例查询用法并相应地计算您的案例。

@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();

Please note that aliases ( AS name and AS street ) has to match the names in @ColumnResult s. 请注意,别名( AS nameAS street )必须与@ColumnResult中的名称匹配。 The example was tested against Ecliselink 2.5.1. 该示例针对Ecliselink 2.5.1进行了测试。

This post deals with the Hibernate. 这篇文章涉及Hibernate。

The suggestion of putting the @SqlResultSetMapping and @NamedNativeQuery (or @NamedQuery ) inside the @Entity class definition is not elegant and evidently does not follow the separation of concerns principle. @SqlResultSetMapping@NamedNativeQuery(@NamedQuery)@Entity类定义的建议是不优雅,显然不符合原则的关注点分离。

The more proper solution is the usage of the @MappedSuperclass annotation as the following: 更合适的解决方案是使用@MappedSuperclass注释,如下所示:

SingerExtended.java (the class must be abstract ): SingerExtended.java (该类必须是抽象的 ):

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 {
}

then DAO class SingerExtendedDAO.java : 然后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;
    }

}

and finally the DTO class SingerExtendedDTO.java (you must provide "full" constructor): 最后是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 ...
}

If all this is put together the way presented above, we obtain a proper solution: 如果以上述方式将所有这些组合在一起,我们将获得一个合适的解决方案:

  • everything is in one package, 一切都在一个包里,
  • query declaration does not pollute any unconcerned entity, 查询声明不会污染任何不关心的实体,
  • separation of concerns is preserved (seperated query+mapping, DAO and DTO). 保留关注点分离(分离查询+映射,DAO和DTO)。

Just found a bit simpler solution using JPQL. 刚刚使用JPQL找到了一个更简单的解决方案。 I stole part of the example from @zbig's answer: 我从@ 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();

The benefit of this solution is that you don't need to use the @SqlResultSetMapping annotation, which must be placed on any entity class, not the DTO class! 这个解决方案的好处是你不需要使用@SqlResultSetMapping注释,它必须放在任何实体类上,而不是DTO类! And that's sometimes confusing because the entity class could only be partially related (when joining multiple tables for example). 这有时令人困惑,因为实体类只能部分相关(例如,当连接多个表时)。

More info here 更多信息在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM