Как исправить LazyInitializationException в Java?

Я работаю над небольшим проектом, и у меня есть 2 таблицы: User и Application. У пользователя может быть несколько приложений, и приложение может использоваться несколькими пользователями, поэтому между ними существует отношение «многие ко многим». В каждой таблице есть несколько полей (идентификатор, имя, пароль, технологии и т. д.), и я также объявил 2 массива как в классе пользователя, так и в классе приложения с аннотацией @ManyToMany. Проблема в том, что на моем бизнес-уровне я написал метод, который должен добавить приложение пользователю, и когда я пытаюсь сделать user.getListOfApplications().add(app), он дает мне это исключение...

открытый класс ManagerHibernate { частная SessionFactory sessionFactory;

public void setup()
{
     sessionFactory = new Configuration().configure().buildSessionFactory();
}

public void exit()
{
    sessionFactory.close();
}

public void create(Object obj)
{
    Session session = sessionFactory.openSession();
    session.beginTransaction();

    session.save(obj);

    session.getTransaction().commit();
    session.close();
}

public Object read(Class<?> c, int idObj)
{
    Session session = sessionFactory.openSession();
    session.beginTransaction();

    Object obj = session.get(c, idObj);

    System.out.println(obj);

    session.getTransaction().commit();
    session.close();
    return obj;
}

public void update(Object obj)
{
    Session session = sessionFactory.openSession();
    session.beginTransaction();

    session.update(obj);

    session.getTransaction().commit();
    session.close();
}

public void delete(Object obj)
{
    Session session = sessionFactory.openSession();
    session.beginTransaction();

    session.delete(obj);

    session.getTransaction().commit();
    session.close();
}

public <T> List<T> loadAllData(Class<T> type)
{
    Session session = sessionFactory.openSession();
    session.beginTransaction();

    CriteriaBuilder builder = session.getCriteriaBuilder();
    CriteriaQuery<T> criteria = builder.createQuery(type);
    criteria.from(type);
    List<T> data = session.createQuery(criteria).getResultList();

    session.getTransaction().commit();
    session.close();
    return data;
}

}

public Boolean addNewApplicationToUser(String userUserName, String applicationName)
{
    int okUser = 0;
    int okApp = 0;

     listOfApplications = managerHibernate.loadAllData(Application.class);
     listOfUsers = managerHibernate.loadAllData(User.class);
     User user = null;
     Application app = null;

    for(Application index: listOfApplications)
    {
        if (index.getApplicationName().equals(applicationName))
            {
                okApp = 1;
                app = index;
            }
    }

    for(User index: listOfUsers)
    {
        if (index.getUserUserName().equals(userUserName))
            {
                okUser = 1;
                user = index;
            }
    }

    if (okUser == 0  || okApp == 0)
        return false;
    else
    {   
        user.getListOfApplications().add(app);
        //app.getUserList().add(user);
        return true;
    }
}

Метод addNewApplicationToUser написан в другом классе с именем ControllerHibernate. Важна только ветвь else, остальное нужно проверить, действительно ли параметры существуют в базе данных.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

public <T> List<T> loadAllData(Class<T> type)
{
        // New session was opened here
        Session session = sessionFactory.openSession();
        session.beginTransaction();

        CriteriaBuilder builder = session.getCriteriaBuilder();
        CriteriaQuery<T> criteria = builder.createQuery(type);
        criteria.from(type);
        List<T> data = session.createQuery(criteria).getResultList();

        session.getTransaction().commit();
        session.close();
        //session is close here
        return data;
    }

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

listOfApplications = managerHibernate.loadAllData(Application.class);
//loading user data and close the session associated with it
listOfUsers = managerHibernate.loadAllData(User.class);
User user = null;
Application app = null;

for(Application index: listOfApplications)
{
  if (index.getApplicationName().equals(applicationName))
      {
          okApp = 1;
          app = index;
      }
}

for(User index: listOfUsers)
{
  if (index.getUserUserName().equals(userUserName))
      {
          okUser = 1;
          user = index;
      }
}

if (okUser == 0  || okApp == 0)
  return false;
else
{   
  // when you run this line the hibernate framework will try to retrieve the application data.Since you have the closed session lazy load exception occurs 
  user.getListOfApplications().add(app);
  return true;
}

Способы решения этой проблемы

1) Старайтесь держать сеанс открытым, чтобы данные приложения могли быть получены вашей структурой.

2) Измените ленивую загрузку на нетерпеливую загрузку в классе вашей модели pojo (поскольку вы используете отношения «многие ко многим», не рекомендуется использовать этот способ)

Мне удалось исправить исключение, переместив session.openSession() и session.close() в методы setup() и exit() и объявив объект Session как переменную экземпляра. По-видимому, метод добавления нового приложения пользователю работает, я проверил это, вызвав затем метод printAllAppsForaUser(), но проблема все еще есть. Моя база данных не добавит запись (user_id, app_id) в таблицу application_user, созданную аннотацией @ManyToMany. Есть идеи?

Gabi Suteu 27.03.2019 11:38

@ManyToMany(mappedBy = "userlist") private List<Application> applist = new ArrayList<Application>(); @ManyToMany private List<User> userlist = new ArrayList<User>(); Вот кто я объявил 2 списка в классах пользователя и приложения

Gabi Suteu 27.03.2019 11:39

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

public interface CriteriaSpec 
{
public void joinFetch(CriteriaBuilder builder, CriteriaQuery criteria, Root root);
}

public <T> List<T> loadAllData(Class<T> type, Optional<CriteriaSpec> spec)
{
Session session = sessionFactory.openSession();
session.beginTransaction();

CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<T> criteria = builder.createQuery(type);
Root root = criteria.from(type);
if (spec.isPresent())
    spec.joinFetch(builder, criteria, root);
List<T> data = session.createQuery(criteria).getResultList();

session.getTransaction().commit();
session.close();
return data;
}

затем используйте его:

managerHibernate.loadAllData(Application.class, Optional.empty());
listOfUsers = managerHibernate.loadAllData(User.class, (rootEntity, query, 
criteriaBuilder) -> {
            rootEntity.fetch("listOfApplications", JoinType.Left_OUTER_JOIN);

        });

Другие вопросы по теме