如何通过ResultTransformer和原生SQL或JPQL生成DTO?


获取超出需要的数据更容易导致性能损失。使用DTO可以让我们只提取所需的数据。在这个应用程序中,我们依赖于Hibernate ResultTransformer和原生SQL生成DTO?

对于不可变的DTO值对象和可变的DTO对象使用不同的方式,不可变的DTO值对象是final字段,因此不能有setter字段:


public class CarDtoNoSetters implements Serializable {

    private static final long serialVersionUID = 1L;

    private final String name;
    private final String color;       

    public CarDtoNoSetters(String name, String color) {
        this.name = name;
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public String getColor() {
        return color;
    }            

    @Override
    public String toString() {
        return "CarDtoNoSetters{" + "name=" + name + ", color=" + color + '}';
    }           
}

可变的DTO对象:


public class CarDtoWithSetters implements Serializable {

    private static final long serialVersionUID = 1L;
    
    private String name;
    private String color;    

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }          

    @Override
    public String toString() {
        return "CarDtoWithSetters{" + "name=" + name + ", color=" + color + '}';
    }        
}

编写自己的DAO类,对于不可变的DTO值对象使用ResultTransformer+AliasToBeanConstructorResultTransformer,可变的使用ResultTransformer+Transformers.aliasToBean():


@Repository
@Transactional
public class Dao<T, ID extends Serializable> implements GenericDao<T, ID> {
    
    @PersistenceContext
    private EntityManager entityManager;

    
    @Transactional(readOnly = true)
    public List<CarDtoNoSetters> fetchCarsNoSetters() {
        Query query = entityManager
                .createNativeQuery("select name, color from car")
                .unwrap(org.hibernate.query.NativeQuery.class)
                .setResultTransformer(
                        new AliasToBeanConstructorResultTransformer(
                                CarDtoNoSetters.class.getConstructors()[0]
                        )
                );
        
        List<CarDtoNoSetters> result = query.getResultList();
        
        return result;
    }
    
    @Transactional(readOnly = true)
    public List<CarDtoWithSetters> fetchCarsWithSetters() {
        Query query = entityManager
                .createNativeQuery(
"select name, color from car")
                .unwrap(org.hibernate.query.NativeQuery.class)
                .setResultTransformer(
                        Transformers.aliasToBean(CarDtoWithSetters.class)
                );        

        List<CarDtoWithSetters> result = query.getResultList();
        
        return result;
    }    
    
    protected EntityManager getEntityManager() {
        return entityManager;
    }
}


使用EntityManager.createNativeQuery()和unwrap(org.hibernate.query.NativeQuery.class)- 从Hibernate 5.2开始,ResultTransformer不推荐使用,但是直到可以使用替换(在Hibernate 6.0中)它可以使用(进一步阅读

使用JPQL和原生SQL在SQL语句上稍微有点不同:

   @Transactional(readOnly = true)
    public List<CarDtoNoSetters> fetchCarsNoSetters() {
        Query query = entityManager
                .createQuery("select c.name as name, c.color as color from Car c")
                .unwrap(org.hibernate.query.Query.class)
                .setResultTransformer(
                        new AliasToBeanConstructorResultTransformer(
                                CarDtoNoSetters.class.getConstructors()[0]
                        )
                );
        
        List<CarDtoNoSetters> result = query.getResultList();
        
        return result;
    }
    
    @Transactional(readOnly = true)
    public List<CarDtoWithSetters> fetchCarsWithSetters() {
        Query query = entityManager
                .createQuery(
"select c.name as name, c.color as color from Car c")
                .unwrap(org.hibernate.query.Query.class)
                .setResultTransformer(
                        Transformers.aliasToBean(CarDtoWithSetters.class)
                );        

        List<CarDtoWithSetters> result = query.getResultList();
        
        return result;
    }    


原生SQL

JPQL源码