Spu*_*ick 18 java entity-relationship jpa jta jpa-2.0
我与以下实体类具有双向一对多关系:
0或1个客户< - > 0个或更多产品订单
当持久化客户端实体时,我也希望持久化相关的产品订单实体(因为它们的"父"客户端的外键可能已被更新).
当然,所有必需的CASCADE选项都在客户端设置.但是,如果在引用现有产品订单时第一次持久保留新创建的客户端,则不起作用,如下所示:
我尝试了几个apporaches,但没有一个显示预期的结果.请参阅下面的结果.我在这里阅读了所有相关问题,但他们没有帮助我.我在GlassFish 3.1.2上的Apache Derby(JavaDB)内存数据库中使用EclipseLink 2.3.0,纯JPA 2.0 Annotations和JTA作为事务类型.实体关系由JSF GUI管理.对象级关系管理工作(除了持久),我用JUnit测试测试它.
方法1)"默认"(基于NetBeans类模板)
客户:
@Entity
public class Client implements Serializable, ParentEntity {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(mappedBy = "client", cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH},
fetch= FetchType.LAZY)
private List<ProductOrder> orders = new ArrayList<>();
// other fields, getters and setters
}
Run Code Online (Sandbox Code Playgroud)
ProductOrder:
@Entity
public class ProductOrder implements Serializable, ChildEntity {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne // owning side
private Client client;
// other fields, getters and setters
}
Run Code Online (Sandbox Code Playgroud)
通用持久性外观:
// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
getEntityManager().persist(entity);
}
// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
getEntityManager().merge(entity);
}
Run Code Online (Sandbox Code Playgroud)
结果:
create()立即抛出此异常:
警告:在EJB ClientFacade方法上调用期间发生系统异常public void javaee6test.beans.AbstractFacade.create(java.lang.Object)javax.ejb.EJBException:Transaction aborted ...
引起:javax.transaction.RollbackException:标记为回滚的事务....
引发者:异常[EclipseLink-4002](Eclipse Persistence Services - 2.3.0.v20110604-r9504):org.eclipse.persistence.exceptions.DatabaseException内部异常:java.sql.SQLIntegrityConstraintViolationException:状态已中止,因为它会已在'PRODUCTORDER'中定义的'SQL120513133540930'标识的唯一或主键约束或唯一索引中导致重复键值.错误代码:-1调用:INSERT INTO PRODUCTORDER(ID,CLIENT_ID)VALUES(?,?)bind => [2个参数绑定]查询:InsertObjectQuery(javaee6test.model.ProductOrder [id = 1])...
引起:java.sql.SQLIntegrityConstraintViolationException:语句已中止,因为它会在唯一或主键约束或由'PRO-DUCTORDER'上定义的'SQL120513133540930'标识的唯一索引中导致重复键值....
由以下原因引起:org.apache.derby.client.am.SqlException:语句被中止,因为它会在唯一或主键约束或由'SQL120513133540930'标识的唯一索引中导致重复键值生产方-DER".
我不明白这个例外.edit()工作正常.但我想在创建时向客户添加产品订单,因此这是不够的.
方法2)仅合并()
对通用持久性外观的更改:
// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
getEntityManager().merge(entity);
}
// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
getEntityManager().merge(entity);
}
Run Code Online (Sandbox Code Playgroud)
结果:
在create()上,EclipseLink Logging输出显示:
好的:插入客户端(ID,NAME,ADDRESS_ID)VALUES(?,?,?)bind => [3个参数绑定]
但产品订单表上没有"更新".因此,这种关系没有建立.同样,另一方面,edit()工作正常.
Apporach 3)两种实体类型的Id GenerationType.IDENTITY
客户端和产品订单类的更改:
...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
Run Code Online (Sandbox Code Playgroud)
结果:
在create()上,EclipseLink Logging输出显示:
好的:INSERT INTO CLIENT(NAME,ADDRESS_ID)VALUES(?,?)bind => [2个参数绑定]
罚款:值IDENTITY_VAL_LOCAL()
好的:INSERT INTO PRODUCTORDER(ORDERDATE,CLIENT_ID)VALUES(?,?)bind => [2个参数绑定]
罚款:值IDENTITY_VAL_LOCAL()
因此,不是建立与添加到客户列表的产品订单的关系,而是创建并保持(!)新的产品订单实体,并建立与该实体的关系.同样在这里,edit()工作正常.
Apporach 4)方法(2)和(3)相结合
结果:与方法(2)相同.
我的问题是:有没有办法实现上述情景?如何才能成就?我想继续使用JPA(没有特定于供应商的解决方案).
嗨,我今天遇到了同样的问题,我通过这封电子邮件询问openJPA邮件列表:
你好.我在同一实体中插入和更新引用时遇到问题.
我试图插入一个新对象(检查),该对象具有对另一个对象(Person)的引用,同时我想更新Person对象的属性(birthDate).尽管我将CascadeType设置为ALL,但更新从未发生过.这种方法的唯一方法是执行持久化,然后进行合并操作.这是正常的吗?我需要改变什么吗?
我不喜欢在Person对象中使用merge进行"手动更新"的想法,因为我不知道用户想要更新多少个对象(Exam的子对象).
实体:
public class Exam{
@ManyToOne(cascade= CascadeType.ALL)
@JoinColumn(name = "person_id")
public Person person;
......
}
public class Person{
private Date birthDate;
@OneToMany(mappedBy = "person")
private List<Exam> exams
.......
}
public class SomeClass{
public void someMethod(){
exam = new Exam()
person.setBirthDate(new Date());
exam.setPerson(person);
someEJB.saveExam(exam);
}
}
public class someEJB(){
public void saveExam(Exam exam){
ejbContext.getUserTransaction().begin();
em.persist(exam);
//THIS WORKS
em.merge(exam.getPerson());
ejbContext.getUserTransaction().commit();
}
}
Run Code Online (Sandbox Code Playgroud)
我是否必须为每个子对象使用MERGE方法?
答案是这样的:
看起来您的问题是考试是新的,但是人员存在并且当级联持久操作时现有实体被忽略.我相信这是按预期工作的.
只要您的关系设置为CascadeType.ALL,您就可以随时更改您的em.persist(考试); 到em.merge(考试); 这将考虑坚持新的考试,它也会将合并呼叫级联到该人.
谢谢,里克
我希望这可以帮到你.
在您的 ProductOrder 实体中使用 @Joincolumn 注释,请参见下文。
@Entity
public class ProductOrder implements Serializable, ChildEntity {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne // owning side
@JoinColumn(name = "PRODUCTORDER_ID", referencedColumnName = "ID")
//write your database column names into name and referencedColumnName attributes.
private Client client;
// other fields, getters and setters
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
35600 次 |
| 最近记录: |