为啥 JPA 重复持久方法不抛出异常?
Posted
技术标签:
【中文标题】为啥 JPA 重复持久方法不抛出异常?【英文标题】:Why doesn't JPA repeat persist method throw an exception?为什么 JPA 重复持久方法不抛出异常? 【发布时间】:2019-11-15 18:57:54 【问题描述】:Product product = new Product();
product.setName( "foo" );
product.setPrice(BigDecimal.valueOf( 4.5 ) );
pm.create( product ); // pm delegates calls to an entity manager object using persist method and tx is immediately commited after the call
List<Product> products = pm.findAllProducts();
products.stream().forEach( System.out::println ); // New product is listed too.
pm.create( product ); // Causes no exception! But, as per API, it should.
products = pm.findAllProducts(); // Fetch successful
products.stream().forEach( System.out::println ); // No difference from first print.
根据persistence API,如果实体已经存在,则坚持(从pm.create
调用)抛出EntityExistsException
,但它不会按照代码发生。
-
持久性提供程序 (PP) - EclipseLink。
为什么 PP 忽略重复仍然存在?
PP 在什么情况下选择抛出异常?
编辑:
Product.java
注意:
-
为简洁起见,排除了 getter 和 setter(适用于所有字段)和 toString()。
我已尽力按照指南格式化代码,但它没有发生,请多多包涵。
@Entity @Table(name = "PRODUCTS") @XmlRootElement @NamedQueries( @NamedQuery(name = "Product.findAll", query = "SELECT p FROM Product p") , @NamedQuery(name = "Product.findById", query = "SELECT p FROM Product p WHERE p.id = :id") , @NamedQuery(name = "Product.findByName", query = "SELECT p FROM Product p WHERE p.name like :name") , @NamedQuery(name = "Product.findByPrice", query = "SELECT p FROM Product p WHERE p.price = :price") , @NamedQuery(name = "Product.findByBestBefore", query = "SELECT p FROM Product p WHERE p.bestBefore = :bestBefore") , @NamedQuery(name = "Product.findByVersion", query = "SELECT p FROM Product p WHERE p.version = :version") , @NamedQuery(name = "Product.findTotal", query = "SELECT count(p.id), sum(p.price) FROM Product p WHERE p.id in :ids" )
公共类产品实现可序列化
private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @SequenceGenerator( name="pidGen", sequenceName="PID_SEQ", allocationSize=1 ) @GeneratedValue( strategy=SEQUENCE, generator="pidGen" ) private Integer id; @Basic(optional = false) @NotNull @Size(min = 3, max = 40, message="prod.name") private String name; // @Max(value=?) @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation @Basic(optional = false) @NotNull @Max( value=1000, message="prod.price.max") @Min( value=1, message="prod.price.min") private BigDecimal price; @Column(name = "BEST_BEFORE") @Temporal(TemporalType.DATE) //private Date bestBefore; private LocalDate bestBefore; @Version private Integer version; public Product() public Product(Integer id) this.id = id; public Product(Integer id, String name, BigDecimal price) this.id = id; this.name = name; this.price = price; @Override public int hashCode() int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; @Override public boolean equals(Object object) // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Product)) return false; Product other = (Product) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) return false; return true;
【问题讨论】:
pm
是什么的一个实例?
显示产品实体
@AlanHay pm
是 POJO - 助手/实用程序/代表。 create
方法:public void create( @Valid Product product ) em.getTransaction().begin(); em.persist( product ); em.getTransaction().commit();
@MaciejKowalski 更新了 OP。
【参考方案1】:
根据JPA Spec:
如果 X 是一个新实体,它将成为托管实体。实体 X 将在或 在事务提交之前或作为刷新操作的结果。
如果 X 是预先存在的托管实体,则持久操作将忽略它 (...)
如果 X 是一个分离的对象,在持久化时可能会抛出 EntityExistsException 操作被调用,或者可能在刷新或提交时抛出 EntityExistsException 或另一个 PersistenceException
当您调用 EntityManager.persist(product)
时,product
成为托管实体 (#1)。任何后续对EntityManager.persist(product)
的调用都将被忽略,如#2 中所述。最后一点仅在您尝试在 分离的 实体上调用 persist()
时适用。
【讨论】:
@MaciejKowalski 啊!现在我看到 OP 正在启动并在pm.create()
内提交事务。是的,你完全正确
@MaciejKowalski 再三考虑,这是一个应用程序管理的 EM,因此根据定义,它具有扩展范围。这是否意味着product
会保留在持久性上下文中,直到 EM 被清除或关闭?
我们不知道。也许你是对的。但这隐藏在 create 方法中
#2 正在根据情况采取行动。感兴趣的用户: - 如果一个新实体被持久化和更新,那么如果在其上调用persist
方法,持久化提供者将更新它。代码(与OP比较):Product product = new Product(); product.setName( "foo1" ); product.setPrice(BigDecimal.valueOf( 4.5 ) ); pm.create( product ); List<Product> products = pm.findAllProducts(); products.stream().forEach( System.out::println ); product.setName( "foo2" ); pm.create( product ); products = pm.findAllProducts(); products.stream().forEach( System.out::println );
规范的“3.2.2Persisting an Entity Instance”部分(参见答案中的链接)有解释。以上是关于为啥 JPA 重复持久方法不抛出异常?的主要内容,如果未能解决你的问题,请参考以下文章
为啥设置 DataSource 时 ComboBox 不抛出异常?