2012-04-22 49 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; 
}