daoレイヤーで遅延初期化されたHibernateエンティティを処理し、異なるレイヤーで状態を共有する方法

プラヴィーンクマール:

私はdaoレイヤー、サービスレイヤー、アプリケーションレイヤーがあるWebアプリケーション(サーバーベースのアプリケーション)を書いています。daoレイヤーから返されたエンティティが、メソッドの内部で開かれたセッションに関係し、そこから閉じられてエンティティが切り離されるため、遅延初期化例外をどのように引き継ぐ必要がありますか?次のことは、異なるレイヤー間で休止状態のエンティティを共有しても安全です。この質問をするのはシナリオです。たとえば、他のエンティティと1対1の関連付けを持つ休止状態のエンティティがあるとします。daoがそれをサービス層からアプリケーション層に渡したとします。今、渡されたエンティティゲッターメソッドを介してアプリケーションレイヤーでこの関連付けられたエンティティを取得しようとすると、データベース関連の操作をdaoレイヤーに制限する必要があるため、「懸念の分離」に混乱していると思われるデータベースクエリが起動されます。私は正しいですか?

インメモリデータベースを介してdaoレイヤーをユニットテストしているときに、上記の問題を発見しました。私のシナリオは、RegisteredUserと呼ばれるpojoクラスの1つに(id、username、firstname、lastname、passwHash、email、StudyCentre)のフィールドがあることです。StudyCentreは、RegistereUserと1対1のマッピングで関連付けられている別のエンティティであり、ユーザー名はnaturalidです。私が欲しいのは2種類の読み取り操作です.1つ目は、Natural IDを通じてstudycentreなしでユーザーの詳細を取得する必要があること、2つ目は、Naturalidを通じて完全なユーザーフィールドを再度取得することです。ここでは、2つの別々のDTOを良いアイデアにして、レイヤー間で渡します。

RegisteredUserエンティティ:

package com.ignoubadhega.pojos;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.NaturalId;

@Entity
@Table(name = "registered_user")
@DynamicUpdate
public class RegisteredUser {

    private Long dbUserId;
    private String userName;
    private String passwHash;
    private String firstName;
    private String lastName;
    private String email;
    private StudyCentre studyCentre;

    RegisteredUser() {
    }

    public RegisteredUser(
            String userName, String passwHash, String firstName,
            String lastName, String email
    ) {
        super();
        this.userName = userName;
        this.passwHash = passwHash;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "db_user_id")
    public Long getDbUserId() {
        return dbUserId;
    }

    @Override
    public String toString() {
        return "RegisteredUser [dbUserId="
               + dbUserId
               + ", userName="
               + userName
               + ", passwHash="
               + passwHash
               + ", firstName="
               + firstName
               + ", lastName="
               + lastName
               + ", email="
               + email
               + "]";
    }

    public void setDbUserId(Long dbUserId) {
        this.dbUserId = dbUserId;
    }

    @Column(name = "username", nullable = false, unique = true)
    @NaturalId
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Column(name = "passw_hash", nullable = false)
    public String getPasswHash() {
        return passwHash;
    }

    public void setPasswHash(String passwHash) {
        this.passwHash = passwHash;
    }

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

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

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

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

    @Column(name = "email", nullable = false, unique = true)
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "db_study_centre_id", nullable = false)
    public StudyCentre getStudyCentre() {
        return studyCentre;
    }

    public void setStudyCentre(StudyCentre studyCentre) {
        this.studyCentre = studyCentre;
    }

}

Daoインプリメンター:

package com.ignoubadhega.dao.impl;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import com.ignoubadhega.dao.RegisteredUserDAO;
import com.ignoubadhega.pojos.RegisteredUser;

public class RegisteredUserDAOImpl implements RegisteredUserDAO {

    private SessionFactory sessionFactory;

