This post deals with the 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.
The more proper solution is the usage of the @MappedSuperclass annotation as the following:
SingerExtended.java (the class must be 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 {
}
then DAO class 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):
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).