我应该如何处理dao层中的懒惰初始化Hibernate实体以及在不同层中共享状态

问题描述 投票:1回答:1

我正在编写一个Web应用程序(基于服务器的应用程序),其中我有一个dao层,服务层和应用程序层。我应该如何接管延迟初始化异常,这是由于从dao层返回的实体关注的是从返回它的方法内部打开的会话以及在那里关闭使得实体分离的事实。接下来是在不同层共享hibernate实体是安全的。让我问这个问题的原因是:例如假设我有一个与其他实体有一对一关联的休眠实体。并假设dao将其传递给服务层到应用层。现在,如果我尝试通过传递的实体getter方法在应用程序层中获取此关联实体,则会触发数据库查询,我认为这会混淆“关注点分离”,因为数据库相关操作应该被约束到dao层。我对吗?

我在通过内存数据库对我的dao层进行单元测试的过程中发现了上述问题。我的情况是,我有一个名为RegisteredUser的pojo类,其中包含以下字段:(id,username,firstname,lastname,passwHash,email,StudyCentre)。 StudyCentre是另一个实体,它与RegistereUser通过一对一映射进行协作,用户名是naturalid。我想要的是2种类型的读取操作,第一种是我需要通过自然id获得没有studycentre的用户详细信息,第二种是通过naturalid再次获取完整的用户字段。正在制作两个单独的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;
    }

}

道实施者:

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();
        }
    }

}

发现延迟初始化问题的测试用例:

@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))隔离开来。我怎样才能实现它。我应该遵循任何设计模式吗?我是hibernate的新手。如果我在任何地方做错事,请指导我。我试图在谷歌上找到类似的线程但未能获得有关该问题的任何见解。

java hibernate jpa-2.0 dao
1个回答
2
投票

我应该如何接管延迟初始化异常,这是由于从dao层返回的实体关注的是从返回它的方法内部打开的会话以及在那里关闭使得实体分离的事实。

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

在不同层共享hibernate实体是否安全

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

一个数据库查询被触发,我认为这会混淆“关注点分离”,因为数据库相关的操作应该被约束到dao层。我对吗?

否。服务层不包含任何用于触发此数据库查询的代码。它透明地发生,没有服务层必须关心它,因为你选择使关联变得懒惰。

在这里制作两个独立的DOT是一个好主意,并将它们传递到各个层。

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

我希望保持我的服务层(尚未实现)与特定于休眠的事物(如会话和数据库相关操作(CRUD))隔离开来。我怎样才能实现它。

通过使用Spring或Java EE(或具有此功能的任何其他框架),允许使用声明式事务并在调用事务方法时处理打开/关闭会话和事务的任务。

您还应该避免使用专有的Session API,而是使用标准的JPA API。

© www.soinside.com 2019 - 2024. All rights reserved.