【问题标题】: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,但它不会按照代码发生。

  1. 持久性提供程序 (PP) - EclipseLink。
  2. 为什么 PP 忽略重复仍然存在?
  3. PP 在什么情况下选择抛出异常?

编辑:

Product.java

注意:

  1. 为简洁起见,排除了 getter 和 setter(适用于所有字段)和 toString()。
  2. 我已尽力按照指南格式化代码,但它没有发生,请多多包涵。

@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。

标签: jpa exception eclipselink persist


【解决方案1】:

根据JPA Spec

  1. 如果 X 是一个新实体,它将成为托管实体。实体 X 将在或 在事务提交之前或作为刷新操作的结果。

  2. 如果 X 是预先存在的托管实体,则持久操作将忽略它 (...)

  3. 如果 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&lt;Product&gt; 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”部分(参见答案中的链接)有解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-22
  • 1970-01-01
  • 2010-12-09
  • 2021-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多