场景:
我有一个包含以下字段的产品表:ID,代码,描述,照片。
我有其他模式的product_stock视图,具有以下字段:prod_code,stok_tot
Misson:我要在Spring Boot
中做的是将产品与他们的总库存一起加入。
规则:
我不能在此任务上使用@Query
,我需要将它们加入到产品Entity Class
中,以便stock
成为产品的某种Transient字段。
我无法更改产品ID为Long
而ProductStock ID为String
的事实,但是我可以改用产品的代码字段,对吗? (怎么样?)
到目前为止,我尝试使用@OneToOne和@JoinColumn来完成这项工作,但是我的REST
给我的股票字段是NULL
。
“ Estoque.java”
@Entity
@Table(name = "VW_ESTOQUE", schema = "ASICAT")
public class Estoque {
@Id
@Column(name = "CD_BEM_SERVICO", unique = true, nullable = false)
private String codigo;
@Column(name = "ESTOQUE")
private Long estoque;
// getters and setters hidden by me
}
“ Produto.java”
@Entity
@NamedEntityGraph(name = "Produto.detail", attributeNodes = @NamedAttributeNode("categorias"))
public class Produto implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
// here is where I get the null values
@Transient
@OneToOne(fetch = FetchType.LAZY)
@JoinTable(name = "VW_ESTOQUE", joinColumns = @JoinColumn(name = "CODIGO", referencedColumnName = "CODIGO"), inverseJoinColumns = @JoinColumn(name = "CD_BEM_SERVICO", referencedColumnName = "CODIGO"))
private Estoque estoque;
private String hash;
@ManyToMany(mappedBy = "produtos", fetch = FetchType.EAGER)
@BatchSize(size = 10)
private List<Categoria> categorias = new ArrayList<>();
// getters and setters hidden by me
}
在我的产品存储库中,我调用FindAll()
最佳答案
您已将Produto.estoque
注释为@Transient
,这意味着它不属于实体的持久状态。管理该实体的实例时,不会写入或读取该字段。那不会达到您的目的。
我可以想象您可能一直在尝试实现两件事:
每次通过Estoque
访问Produto
时,都应该从数据库中加载它,以确保其新鲜度。 JPA不提供此功能,尽管您可能想用Estoque
注释@Cacheable(value = false)
,并在关系的Produto
端指定延迟获取策略。
您要避免持久性提供程序尝试保留对Estoque
的任何更改,因为它由视图而不是可更新的表支持。我们可以解决这个问题。
我的第一个建议是将ASICAT.VW_ESTOQUE
映射为辅助表,而不是完全独立的实体。可能看起来像这样:
@Entity
@SecondaryTable(name = "VW_ESTOQUE", schema = "ASICAT"
pkJoinColumns = {
@PrimaryKeyJoinColumn(name = "CD_BEM_SERVICO",
referencedColumnName = "CODIGO") })
public class Produto implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
@Column(name = "ESTOQUE", table = "VW_ESTOQUE", nullable = true,
insertable = false, updatable = false)
private Long estoque;
// ...
}
您还可以避免为
estoque
属性提供设置器。但是,如果您不能始终依靠
SecondaryTable
视图始终为ESTOQUE
的每一行提供一行,则PRODUTO
方法可能效果不佳,因为检索中很可能涉及内部联接。而且,您不会以这种方式懒惰获取。主要的替代方案或多或少是您在问题中提出的内容:建立单独的Estoque
实体。但是,如果您确实设置了单独的
Estoque
,那么我的处理方式会有所不同。特别,我将使关系双向化,这样我就可以
使
Estoque
实体成为关系所有者。像这样,然后:
@Entity
public class Produto implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String codigo;
private String descricao;
// must not be @Transient:
@OneToOne(fetch = FetchType.LAZY, mappedBy = "produto", cascade = {
CascadeType.REFRESH
})
private Estoque estoque;
// ...
}
@Entity
@Table(name = "VW_ESTOQUE", schema = "ASICAT")
@Cacheable(value = false)
public class Estoque {
@Id
@Column(name = "CD_BEM_SERVICO", nullable = false,
insertable = false, updatable = false)
private String codigo;
@Column(name = "ESTOQUE", insertable = false, updatable = false)
private Long estoque;
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "CD_BEM_SERVICO", referencedColumnName = "CODIGO",
nullable = false, insertable = false, updatable = false, unique = true)
Produto produto;
// getters and setters hidden by me
}
在这种情况下,我将避免为
Estoque
的任何属性提供setter方法,并避免提供任何允许设置初始属性值的构造函数。因此,从一阶近似值来看,实例属性将采用非null值的方式是如果它们由持久性提供程序设置。
此外,由于提到了Oracle,因此如果您将TopLink用作持久性提供程序,则可以考虑将其
@ReadOnly
扩展属性应用于Estoque
实体,以代替或什至在其中一些针对尝试插入的保护措施之外进入或更新视图。