2012-04-22 47 views
3

我想在一个非常简单的Hibernate示例中填充一些实体对象。我的数据库包含两个表,“部门”(Id,Name)和“Employees”(Id,DepartmentsId,FirstName,LastName)。我的SQL查询只是员工与部门的左连接。如何在获取双向集合时避免Hibernate中的无限循环?

我已经设置了Hibernate documentation中指定的注释,但每当我尝试序列化实体时,Hibernate会进入无限循环并最终抛出StackOverFlowError异常。有人回答我的另一个问题是能够确定发生堆栈溢出,因为“Department”对象包含一组“Employee”对象,每个对象都包含一个“Department”对象,其中包含一组Employee对象等。等

这种类型的双向关系应该是合法的,根据上面链接的文档(Department的“mappedBy”参数应该提示Hibernate在;我也尝试使用“joinColumn”注释被注释在下面的代码中)和其他我已经读过的东西指出休眠是假设足够聪明,在这种情况下不会陷入无限循环,但它不适用于我的示例。如果我通过从Employee类中移除Department对象将双向关系更改为单向关系,那么一切正常,但显然这会导致很多功能的丢失。

我也尝试了前面的旧XML映射文件的注释,并为子表设置“反向”参数,但它仍然会产生相同的问题。我如何才能使这种双向关系按照它应该工作的方式工作?

部:

package com.test.model; 

import java.util.HashSet; 
import java.util.Set; 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.GeneratedValue; 
import javax.persistence.JoinTable; 

import static javax.persistence.GenerationType.IDENTITY; 
import javax.persistence.Id; 
import javax.persistence.OneToMany; 
import javax.persistence.Table; 
import javax.persistence.JoinColumn; 

import org.hibernate.Hibernate; 
import org.hibernate.proxy.HibernateProxy; 

@Entity 
@Table(name="Departments" 
,catalog="test" 
) 
public class Department implements java.io.Serializable { 

private Integer id; 
private String name; 
public Set<Employee> employees = new HashSet<Employee>(0); 

public Department() { 
} 


public Department(String name) { 
    this.name = name; 
} 
public Department(String name, Set employees) { 
    this.name = name; 
    this.employees = employees; 
} 

@Id @GeneratedValue(strategy=IDENTITY) 


@Column(name="Id", unique=true, nullable=false) 
public Integer getId() { 
    return this.id; 
} 

public void setId(Integer id) { 
    this.id = id; 
} 


@Column(name="Name", nullable=false) 
public String getName() { 
    return this.name; 
} 

public void setName(String name) { 
    this.name = name; 
} 

@OneToMany(fetch=FetchType.LAZY, mappedBy="department") 
/*@OneToMany 
@JoinColumn(name="DepartmentsId")*/ 
public Set<Employee> getEmployees() { 
    return this.employees; 
} 

public void setEmployees(Set employees) { 
    this.employees = employees; 
} 
} 

员工:

package com.test.model; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.GeneratedValue; 
import javax.persistence.JoinTable; 

import static javax.persistence.GenerationType.IDENTITY; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.ManyToOne; 
import javax.persistence.Table; 

@Entity 
@Table(name="Employees" 
,catalog="test" 
) 
public class Employee implements java.io.Serializable { 


private Integer id; 
private Department department; 
private String firstName; 
private String lastName; 

public Employee() { 
} 

public Employee(Department department, String firstName, String lastName) { 
    this.department = department; 
    this.firstName = firstName; 
    this.lastName = lastName; 
} 

@Id @GeneratedValue(strategy=IDENTITY) 


@Column(name="Id", unique=true, nullable=false) 
public Integer getId() { 
    return this.id; 
} 

public void setId(Integer id) { 
    this.id = id; 
} 

@ManyToOne 
@JoinColumn(name="DepartmentsId", nullable=false, insertable=false, updatable=false) 
public Department getDepartment() { 
    return this.department; 
} 

public void setDepartment(Department department) { 
    this.department = department; 
} 


@Column(name="FirstName", nullable=false) 
public String getFirstName() { 
    return this.firstName; 
} 

public void setFirstName(String firstName) { 
    this.firstName = firstName; 
} 


@Column(name="LastName", nullable=false) 
public String getLastName() { 
    return this.lastName; 
} 

public void setLastName(String lastName) { 
    this.lastName = lastName; 
} 
} 

部经理(包含HQL查询):

package com.test.controller; 

import java.util.Collections; 
import java.util.List; 

import java.util.Iterator; 

