使用 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
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";
Map<String, Object> result = jdbcTemplate.queryForMap(sqlQuery);
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();
}
我想使用数据库中的本机查询来检索给定记录的更多数据,并且我正在寻找有关如何声明容纳此数据的字段的解决方案。
如果我不注释该字段,在更新期间 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
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";
Map<String, Object> result = jdbcTemplate.queryForMap(sqlQuery);
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();
}