繁体   English   中英

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

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

我是新手,试图谷歌,但我无法解决我的问题。 请帮忙。

我试图在我的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,从上面的哪个类? )})

@SqlResultSetMapping可以放在任何实体类中(不要注释POJO - 它不起作用)。 在JPA 2.1版中添加了使用@ConstructorResult映射到POJO类。 与映射一起使用的POJO必须具有正确的构造函数。

必须使用ConstructorResult批注的columns元素以与构造函数的参数列表相同的顺序指定与预期构造函数的参数对应的所有列。

请参考以下示例查询用法并相应地计算您的案例。

@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 )必须与@ColumnResult中的名称匹配。 该示例针对Ecliselink 2.5.1进行了测试。

这篇文章涉及Hibernate。

@SqlResultSetMapping@NamedNativeQuery(@NamedQuery)@Entity类定义的建议是不优雅,显然不符合原则的关注点分离。

更合适的解决方案是使用@MappedSuperclass注释,如下所示:

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

然后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)。

刚刚使用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注释,它必须放在任何实体类上,而不是DTO类! 这有时令人困惑,因为实体类只能部分相关(例如,当连接多个表时)。

更多信息在这里

暂无
暂无

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

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