Как я должен обрабатывать лениво инициализированные объекты Hibernate на дао-уровне и состояние общего доступа на разных уровнях - PullRequest
1 голос
/ 18 апреля 2019

Я пишу веб-приложение (серверное приложение), где у меня есть слой дао, уровень обслуживания и уровень приложения.как я должен принять исключение отложенной инициализации, вызванное тем, что сущность, возвращаемая из дао-уровня, связана с сеансом, открытым внутри метода, из которого он возвращается, а также закрытым, что делает сущность отсоединенной.Следующая вещь - это безопасно разделять спящие сущности между различными уровнями.что заставляет меня задать этот вопрос, так это сценарий: например, предположим, что у меня есть находящийся в спящем состоянии объект, имеющий один к одному связь с некоторым другим объектом.и предположим, что дао передал его на сервисный уровень на прикладной уровень.Теперь, если я пытаюсь получить эту ассоциированную сущность на уровне приложения с помощью переданного метода получения сущности, запускается запрос к базе данных, который, я думаю, портит «разделение проблем», так как связанная с базой данных операция должна быть ограничена уровнем дао.я прав?

Я обнаружил упомянутую проблему во время модульного тестирования своего даослоя через базу данных в памяти.Мой сценарий, у меня есть один из класса pojo с именем RegisteredUser, имеющего поля: (id, имя пользователя, имя, фамилия, passwHash, электронная почта, StudyCentre).StudyCentre - это другая сущность, которая связана с RegistereUser отображением один на один, а имя пользователя - это Naturalid.Мне нужны 2 типа операций чтения, во-первых, мне нужно получить данные пользователя без учебного центра через натуральный идентификатор, а во-вторых, снова получить полные поля пользователя через натурал.хорошая идея сделать два отдельных DTO и передавать их по слоям.

RegisteredUser Entity:

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 Implementor:

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, но не смог получить представление о проблеме.

1 Ответ

2 голосов
/ 18 апреля 2019

как мне взять на себя исключение отложенной инициализации, вызванное тем, что сущность, возвращаемая из слоя дао, связана с сеансом, открытым внутри метода, из которого он возвращается, а также закрытым, что делает сущность отсоединенной.

Вы бы справились с этим, открыв и закрыв сеанс на уровне службы или приложения и выполнив всю работу в одной транзакции.

это такбезопасно делить объекты гибернации на другой слой

Да.Что небезопасно, так это использование экземпляра сущности в нескольких потоках, потому что сущности не являются потоко-безопасными.

выполняется запрос к базе данных, который, как мне кажется, путается с «разделением проблем», так какОперация, связанная с базой данных, должна быть ограничена уровнем dao.я прав?

Нет.Слой службы не содержит кода для запуска этого запроса к базе данных.Это происходит прозрачно, без необходимости заботиться об этом на уровне обслуживания и потому, что вы решили сделать ассоциацию ленивой.

делает две отдельные DTO хорошей идеей и передает их через слои.

Нет.DTO полезны для передачи данных между отдельными приложениями.Внутри вашего приложения работа с управляемыми объектами является правильным способом.

Я хочу, чтобы мой уровень обслуживания (еще не реализованный) был изолирован от вещей, характерных для спящего режима, таких как сеансы и операции с базами данных (CRUD).).как я могу этого достичь.

Используя Spring или Java EE (или любую другую платформу, имеющую эту функцию), которая позволяет использовать декларативные транзакции и решать задачу открытия / закрытия сеансов и транзакций для васвсякий раз, когда вызывается транзакционный метод.

Вам также следует избегать использования проприетарного Session API и использовать вместо него стандартный JPA API.

...