4

HibernateとJSF/primefacesを使用してJavaWebアプリケーションを開発しています。次のようなエラーが発生することがあります

1)同じ識別子を持つオブジェクトはすでにセッションに関連付けられています。

2)遅延初期化のロードに失敗しました*、セッションが存在しないか、セッションがすでに閉じられています。

これは私のアプリの不適切なコーディングが原因であることを私は知っています。これはaapを行う方法です:

ユーザーがページを要求したとき(それを従業員のリストとします)。ユーザーは従業員リストページ(empployeeList.xhtml)を取得します。EmployeeListMBeanはこのページの管理対象Beanです。コンストラクターのマネージドBeanで、メソッドpopulateEmployees()を呼び出しています。PopulateEmployee()は、EmployeeDaoメソッドgetAllEmployee()を使用してgetAllemployeesを取得します。

従業員クラスはここに行きます:

@Entity
@Table(name = "Employee")
@NamedQueries({
    @NamedQuery(name = "Employee.getAllEmployee", query = "from Employee"),
    @NamedQuery(name = "Employee.findEmployeeByFirstName", query = "from Employee where firstName = :firstName"),
    @NamedQuery(name = "Employee.findEmployeeByLastName", query = "from Employee where lastName = :lastName"),
    @NamedQuery(name = "Employee.findEmployeeByMiddleName", query = "from Employee where middleName = :middleName"),
    @NamedQuery(name = "Employee.findEmployeeByOffice", query = "from Employee where office.id = :officeId")
})
public class Employee implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "EID")
    private long id;
    @Column(name = "FIRST_NAME")
    private String firstName;
    @Column(name = "LAST_NAME")
    private String lastName;
    @Column(name = "GENDER")
    private String gender;
    @Column(name = "DOB")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date dateOfBirth;
    @Column(name = "DOH")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date dateOfHire;
    @ManyToOne(cascade= CascadeType.ALL)
    private Office office;
    @OneToOne(cascade = CascadeType.ALL)
    private ResidenceAddress residence;
    @OneToMany
    private List<Project> projects;

    //getters and setters

}

これが私のEmployeeDaoです:

public class EmployeeDao implements Serializable{
    private SessionFactory factory;
    private Session session;
    public void addEmployee(Employee employee){
        factory = HibernateUtil.getSessionFactory();
        session = factory.openSession();
        session.beginTransaction();
        session.save(employee); 
        session.getTransaction.commit();        
    }
    public List<Employee> getAllEmployee(){
        factory = HibernateUtil.getSessionFactory();
        session = factory.openSession();
        session.beginTransaction();
        List<Employee> cities = session.getNamedQuery("Employee.getAllEmployee").list();
        session.close();
        return cities;
    }

    public Employee getEmployeeByEmployeeId(long employeeId){
        factory = HibernateUtil.getSessionFactory();
        session = factory.openSession();
        session.beginTransaction();
        Employee employee = (Employee) session.get(Employee.class, employeeId);
        session.close();
        return employee;
    }    
}

質問1)ここで、メソッドでセッションを閉じてから、結果をマネージドBeanに返します。したがって、従業員リストページでは、テーブルに名前dobdateOfHireがリストされます。そして、私はバウトンビューをより詳細に持っています。このボタンをクリックすると、同じ管理対象Beanを使用して作業している選択した従業員のすべてのプロジェクトを表示したいのですが、エラー(2)が発生し、レイジーロードに失敗し、セッションがないか、セッションがすでに閉じられていません。daoのgetemployeeMethodでセッションを開いたままにしておくと、メモリリークの問題やその他の問題が発生する可能性があると思います。そうですか?また、遅延読み込みと熱心な読み込みを試しました。これらのタイプのフェッチをいつ/どのように使用するかを明確に教えてください。どうすればこれを解決できますか?これを解決するためにフィルターやフェイスリスナーを探すことはできますか?

質問2)従業員のプロジェクトを編集し、session.saveorupadte()、merge()、flush()を使用して更新しようとすると、「同じ識別子を持つオブジェクトはすでにセッションに関連付けられています」というエラーが発生します。どうすればこれを解決できますか?

質問3)sessionfactoryはリソースを消費していることを知っています。したがって、1つのアプリには1つのインスタンスだけで十分です。しかし、セッションはどうですか?アプリのシングルユーザーの場合、必要なセッションは1つだけですか?そのようなアプリを開発するための良い戦略を教えてください。

みなさん、ありがとうございました:)

4

2 に答える 2

0

他の答えで言われているように、あなたはcommitingあなたが始めた取引でなければなりません。データの読み取り中は、トランザクションは必要ありません。

質問1:遅延読み込み。

ビューで自由に使用できるように、遅延読み込み を行う必要があります。projectsこれを行うには、以下のようにコードを変更します。

@OneToMany(fetch=FetchType.LAZY)
private List<Project> projects;

質問2:同じ識別子を持つオブジェクトがすでに存在します。

デバッグしてid、保存しようとしているオブジェクトが何であるかを調べます。エラーのスタックトレースがなければ、ここではあまり役に立ちません。

質問3:セッションとセッションファクトリー。

この記事の2つの違いは次のとおりです。

SessionFactoryは、単一のデータストアのHibernatesの概念であり、スレッドセーフであるため、多くのスレッドが同時にアクセスして、単一のデータベースのコンパイル済みマッピングのセッションと不変のキャッシュを要求できます。SessionFactoryは通常、起動時に1回だけビルドされます。SessionFactoryは、アプリケーションコードで簡単にアクセスできるように、ある種のシングルトンでラップする必要があります。

セッションは軽量でスレッドセーフではないオブジェクト(いいえ、スレッド間で共有することはできません)であり、データベースとの単一の作業単位を表します。セッションはSessionFactoryによって開かれ、すべての作業が完了すると閉じられます。セッションは、永続性サービスの主要なインターフェイスです。セッションはデータベース接続を遅延的に取得します(つまり、必要な場合のみ)。あまりにも多くのセッションを作成しないようにするために、以下に示すようにThreadLocalクラスを使用して、currentSession()メソッドを何度呼び出しても現在のセッションを取得できます。

したがって、コードではsession、メソッドを閉じるたびにのローカル変数を作成して閉じる必要があります。

于 2012-04-27T13:38:17.807 に答える
0

最初の質問の答えは、すべての使用法でセッションをコミットするわけではないということだと思います。つまり、getAllEmployeeメソッドとgetEmployeeByEmployeeIdメソッドでは、トランザクションをコミットしません。追加でき session.getTransaction.commit(); ますが、トランザクションがコミットされないため、最初の問題が2番目の問題を引き起こす可能性があります。セッションでの良い戦略については、これを見ることができます。

于 2012-04-08T19:08:10.473 に答える