-1

私はSpringの世界ではまったく新しく、単純なHibernate DAOを実装しようとしていますが、削除更新の操作には疑問があります

Spring 3.2.1 と Hibernate 4.1.9 と MySQL データベースを使用しています。

したがって、MySQL データベースには、次の構造を持つpersonという名前のテーブルがあります。

mysql> describe person;
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| pid       | int(11)      | NO   | PRI | NULL    | auto_increment |
| firstname | varchar(255) | YES  |     | NULL    |                |
| lastname  | varchar(255) | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

私の Spring アプリケーションでは、必要な CRUD 操作を定義した DAO 用に次のインターフェースを定義しました。

package org.andrea.myexample.HibernateOnSpring.dao;

import java.util.List;

import org.andrea.myexample.HibernateOnSpring.entity.Person;

public interface PersonDAO {

    public void addPerson(Person p);

    public Person getById(int id);

    public List<Person> getPersonsList();

    public void delete(int id);

    public void update(Person person);

}

次に、クラスPersonDAOImplementによってこのインターフェイスを次のように実装しました。

package org.andrea.myexample.HibernateOnSpring.dao;

import java.util.List;

import org.andrea.myexample.HibernateOnSpring.entity.Person;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;

public class PersonDAOImpl implements PersonDAO {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    // Metodo che inserisce un nuovo record nella tabella person
    @Transactional(readOnly = false)
    public void addPerson(Person p) {
        Session session = sessionFactory.openSession();
        session.save(p);
        session.close();
    }

    /*
     * Metodo che recupera un record, rappresentante una persona, avente uno
     * specifico id dalla tabella.
     * 
     * @param L'id univoco della persona
     */

    public Person getById(int id) {
        Session session = sessionFactory.openSession();
        try {
            return (Person) session.get(Person.class, id);
        } finally {
            session.close();
        }
    }

    /*
     * Metodo che recupera la lista di tutti le persone rappresentanti dalle
     * righe della tabella person
     */
    @SuppressWarnings("unchecked")
    public List<Person> getPersonsList() {

        Session session = sessionFactory.openSession();
        try {

            Criteria criteria = session.createCriteria(Person.class);
            return criteria.list();

        } finally {
            session.close();
        }

    }

    /*
     * Metodo che elimina dalla tabella person la riga avente uno specifico id
     * 
     * @param l'id della persona da eliminare dalla tabella person
     */
    @Transactional
    public void delete(int id) {
        Person personToDelete = getById(id);
        sessionFactory.getCurrentSession().delete(personToDelete);
        /*Session session = sessionFactory.openSession();

        try {
            Person personToDelete = getById(id);
            System.out.println("person to delete: " + personToDelete);
            session.delete(personToDelete);


        } finally {
            session.close();
        }
        */
    }

    @Transactional
    public void update(Person person){

        sessionFactory.getCurrentSession().update(person);
        /*
        Session session = sessionFactory.openSession();

        try {
            System.out.println("UPDATING");
            session.merge(person);

        } finally {
            System.out.println("CLOSE SESSION");
            session.close();
        }
        */
    }

}

この例は問題なく動作するようです (CRUD メソッドを実行してテーブルに行を挿入し、単一の行または行のリストをクエリし、行を削除し、行を更新するメイン メソッドを含むメイン クラスを使用してテストしました)。行の値)

私が奇妙だと思う唯一のことは、deleteおよびupdateメソッドで正しく機能するには、次の方法でsessionFactory オブジェクトから現在のセッションを取得する必要があることです。

sessionFactory.getCurrentSession().delete(personToDelete);
sessionFactory.getCurrentSession().update(person);

逆に、行を追加またはクエリする必要がある場合は、次のように新しいセッションを開く必要があります。

Session session = sessionFactory.openSession();

なんで?

前のPersonDAOImplクラスでは、削除および更新メソッドの古い実装にコメントしましたが、新しいセッションを開こうとしました (addPerson および query メソッドで問題なく行うように) が、この方法では機能しません...作業現在のセッションを取得した場合にのみ問題ありません

なんで?この DAO オブジェクトの実装は正しいですか?

4

2 に答える 2

1

開始するのに適した場所は、トランザクションに関するよく書かれたHibernateドキュメントを読むことです。SessionFactory.openSession()との両方をSessionFactory.getCurrentSession()使用してセッションインスタンスを取得できることが明確に示されていますが、セッションの取得方法が異なります。

あなたSessionFactory.openSession()がいると、Hibernateにあなたのニーズに合わせて新しいセッションを開くように強制します。これは、望ましくない/予期しない結果につながる可能性があります。逆に、SessionFactory.getCurrentSession()使用される場合、aSessionは現在のコンテキストから受信されます。これは、トランザクション、要求などを意味する場合があります。

それをよりよく理解するために、次の例を考えてみましょう。次のような2つのDAOオブジェクトからメソッドを呼び出す必要があるサービスレイヤーがあります。

