Spring Boot Hibernate:将左连接查询结果绑定到实体字段
你遇到的核心问题是用@Transient标记字段的方式错误——@Transient的作用是告诉Hibernate该字段不需要持久化到数据库,同时也会忽略查询结果中的对应字段,所以SQL返回的customer_response值根本不会被设置到这个字段上。
下面是几个可行的解决方法:
方法一:用@Column标记非持久化字段
去掉实体中的@Transient和@Fetch注解,改用@Column并设置insertable和updatable为false,这样Hibernate会映射查询结果,但不会在增改操作中处理该字段:
@Column(name = "customer_response", insertable = false, updatable = false) private Integer customerResponse;
注意字段名尽量用Java驼峰命名,Hibernate默认会自动将SQL中的下划线别名(customer_response)映射到驼峰字段(customerResponse),如果映射失败可以手动通过name属性指定对应关系。
方法二:构造函数投影(JPQL方式)
如果不想修改实体字段的注解,可以通过JPQL的构造函数投影来创建实体。首先在CustomerFormEntity中添加包含所有必要字段(包括customerResponse)的构造函数:
public CustomerFormEntity(Integer id, String formName, ..., Integer customerResponse) { this.id = id; this.formName = formName; // 初始化其他原有字段 this.customerResponse = customerResponse; }
然后将查询改为JPQL(去掉nativeQuery=true),通过new关键字调用构造函数:
@Query( value = "SELECT new com.yourpackage.CustomerFormEntity(c.id, c.formName, ..., r.id) " + "FROM CustomerFormEntity c " + "LEFT JOIN CustomerResponsesEntity r " + "ON c.id = r.formId " + "AND r.customerId = :customerId " + "WHERE c.id = :formId " ) Optional<CustomerFormEntity> findById(@Param("formId") int formId, @Param("customerId") int customerId);
这种方式无需修改实体原有注解,直接通过构造函数注入查询结果。
方法三:用@SqlResultSetMapping映射原生SQL结果
如果必须使用原生SQL,可以通过@SqlResultSetMapping定义结果映射规则。首先在CustomerFormEntity类上添加映射配置:
@Entity @Table(name = "customer_form", schema = "postgres") @SqlResultSetMapping( name = "CustomerFormWithResponse", entities = { @EntityResult( entityClass = CustomerFormEntity.class, fields = { @FieldResult(name = "id", column = "id"), // 依次映射customer_form表的所有字段,例如: // @FieldResult(name = "formName", column = "form_name"), @FieldResult(name = "customerResponse", column = "customer_response") } ) } ) public class CustomerFormEntity { // 原有字段... private Integer customerResponse; // 不要加@Transient // getter和setter方法 }
然后在@Query注解中指定这个映射:
@Query( value = "SELECT c.*, r.id AS customer_response " + "FROM postgres.customer_form AS c " + "LEFT JOIN postgres.customer_responses AS r " + "ON c.id = r.form_id " + "AND r.customer_id = :customerId " + "WHERE c.id = :formId " + "LIMIT 1", nativeQuery = true, resultSetMapping = "CustomerFormWithResponse" ) Optional<CustomerFormEntity> findById(@Param("formId") int formId, @Param("customerId") int customerId);
这样Hibernate就会按照定义的规则,将原生SQL的查询结果填充到实体中。
内容的提问来源于stack exchange,提问作者Hedin Noviybog

