我应该如何处理 dao 层中延迟初始化的 Hibernate 实体和不同层中的共享状态

How should i handle the lazily initialized Hibernate entities in dao layer and sharing state in diferrent layers

我正在编写一个 Web 应用程序(基于服务器的应用程序),其中有一个 Dao 层、服务层和应用程序层。我应该如何接管惰性初始化异常,这是由于从 dao 层返回的实体与在返回它的方法内部打开并在那里关闭的会话有关,这使得实体分离。 接下来的事情是跨不同层共享休眠实体是安全的。是什么让我问这个问题是场景:例如,假设我有一个与其他实体一对一关联的休眠实体。并假设dao将它传递给服务层到应用层。现在,如果我尝试通过传递的实体 getter 方法在应用程序层中获取此关联实体,则会触发一个数据库查询,我认为这与 "seperation of concerns" 混淆,因为数据库相关操作应限于道层。我说得对吗?

我在通过内存数据库对 Dao 层进行单元测试时发现了上述问题。我的情况是,我有一个名为 RegisteredUser 的 pojo class,它具有以下字段:(id、用户名、名字、姓氏、passwHash、电子邮件、StudyCentre)。 StudyCentre 是另一个实体,它通过一对一映射与 RegistereUser 相关联,用户名是 naturalid。 我想要的是两种类型的读取操作,第一种是我需要通过 natural id 在没有 studycentre 的情况下获取用户详细信息,第二种是通过 naturalid 再次获取完整的用户字段。在这里制作两个单独的 DTO 并将它们跨层传递是个好主意。

注册用户实体:

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))隔离开来。我怎样才能实现它。有没有我应该遵循的设计模式。我是休眠新手。如果我在任何地方做错了什么,请指导我。我曾尝试在 google 上找到类似的帖子,但未能获得有关该问题的任何见解。

how should i take over the lazy initialization exception, caused due to the fact that entity returned from dao layer is concerned with the session opened inside the method from where it is returned and also closed there which makes the entity detached.

您可以通过在服务或应用程序层中打开和关闭会话来处理该问题,并在单个事务中完成所有工作。

is it safe to share the hibernate entities across different layer

是的。不安全的是跨多个线程使用实体实例,因为实体不是线程安全的。

a database query is fired which i think is messing up with the "seperation of concerns" as database related operation should be constrained to the dao layer. am i right?

没有。服务层不包含任何代码来触发此数据库查询。它透明地发生,服务层不必关心它,因为您选择使关联变得惰性。

is making two seperate DTOs a good idea here and passing them across layers.

没有。 DTO 可用于在不同的应用程序之间传输数据。在您的应用程序中,使用托管实体是正确的方法。

i want to keep my service layer(not yet implemented) to be isolated from things specific to hibernate like sessions and database related operations(CRUD). how can i achieve it.

通过使用 Spring 或 Java EE(或具有此功能的任何其他框架)允许使用声明式事务并为您处理 opening/closing 会话和事务的任务每当调用事务方法时。

您还应避免使用专有会话 API,而应使用标准 JPA API。