import org.hibernate.Criteria; 
import org.hibernate.Hibernate; 
import org.hibernate.HibernateException; 
import org.hibernate.Query; 
import org.hibernate.Session; 

import com.test.model.Department; 
import com.test.util.HibernateUtil; 

public class DepartmentManager extends HibernateUtil { 
public List<Department> list() { 
    Session session = HibernateUtil.getSessionFactory().getCurrentSession(); 
    session.beginTransaction(); 
    List<Department> set = null; 
    try { 
     Query q = session.createQuery("FROM Department d JOIN FETCH d.employees e"); 
     q.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 
     set = (List<Department>) q.list(); 
    } catch (HibernateException e) { 
     e.printStackTrace(); 
     session.getTransaction().rollback(); 
    } 
    session.getTransaction().commit(); 
    return set; 
} 
} 
+0

是否有该证明的代码会导致异常,还是仅仅在序列化期间?因为如果只是在序列化过程中才有意义。这是因为在序列化期间,每个实体都充当代理。如果你要采用一个部门并说“给我的员工”,那么对于每个员工你会说“给我部门”,然后为每个部门说“给我员工”......我预计堆栈溢出。在黑暗中刺,但如果你序列化为JSON,那就是struts2-json-plugin。该序列化程序提供包含和排除参数。 – Quaternion 2012-04-22 05:56:41

+0

这会让你修剪树木,在你想要的深度处将它挡住。在你提供的关系的情况下,参数可以为了不同的目的修剪树,因此这个单个查询可以最好由几个不同的动作来表示。 – Quaternion 2012-04-22 06:01:50

+0

我相信这个例外只是在序列化过程中,所以你说的是有道理的。我使用XSLT结果,而不是JSON,但挂断可能是相同的。 Struts2曾经提供了一些包含/排除模式,但是这个功能在某个时候被打破了,并且从未被修复过。它提供了一个使用OGNL表达式的“exposedValue”参数,但我从来没有得到它的工作权利(我可以用“[0] .departmentsList指定部门对象的列表,但不能让它指定特定的领域,如”[0 ] .departmentsList.id。“也许我将不得不去DOT路线Bozho下面提到。 – 2012-04-22 10:24:00

回答

5

一般情况下,你不应该序列化的实体。循环依赖和代理使这很难。相反,您应该手动将需要发送的数据传输到DTO(一个新的纯数据类),然后将其序列化。它不会有懒惰的集合,代理和什么。

+0

我有想过使用DTO,但没有这是让他们工作的好例子。例如,我怎样才能抓住集合将其转移到DTO而不触发循环引用?如果集合没有被填充(比如说我没有进行连接),我如何访问它以确定是否有任何需要填充到DTO中的内容,而不触发LazyInitializationException,或者导致Hiberante自动运行另一个查询填充它(使用OpenSessionInView过滤器时它会执行哪个操作)?一些关于如何在Hibernate中填充DTO的好例子会很有帮助。 – 2012-04-22 10:29:05

0

补上回应,我做了一个通用的转换谁给我做的工作,转化中的实体值DTO对象,你就必须从映射的实体相同的名称,让您的DTO领域。

这里是源代码。

/** * Atribui os valores de campos correspondentes de um objeto para um outro objeto de destino。 OS *坎波斯做objeto德DESTINO阙JA estiverem preenchidos NAO serao substituidos * *参数objetoOrigem * @参数objetoDestino * @返回 * @throws NegocioException */

public static <T1, T2> T2 convertEntity(T1 objetoOrigem, T2 objetoDestino) throws NegocioException { 

    if (objetoOrigem != null && objetoDestino != null) { 
     Class<? extends Object> classe = objetoOrigem.getClass(); 
     Class<? extends Object> classeDestino = objetoDestino.getClass(); 

     Field[] listaCampos = classe.getDeclaredFields(); 
     for (int i = 0; i < listaCampos.length; i++) { 
      Field campo = listaCampos[i]; 
      try { 
       Field campoDestino = classeDestino.getDeclaredField(campo.getName()); 
       campo.setAccessible(true); 
       campoDestino.setAccessible(true); 
       atribuiValorAoDestino(objetoOrigem, objetoDestino, campo, campoDestino); 
      } catch (NoSuchFieldException e) { 
       LOGGER.log(Logger.Level.TRACE, (Object) e); 
       continue; 
      } catch (IllegalArgumentException | IllegalAccessException e) { 
       LOGGER.error(e.getMessage(), e); 
       throw new NegocioException(e.getMessage(), EnumTypeException.ERROR); 
      } 
     } 
    } 
    return objetoDestino; 
}