如何用Quarkus Hibernate Reactive Panache创建SQL视图
在Quarkus Hibernate Reactive Panache中映射SQL视图
你之前的写法问题在于误用了@Entity注解(该注解用于映射数据库物理表),而SQL视图是只读逻辑结构,需要搭配Hibernate的@Immutable和@Subselect来实现,同时必须指定主键(Hibernate要求所有实体都具备唯一标识)。下面提供两种可行的解决方案:
方案1:逻辑视图(无需手动创建数据库视图)
这种方式不需要在数据库中提前创建视图,Hibernate会在查询时自动执行@Subselect中的SQL语句生成视图数据:
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase; import jakarta.persistence.Column; import jakarta.persistence.Id; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Subselect; import org.hibernate.annotations.Synchronize; @Immutable // 标记为只读实体,禁止生成更新/插入/删除语句 @Subselect("SELECT i.id as invoice_id, i.appointment_ids FROM invoice i") @Synchronize("invoice") // 可选:当invoice表数据变更时自动刷新视图缓存 public class BilledAppointments extends PanacheEntityBase { @Id // 必须指定主键,Hibernate要求实体有唯一标识 @Column(name = "invoice_id") public Long invoiceId; @Column(name = "appointment_ids") public String appointmentsIds; }
核心要点说明:
@Immutable:明确告知Hibernate该实体为只读,完全适配视图的不可修改特性。@Subselect:定义视图的查询逻辑,相当于动态生成视图数据,无需数据库中存在物理视图。@Synchronize:关联源表invoice,当源表数据更新时,自动清除视图实体的缓存,确保读取最新数据。@Id:必须配置,这里用invoice_id作为主键(保证每行唯一),否则Hibernate会抛出实体无主键的异常。
方案2:映射物理数据库视图(提前创建视图)
如果需要在数据库层面创建物理视图(比如需要复杂SQL优化、权限控制),可以先手动创建视图,再映射实体:
1. 创建物理视图(可通过Quarkus的import.sql或Flyway/Liquibase脚本执行)
CREATE VIEW billed_appointments AS SELECT i.id as invoice_id, i.appointment_ids FROM invoice i;
2. 实体映射代码
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase; import jakarta.persistence.Column; import jakarta.persistence.Id; import jakarta.persistence.Entity; import jakarta.persistence.Table; import org.hibernate.annotations.Immutable; @Entity @Immutable @Table(name = "billed_appointments") // 直接映射到已创建的物理视图 public class BilledAppointments extends PanacheEntityBase { @Id @Column(name = "invoice_id") public Long invoiceId; @Column(name = "appointment_ids") public String appointmentsIds; }
使用方式
两种方案的使用逻辑和普通Panache实体完全一致,例如:
public Uni<List<BilledAppointments>> getAllBilledAppointments() { return BilledAppointments.listAll(); } public Uni<BilledAppointments> findByInvoiceId(Long id) { return BilledAppointments.findById(id); }
内容的提问来源于stack exchange,提问作者Carla C
相关产品推荐
相关产品推荐

