为什么 JPA 不重复 persist 方法抛出异常?
Why doesn't JPA repeat persist method throw an exception?
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" ) })
public class Product implements Serializable {
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;
}
}
根据 JPA Spec:
If X is a new entity, it becomes managed. The entity X will be entered into the database at or
before transaction commit or as a result of the flush operation.
If X is a preexisting managed entity, it is ignored by the persist operation (...)
If X is a detached object, the EntityExistsException may be thrown when the persist
operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at flush or commit time
当您调用 EntityManager.persist(product)
时,product
成为托管实体 (#1)。如 #2 中所述,将忽略对 EntityManager.persist(product)
的任何后续调用。最后一点仅在您尝试在 detached 实体上调用 persist()
时适用。
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" ) })
public class Product implements Serializable {
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; } }
根据 JPA Spec:
If X is a new entity, it becomes managed. The entity X will be entered into the database at or before transaction commit or as a result of the flush operation.
If X is a preexisting managed entity, it is ignored by the persist operation (...)
If X is a detached object, the EntityExistsException may be thrown when the persist operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at flush or commit time
当您调用 EntityManager.persist(product)
时,product
成为托管实体 (#1)。如 #2 中所述,将忽略对 EntityManager.persist(product)
的任何后续调用。最后一点仅在您尝试在 detached 实体上调用 persist()
时适用。