2012-04-22 2 views
3

非常に単純なHibernateの例で、いくつかのエンティティオブジェクトを設定しようとしています。私のデータベースは「部署」(Id、Name)と「従業員」(Id、DepartmentsId、FirstName、LastName)の2つのテーブルで構成されています。私のSQLクエリは、EmployeesとDepartmentsの左結合です。双方向コレクションをフェッチする際のHibernateでの無限ループを避けるには?

Hibernate documentationで指定された注釈を設定しましたが、エンティティをシリアル化しようとするたびに、Hibernateは無限ループに入り、最終的にStackOverFlowError例外をスローします。私の別の質問に答える誰かが、 "Department"オブジェクトにEmployeeオブジェクトのセットを含む "Department"オブジェクトを含む "Employee"オブジェクトのセットが含まれているため、スタックオーバーフローが発生していると判断できました。

このタイプの双方向関係は、上にリンクされたドキュメントの通りです(Departmentの "mappedBy"パラメータはHibernateを手がかりにしていると思います)、私はコメントされている "joinColumn"アノテーションを使ってみました私が読んだその他のことは、と仮定してがこの状況で無限ループにならないほどスマートであると仮定していますが、私の例では機能しません。 EmployeeクラスからDepartmentオブジェクトを削除することで双方向関係を単方向関係に変更するとすべてうまく動作しますが、明らかにこれは多くの機能を失う原因になります。

私は古いXMLマッピングファイルのアノテーションを前にして、子テーブルの "inverse"パラメータを設定しようとしましたが、それでも同じ問題が発生します。どのようにしてこの双方向関係が働くはずですか?

部門:

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

が示されたのいずれかをい@throwsコードは例外を引き起こすか、まさに直列化中ですか?ので、それはちょうどセットのシリアル化の間に意味がある場合。これは、シリアライズ中に各エンティティがプロキシとして機能するためです。あなたが部門を取って「私に従業員を譲りたい」と言ったら、各従業員は「私に部門を教えてください」と言っていたのですが、部門ごとに「私に従業員をください」と言います...私はスタックオーバーフロー。暗闇の中で刺すが、JSONにシリアル化している場合は、struts2-json-pluginがある。このシリアライザには、includeパラメータとexcludeパラメータが用意されています。 – Quaternion

+0

これは、木を剪定して、あなたが望む深さでそれをブロックすることができます。あなたが指定した関係の場合、パラメータはさまざまな目的のためにツリーを刈り込むことができます。したがって、この単一のクエリはいくつかの異なるアクションによって最もよく表現されます。 – Quaternion

+0

私は例外がシリアライゼーションの間だけであると信じています。 JSONではなくXSLTの結果を使用しますが、ハングアップはおそらく同じです。 Struts2はいくつかのインクルード/エクスクルードパターンを提供していましたが、その機能はある時点で壊れており修正されていませんでした。 OGNL式を使って "exposedValue"パラメータを提供していますが、正しく動作することはありませんでした( "[0] .departmentsList"でDepartmentオブジェクトのリストを指定できますが、 "[0 ] .departmentsList.id。 "おそらく、Bozhoが以下に言及しているDTOルートに行く必要があります。 –

答えて

5

、あなたはあなたのエンティティをシリアライズするべきではありません。円の依存関係と代理はそれを難し​​くします。代わりに、送信する必要のあるデータを手動でDTO(新しいデータ専用クラス)に転送し、代わりにそれをシリアル化する必要があります。それは、怠惰なコレクション、プロキシ、およびその他のものを持っていません。

+0

DTOの使用について考えたそれらを働かせる良い例です。たとえば、循環参照をトリガーせずにコレクションをDTOに転送するにはどうすればよいですか?コレクションにデータが入力されていない場合(例:I did not join)、LazyInitializationExceptionをトリガーせずにDTOにデータを格納する必要があるかどうかを調べたり、Hiberanteに自動的に別のクエリを実行させる方法OpenSessionInViewフィルタを使用しているときに入力します。 HibernateでDTOをどのように埋め込むかの良い例は、非常に役に立ちます。 –

0

トップのレスポンスを補完するために、エンティティ値をDTOオブジェクトに転送するジェネリック変換を行ったのですが、マッピングされたエンティティから同じ名前のフィールドを作成するだけです。

ここにソースコードがあります。

/** *アトリビューションは、デストロから遠く離れた場所にもあります。 OS *カンポス行うobjetoデDESTINO queのJA estiverem preenchidos奈央serao substituidos * * @param objetoOrigem * @param objetoDestino * @return *は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; 
} 
関連する問題