@Service
public class Service {

    @Autowired
    private FirstDao firstDao;
    @Autowired
    private SecondDao secondDao;

    @Transactional
    public void serviceMethod(int param) {
        FirstEntity firstEntity = firstDao.getFirstEntity(param);
        SecondEntity secondEntity = secondDao.updateSecondEntity(firstEntity);
    }

}

現在、2つのオプションがあります。各DAOにSession session = factory.openSession();またはのようなメソッドを実装させることSession session = factory.getCurrentSession()です。あなたの信念は、両方のメソッド呼び出し(firstDao.getFirstEntitysecondDao.updateSecondEntity)が同じセッションを使用する必要があるということです。少なくとも、メソッド呼び出しが終了firstEntityした後はデタッチ状態にfirstDaoなり(セッションはそこで閉じられる必要があります)、新しいセッションに再接続する必要があります。コードを機能させる。

ただし、両方のDAOメソッドを呼び出すとgetCurrentSession、同じセッションインスタンスが返されます(トランザクション管理メカニズムによって事前に開かれているため)。この場合、Hibernateのドキュメントには次のように明確に記載されています

BMTまたはCMTでHibernateTransactionAPIを使用する必要はまったくなく、トランザクションにバインドされた「現在の」セッションの自動伝播を取得します。

そして、すべてのセッション管理は、DAOで呼び出しによってセッションインスタンスを取得したときに行われますSession session = factory.getCurrentSession();。それだけです。Hibernateドキュメントからの別の抜粋:

アプリケーションコードは、sessionFactory.getCurrentSession()を呼び出すことにより、「現在のセッション」にアクセスしてリクエストを処理できます。常に現在のデータベーストランザクション(イタリック鉱山)にスコープされたセッションを取得します。

Springによる宣言型トランザクションの境界はサービス/ビジネスレイヤーに影響し、そこSessionから、現在のトランザクションコンテキストにバインドされている同じオブジェクトを使用する複数のDAOを呼び出す必要がある場合があることに注意してください。マークされたメソッド@Transactionalが呼び出され、トランザクションがコミットされたときにセッションが開始され、トランザクションが開始され、メソッドが実行されたときにセッションが閉じられます。

もちろん、これは、トランザクションごとのセッションがトランザクション管理を実装する唯一の方法であることを意味するものではありません。ビュー内のセッションを開く、会話ごとのセッションなどの他の概念もあります。ただし、いずれの場合も、DAOは、コンテナ、フレームワーク、または記述されたコードによって管理されるSession現在のコンテキストからオブジェクトを取得します。

あなたのコードに関して、私は以下の矛盾を修正することを提案します:

  • @TransactionalDAOレイヤーの実装からすべての注釈を削除します
  • sessionFactory.getCurrentSession()データを操作するためにのみ使用する
  • トランザクションの境界を念頭に置いてサービスレイヤーを選択します。そこにあるメソッドにマークを付ける必要があります。これにより、Springがセッションとトランザクションを管理します(それ以外の場合は、Hibernateのドキュメントでプログラムによるトランザクションの境界@Transactionalを探します。

関連資料:

于 2013-02-25T10:07:39.493 に答える
0

いくつかのヒントを提供しますが、ソース コードとドキュメントを掘り下げる必要があります。ここには多くの概念があります。

セッションで何らかの操作を行っても、変更がすぐにデータベースにプッシュされるわけではありません。これは、セッションのフラッシュ モードと実行中の操作に大きく依存します。これについては、休止状態のドキュメントを参照してください。セッションがフラッシュされると、変更が db にプッシュされます。トランザクションがコミットされると、変更は持続します。

コメントされたコードで閉じる前に呼び出すとうまくいきますsession.flush()が、以下の説明のため不要です。

上記の更新と削除のコードでは、「getCurrentSession」を呼び出すと、既存のセッションを取得するか、新しいセッションを取得org.springframework.orm.hibernate4.SpringJtaSessionContextします (JTA を使用していると仮定して、これを理解するためにコードをもう一度掘り下げます)。

新しいセッションが作成されると、Hibernate はトランザクションと同期を登録します。トランザクションが完了する前に、Hibernate はコールバックを受け取ります。ここで、Hibernate はセッションをフラッシュします。あなたの場合、トランザクションは、配置した注釈を介して春に区切られます。

何が起こっているのかを理解するために、「getCurrentSession」の呼び出しからコードをデバッグすることをお勧めします。構成に応じて、これらのクラス/メソッドに遭遇します。

 org.hibernate.internal.SessionFactoryImpl.getCurrentSession()
 org.springframework.orm.hibernate4.SpringJtaSessionContext.buildOrObtainSession()
 org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization
 org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorImpl.beforeCompletion()
于 2013-02-24T18:55:09.073 に答える