如何透過Hibernate/JPA的SqlResultSetMapping生成需要資料的DTO?

banq發表於2019-02-11

獲取比你實際所需要的更多資料並不好,此外,當您不打算修改實體時,獲取實體(透過在持久化上下文中加入的方式獲取實體)是最常見的錯誤之一,它隱含效能損失。
因此,使用DTO可允許我們僅提取所需的資料。在這個應用程式中,我們依賴  @SqlResultSetMapping和EntityManager來實現。

假設三個實體TopCategory和MiddleCategory是一對多關係,MiddleCategory和BottomCategory 是一對多關係,現在需要分別從TopCategory、MiddleCategory和BottomCategory 中提取各自的namet、namem和nameb欄位組成CategoryDto:
TopCategory程式碼如下,我們使用了註釋@SqlResultSetMapping構造獲得CategoryDto的欄位,TopCategory是父級根實體(DDD聚合中的實體根)。


@SqlResultSetMapping(name = "CategoryDtoMapping",
        classes = {
            @ConstructorResult(
                    targetClass = CategoryDto.class,
                    columns = {
                        @ColumnResult(name = "namet"),
                        @ColumnResult(name = "namem"),
                        @ColumnResult(name = "nameb")
                    }
            )}
)
@Entity
@Table(name = "top_category")
public class TopCategory implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL,
            mappedBy = "topCategory", orphanRemoval = true)
    private List<MiddleCategory> middleCategories = new ArrayList<>();

    // helper methods
    public void addMiddleCategory(MiddleCategory middleCategory) {
        middleCategories.add(middleCategory);
        middleCategory.setTopCategory(this);
    }

    public void removeMiddleCategory(MiddleCategory middleCategory) {
        middleCategory.setTopCategory(null);
        middleCategories.remove(middleCategory);
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<MiddleCategory> getMiddleCategories() {
        return middleCategories;
    }

    public void setMiddleCategories(List<MiddleCategory> middleCategories) {
        this.middleCategories = middleCategories;
    }

}



然後實現自己的DAO倉儲,在其中呼叫EntityManager的createNativeQuery,其他方式點選標籤#DTO可看到更多EntityManager呼叫方式。

@Repository
@Transactional
public class Dao<T, ID extends Serializable> implements GenericDao<T, ID> {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public <S extends T> S persist(S entity) {
        
        Objects.requireNonNull(entity, "Cannot persist a null entity");
        
        entityManager.persist(entity);

        return entity;
    }

    @Transactional(readOnly=true)
    public List<CategoryDto> fetchCategories() {
        Query query = entityManager.createNativeQuery(
                "SELECT t.name AS namet, m.name AS namem, b.name AS nameb "
                + "FROM middle_category m "
                + "INNER JOIN top_category t ON t.id=m.top_category_id "
                + "INNER JOIN bottom_category b ON m.id=b.middle_category_id", "CategoryDtoMapping");
        List<CategoryDto> result = query.getResultList();

        return result;
    }

    protected EntityManager getEntityManager() {
        return entityManager;
    }
}


下面是Dao客戶端Service的呼叫方式:

@Service
public class CategoryService {

    private final Dao dao;

    public CategoryService(Dao dao) {
        this.dao = dao;
    }

  
    public List<CategoryDto> fetchCategories() {
        return dao.fetchCategories();
    }
}


原始碼可以在這裡找到  

相關文章