使用 Spring 存储库填充实体中的非列字段

Populating a non-column field in entity using Spring Repository

我想使用数据库中的本机查询来检索给定记录的更多数据,并且我正在寻找有关如何声明容纳此数据的字段的解决方案。

如果我不注释该字段,在更新期间 JPA 认为该字段应该映射到具有相同名称的列(当然 table 中不存在);如果我用 @Transient 注释它,该字段在更新时被忽略,但在 select 上也被忽略(为什么?)。

让我们用一个例子来说明。我有实体 Discount

class Discount {
    @Column(name = "ID")
    private long id;

    @Column(name = "PRODUCT_ID")
    private long productId;

    // private String productName; <-- Uncommented, error on save; with @Transient empty on read
}

当我从数据库中检索折扣时,我也想对 return 整个记录加上来自 Product table 的产品名称使用相同的查询.

使用 Spring 个存储库,我将查询设置如下:

public interface DiscountRepo extends Repository<Discount, Long> {
    @Query(nativeQuery = true, value = "select d.*,p.name as productName from discount d join product p on d.product_id = d.id")
    List<Discount> getAll();
}

我不想在 Discount 实体中使用 属性 之类的 private Product product 的原因是,在 select 上会传输太多数据数据库,而我只需要产品名称。

您可以使用 JdbcTempate 然后将 return 值映射到 Discount.class

  1. String sqlQuery = "select d.ID as id , d.PRODUCT_ID as productId p.name as productName from discount d join product p on d.product_id = d.id";
  2. Map<String, Object> result = jdbcTemplate.queryForMap(sqlQuery);
  3. Discount discount = new ObjectMapper().convertValue(result, Discount.class);

用@Transient 标记一个字段与为Java 序列化进程标记一些瞬态具有相同的效果——它在两个方向上都被完全排除在数据库持久性之外。无论如何,您都不希望将其作为常规 属性 尝试读取,因为这会干扰所有折扣读取操作 - 如果 是您想要的 ,您应该将此字段映射到实体中。

我建议您为此数据操作使用其他一些非实体对象,这样您的业务逻辑就不会混淆或混合此折扣对象数据与填充有其他 JPA 操作的额外 'productName'在折扣上将被排除在外。

就是说,您可以使用 JPA 构造函数查询或构造函数操作让它从数据构建任何 pojo。您所需要的只是对象的构造函数,以接收相同的参数和类型。

@Entity
class Discount {
    @Column(name = "ID")
    private long id;

    @Column(name = "PRODUCT_ID")
    private long productId;

    @Transient
    private String productName;

    public AbstractDomainObject(Long id, Long productId, String productName)  {
      this.id = id;
      this.productId = productId;
      this.productName = productName;
    }
}

然后您可以使用

public interface DiscountRepo extends Repository<Discount, Long> {
    @Query("select new your.package.Discount(d.id, d.productId, p.productName) from discount d join product p on d.productId = p.id"
    List<Discount> getAll();
}

如果您的提供商不支持这样的加入(有些允许),您仍然可以享受产品参考折扣。只需确保将其标记为惰性——在查询中使用它不会强制获取所有数据。只需确保您的提供者支持惰性关系,因为许多需要额外的东西,比如增强 类:

 public interface DiscountRepo extends Repository<Discount, Long> {
    @Query("select new your.package.Discount(d.id, d.productId, p.productName) from discount d join d.product p"
    List<Discount> getAll();
}

或者您仍然可以使用本机查询,但可能希望使用定义结果生成方式的 SqlResultSetMapping 在您的实体中定义它:

@Entity
@SqlResultSetMappings(
    SqlResultSetMapping(
            name = "summary",
            classes = [
                ConstructorResult(
                        targetClass = Discount.class,
                        columns = [
                            ColumnResult(name = "id", type = Long::class),
                            ColumnResult(name = "productId", type = Long::class),
                            ColumnResult(name = "productName", type = String::class)
                        ]
                )
            ]
    )
)
@NamedNativeQueries(
    NamedNativeQuery(name = "Discount.getAll",
            query = "select d.*,p.name as productName from discount d join product p on d.product_id = p.id", resultSetMapping = "summary")
)
class Discount {
..

然后 Spring 应该在您的存储库中执行 getAll 时查找该查询名称:

public interface DiscountRepo extends Repository<Discount, Long> {
    List<Discount> getAll();
}