    public RegisteredUserDAOImpl(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public void addUser(RegisteredUser user) {
        try (Session session = sessionFactory
                .openSession()) {
            session.beginTransaction();
            session.persist(user);
            session.getTransaction().commit();
        } catch (HibernateException except) {
            except.printStackTrace();
        }
    }

    @Override
    public RegisteredUser getUserByUserName(String username, boolean doesStudyCentereNeeded) {
        try (Session session = sessionFactory
                .openSession()) {
            RegisteredUser user = session
                    .bySimpleNaturalId(RegisteredUser.class).load(username);
            if (doesStudyCentereNeeded) {
                user.setStudyCentre(user.getStudyCentre());
            }
            return user;
        } catch (HibernateException except) {
            except.printStackTrace();
        }
        return null;
    }

    @Override
    public void deleteUser(RegisteredUser user) {
        try (Session session = sessionFactory
                .openSession()) {
            session.beginTransaction();
            session.delete(user);
            session.getTransaction().commit();
        } catch (HibernateException except) {
            except.printStackTrace();
        }
    }

    @Override
    public void updateUser(RegisteredUser user) {
        try (Session session = sessionFactory
                .openSession()) {
            session.beginTransaction();
            session.update(user);
            session.getTransaction().commit();
        } catch (HibernateException except) {
            except.printStackTrace();
        }
    }

}

怠惰な初期化の問題を見つけるTestCase:

@Test
@DisplayName(
    "User through its natural id 'username' assuming the user"
        + " is persistent in the database is successful"
)
void test_fetching_a_persistent_user_through_username_is_successful() {
    try (Session session = sessionFactory.openSession()) {
        session.beginTransaction();
        session.persist(user);
        session.getTransaction().commit();
        RegisteredUser retrievedUser =
                dao.getUserByUserName("prav", true);
        assertNotNull(retrievedUser);
        assert_actual_user_and_retrieved_user_fields_are_equal(user,
                retrievedUser);
    } catch (HibernateException except) {
        except.printStackTrace();
    }
}

private static void assert_actual_user_and_retrieved_user_fields_are_equal(
        RegisteredUser actualUser, RegisteredUser userRetrieved
) throws MultipleFailuresError {
    assertAll("user fields",
            () -> assertEquals(actualUser.getUserName(),
                    userRetrieved.getUserName()),
            () -> assertEquals(actualUser.getPasswHash(),
                    userRetrieved.getPasswHash()),
            () -> assertEquals(actualUser.getFirstName(),
                    userRetrieved.getFirstName()),
            () -> assertEquals(actualUser.getLastName(),
                    userRetrieved.getLastName()),
            () -> assertEquals(actualUser.getEmail(),
                    userRetrieved.getEmail()),
            () -> {
                StudyCentre retrievedCentre =
                        userRetrieved.getStudyCentre();
                assertNotNull(retrievedCentre);
                assertAll("user study centre assosciated",
                        () -> assertEquals(
                                actualUser.getStudyCentre().getData()
                                        .getStudyCentreName(),
                                retrievedCentre.getData()
                                        .getStudyCentreName()),
                        () -> assertEquals(
                                actualUser.getStudyCentre().getData()
                                        .getRegionalCentreCode(),
                                retrievedCentre.getData()
                                        .getRegionalCentreCode()));
            });
}

サービス層(まだ実装されていない)を、セッションやデータベース関連の操作(CRUD)などの休止状態に固有のものから分離したい。どうすればそれを達成できますか?私が従うべきデザインパターンはありますか?私は休止状態に新しいです。私がどこかで何か間違ったことをしているなら私を導いてください。私はグーグルで同様のスレッドを見つけようとしましたが、問題に関する洞察を得ることができませんでした。

JB Nizet:

daoレイヤーから返されたエンティティが、メソッドの内部で開かれたセッションに関係し、そこから閉じられてエンティティが切り離されるため、遅延初期化例外をどのように引き継ぐ必要がありますか?

サービス層またはアプリケーション層でセッションを開閉して、すべての作業を1つのトランザクションで実行することで、これに対処します。

異なるレイヤー間で休止状態のエンティティを共有しても安全ですか

はい。エンティティはスレッドセーフではないため、安全ではないのは、複数のスレッドにわたってエンティティインスタンスを使用することです。

データベース関連の操作をdaoレイヤーに制限する必要があるため、「懸念の分離」に混乱していると思われるデータベースクエリが起動されます。私は正しいですか?

いいえ。サービスレイヤーには、このデータベースクエリをトリガーするコードは含まれていません。これは透過的に行われ、サービスレイヤーが気にする必要はありません。関連付けを遅延させることを選択したためです。

ここでは、2つの別々のDTOを良いアイデアにして、レイヤー間で渡します。

いいえ。DTOは、個別のアプリケーション間でデータを転送するのに役立ちます。アプリケーション内では、管理対象エンティティを操作するのが正しい方法です。

サービス層(まだ実装されていない)を、セッションやデータベース関連の操作(CRUD)などの休止状態に固有のものから分離したい。どうすればそれを達成できますか?

SpringまたはJava EE(またはこの機能を備えた他のフレームワーク)を使用することにより、宣言型トランザクションを使用し、トランザクションメソッドが呼び出されるたびにセッションとトランザクションを開く/閉じるタスクを処理します。

また、独自のセッションAPIの使用は避け、代わりに標準のJPA APIを使用してください。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