Java JPA-EclipseLink在合并实体时对关系发出额外的SELECT请求
我对使用merge方法更新实体时出现的“奇怪”行为提出了一个问题 我有一个实体“Personne”,它有两个关系Java JPA-EclipseLink在合并实体时对关系发出额外的SELECT请求,java,jpa,eclipselink,fetch,relationships,Java,Jpa,Eclipselink,Fetch,Relationships,我对使用merge方法更新实体时出现的“奇怪”行为提出了一个问题 我有一个实体“Personne”,它有两个关系(fetch=LAZY)。我有一个HTML表单,用于仅修改该实体的字段。当使用merge方法保存该实体时,我看到对所有关系的SELECT请求 我不明白为什么要进行这些选择,我想避免这些选择,因为这种行为加载的数据可能是“巨大的” 这就是我的实体: @Entity @Table(name="personne") @NamedQuery(name="Personne.findAll", q
(fetch=LAZY)
。我有一个HTML表单,用于仅修改该实体的字段。当使用merge方法保存该实体时,我看到对所有关系的SELECT请求
我不明白为什么要进行这些选择,我想避免这些选择,因为这种行为加载的数据可能是“巨大的”
这就是我的实体:
@Entity
@Table(name="personne")
@NamedQuery(name="Personne.findAll", query="SELECT p FROM Personne p")
public class Personne implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(unique=true, nullable=false)
private Integer id;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="date_naissance")
private Date dateNaissance;
@Column(length=75)
private String denomination;
@Column(length=75)
private String nom;
@Column(precision=10, scale=2)
private BigDecimal poids;
@Column(length=75)
private String prenom;
@Column(precision=10, scale=2)
private BigDecimal taille;
//bi-directional many-to-one association to Contact
@OneToMany(mappedBy="personne", fetch = FetchType.LAZY)
private List<Contact> contacts;
//bi-directional many-to-one association to Log
@OneToMany(mappedBy="personne", fetch = FetchType.LAZY)
private List<Logs> logs;
//bi-directional many-to-one association to PersonneHasAdresse
@OneToMany(mappedBy="personne", fetch = FetchType.LAZY)
private List<PersonneHasAdresse> personneHasAdresses;
//bi-directional many-to-one association to PersonneHasPersonne
@OneToMany(mappedBy="parent", fetch = FetchType.LAZY)
private List<PersonneHasPersonne> personneHasPersonnesParent;
//bi-directional many-to-one association to PersonneHasPersonne
@OneToMany(mappedBy="child", fetch = FetchType.LAZY)
private List<PersonneHasPersonne> personneHasPersonnesChild;
public Personne() {
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getDateNaissance() {
return this.dateNaissance;
}
public void setDateNaissance(Date dateNaissance) {
this.dateNaissance = dateNaissance;
}
public String getDenomination() {
return this.denomination;
}
public void setDenomination(String denomination) {
this.denomination = denomination;
}
public String getNom() {
return this.nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public BigDecimal getPoids() {
return this.poids;
}
public void setPoids(BigDecimal poids) {
this.poids = poids;
}
public String getPrenom() {
return this.prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public BigDecimal getTaille() {
return this.taille;
}
public void setTaille(BigDecimal taille) {
this.taille = taille;
}
@JsonIgnore
public List<Contact> getContacts() {
return this.contacts;
}
public void setContacts(List<Contact> contacts) {
this.contacts = contacts;
}
public Contact addContact(Contact contact) {
this.getContacts().add(contact);
contact.setPersonne(this);
return contact;
}
public Contact removeContact(Contact contact) {
this.getContacts().remove(contact);
contact.setPersonne(null);
return contact;
}
@JsonIgnore
public List<Logs> getLogs() {
return this.logs;
}
public void setLogs(List<Logs> logs) {
this.logs = logs;
}
public Logs addLog(Logs log) {
this.getLogs().add(log);
log.setPersonne(this);
return log;
}
public Logs removeLog(Logs log) {
this.getLogs().remove(log);
log.setPersonne(null);
return log;
}
@JsonIgnore
public List<PersonneHasAdresse> getPersonneHasAdresses() {
return this.personneHasAdresses;
}
public void setPersonneHasAdresses(List<PersonneHasAdresse> personneHasAdresses) {
this.personneHasAdresses = personneHasAdresses;
}
@JsonIgnore
public PersonneHasAdresse addPersonneHasAdress(PersonneHasAdresse personneHasAdress) {
this.getPersonneHasAdresses().add(personneHasAdress);
personneHasAdress.setPersonne(this);
return personneHasAdress;
}
public PersonneHasAdresse removePersonneHasAdress(PersonneHasAdresse personneHasAdress) {
this.getPersonneHasAdresses().remove(personneHasAdress);
personneHasAdress.setPersonne(null);
return personneHasAdress;
}
@JsonIgnore
public List<PersonneHasPersonne> getPersonneHasPersonnesParent() {
return this.personneHasPersonnesParent;
}
public void setPersonneHasPersonnesParent(List<PersonneHasPersonne> personneHasPersonnesParent) {
this.personneHasPersonnesParent = personneHasPersonnesParent;
}
public PersonneHasPersonne addPersonneHasPersonnesParent(PersonneHasPersonne personneHasPersonnesParent) {
this.getPersonneHasPersonnesParent().add(personneHasPersonnesParent);
personneHasPersonnesParent.setParent(this);
return personneHasPersonnesParent;
}
public PersonneHasPersonne removePersonneHasPersonnesParent(PersonneHasPersonne personneHasPersonnesParent) {
this.getPersonneHasPersonnesParent().remove(personneHasPersonnesParent);
personneHasPersonnesParent.setParent(null);
return personneHasPersonnesParent;
}
@JsonIgnore
public List<PersonneHasPersonne> getPersonneHasPersonnesChild() {
return this.personneHasPersonnesChild;
}
public void setPersonneHasPersonnesChild(List<PersonneHasPersonne> personneHasPersonnesChild) {
this.personneHasPersonnesChild = personneHasPersonnesChild;
}
public PersonneHasPersonne addPersonneHasPersonnesChild(PersonneHasPersonne personneHasPersonnesChild) {
this.getPersonneHasPersonnesChild().add(personneHasPersonnesChild);
personneHasPersonnesChild.setChild(this);
return personneHasPersonnesChild;
}
public PersonneHasPersonne removePersonneHasPersonnesChild(PersonneHasPersonne personneHasPersonnesChild) {
this.getPersonneHasPersonnesChild().remove(personneHasPersonnesChild);
personneHasPersonnesChild.setChild(null);
return personneHasPersonnesChild;
}
在UPDATE语句之前,会选择联系人和日志。
有人知道为什么吗?这是正常的行为吗?如果是,如何避免
非常感谢您的帮助
编辑1: 所有层(从REST到DAO)的完整代码:
@Path(“人员”)
@产生({MediaType.APPLICATION_JSON})
@使用({MediaType.APPLICATION_JSON})
公共类人员名单{
私人人员服务人员服务=新人员服务();
...
@放
@路径(“{id}”)
公共响应更新(@PathParam(“id”)整数id,Personne Personne){
if(personne==null | |!id.equals(personne.getId()){
返回Response.status(status.BAD_REQUEST.build();
}
this.personeservices.merge(personne);
返回Response.noContent().build();
}
@职位
公众响应创建(persone persone){
Personne createdPersonne=this.personeServices.persist(Personne);
返回Response.created(URI.create(“persones/”+createdPersonne.getId()).build();
}
...
}
公共类PersonneServices扩展了AbstractBasicServices{
公共人员服务(){
super(newpersonedao(),Personne.class,“poi.model”);
}
}
公共类人员DAO扩展了AbstractBasicDao{
}
公共抽象类AbstractBasicServices{
私渡道;
私人课堂;
专用字符串存储单元;
公共AbstractBasicServices(AbstractBasicDao、类clazz、字符串持久化单元){
this.dao=dao;
this.clazz=clazz;
this.persistUnit=persistUnit;
}
公共电子商务(E obj){
EntityManager em=persistenceManager.getInstance().getEm(this.persistUnit);
E res=null;
试一试{
EntityTransaction t=em.getTransaction();
试一试{
t、 begin();
res=this.dao.persist(obj,em);
t、 提交();
}最后{
if(t.isActive()){
t、 回滚();
}
}
}最后{
em.close();
}
返回res;
}
//合并方法是第一篇文章中的方法。
}
公共抽象类AbstractBasicDao{
公共T持久化(T对象、实体管理器em){
em.persist(obj);
em.flush();
em.refresh(obj);
返回obj;
}
公共T合并(T对象、实体管理器em){
返回em.merge(obj);
}
}
编辑2: 回顾之后,我(在persistence.xml中)将“共享缓存模式”改为“无”。很明显,实体经理需要重新加载关系(愚蠢的我…)。 所以我把它设为“默认” 我进行了以下测试(每次测试我都会重新启动服务器):
- (测试1-1)创建新实体(持久化):插入+选择关系
- (测试1-2)更新上一个实体(合并):更新(无选择)
- (测试2-1)更新实体(合并):更新+选择关系
- (测试2-2)更新同一实体(合并):更新(无选择)
编辑3: 我发布了整个人事实体,现在我看到了我的错误。我使用@JsonIgnore来避免在查询显示人员属性列表时加载关系,因为关系是编组的。但是,解组并没有使间接列表后退。我删除了注释 我创建了一个包装器,它只包含JAX-RS层所需的属性,并在需要更新时将字段更新到相应的实体,并且没有更多的选择
谢谢大家抽出时间。我无法复制此内容。
在调用
merge()
之前,能否验证以下表达式是否返回false:
((org.eclipse.persistence.indirection.IndirectContainer)obj.getContacts()).isInstantiated()
(假设您有一个名为getContacts()
的getter,它只返回实体类中的字段contacts
)
如果它返回false
,那么这将是一个“奇怪”的行为,需要进一步调查
如果它返回true
(很可能是这种情况),那么这个延迟加载的集合在之前已经被访问过,因此EclipseLink需要检查数据库中是否有更改。在这种情况下,请事先检查哪些代码访问该集合(使用断点来解决此问题)。合并前是否访问了联系人和日志列表?检查它们是否在调试器中填充-如果是,那么JPA必须检查您在合并时没有修改列表,这需要数据库命中。你好,Chris,谢谢您的回复。否,在合并之前无法访问这些列表。我将断点放在getter/setter上,使其为sur,但没有命中。字段也为空。您在什么环境中运行?您是否在任何地方访问关系,例如在toString方法中?我正在Tomcat7下运行应用程序。webapp由AngularJS制作,其余的Webservices由JAX-RS(+Jackson)制作。bean中没有toString()方法。我还更新了帖子。你是如何获得合并实体的?看起来您可能没有在JAX-RS中映射该集合,因此它没有被传递回进行合并-JPA需要将这个空列表合并到一个非迁移列表中
@Path("personnes")
@Produces({ MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_JSON })
public class PersonnesRest {
private PersonneServices personneServices = new PersonneServices();
...
@PUT
@Path("{id}")
public Response update(@PathParam("id") Integer id, Personne personne) {
if (personne == null || !id.equals(personne.getId())) {
return Response.status(Status.BAD_REQUEST).build();
}
this.personneServices.merge(personne);
return Response.noContent().build();
}
@POST
public Response create(Personne personne) {
Personne createdPersonne = this.personneServices.persist(personne);
return Response.created(URI.create("personnes/" + createdPersonne.getId())).build();
}
...
}
public class PersonneServices extends AbstractBasicServices<Personne> {
public PersonneServices() {
super(new PersonneDao(), Personne.class, "poi.model");
}
}
public class PersonneDao extends AbstractBasicDao<Personne> {
}
public abstract class AbstractBasicServices<E> {
private AbstractBasicDao<E> dao;
private Class<E> clazz;
private String persistUnit;
public AbstractBasicServices(AbstractBasicDao<E> dao, Class<E> clazz, String persistUnit) {
this.dao = dao;
this.clazz = clazz;
this.persistUnit = persistUnit;
}
public E persist(E obj) {
EntityManager em = PersitenceManager.getInstance().getEm(this.persistUnit);
E res = null;
try {
EntityTransaction t = em.getTransaction();
try {
t.begin();
res = this.dao.persist(obj, em);
t.commit();
} finally {
if (t.isActive()) {
t.rollback();
}
}
} finally {
em.close();
}
return res;
}
// The merge method is the one in first post.
}
public abstract class AbstractBasicDao<T> {
public T persist(T obj, EntityManager em) {
em.persist(obj);
em.flush();
em.refresh(obj);
return obj;
}
public T merge(T obj, EntityManager em) {
return em.merge(obj);
}
}
((org.eclipse.persistence.indirection.IndirectContainer)obj.getContacts()).isInstantiated()