#java #hibernate #lazy-initialization
#java #спящий режим #ленивая инициализация
Вопрос:
Я работаю над небольшим проектом, и у меня есть 2 таблицы, User и Application. У пользователя может быть несколько приложений, и приложение может использоваться несколькими пользователями, поэтому между ними существует отношение «многие ко многим». В каждой таблице есть несколько полей (id, имя, пароль, технологии и т.д.), И я также объявил 2 списка массивов как в классе User, так и в классе Application с добавлением @ManyToMany. Проблема в том, что на моем бизнес-уровне я написал метод, который должен добавлять приложение к пользователю, и когда я пытаюсь выполнить user.getListOfApplications().add(app), он выдает мне это исключение…
общедоступный класс ManagerHibernate {private 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, остальное — проверить, действительно ли параметры существуют в базе данных
Ответ №1:
Проблема возникает при загрузке данных с помощью следующего метода managerHibernate.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;
}
Итак, когда вы загружаете данные, платформа hibernate будет загружать только пользовательский объект. Поскольку вы решили использовать отложенную загрузку в своем классе модели, значения приложения будут загружены только при вашей попытке получить доступ к списку. Поскольку вы уже закрыли свой сеанс, платформа больше не может получать список приложений, что приводит к исключению отложенной загрузки.
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) Измените отложенную загрузку на быструю загрузку в вашем классе model pojo (поскольку вы используете отношение «многие ко многим», не рекомендуется использовать этот способ)
Комментарии:
1. Мне удалось исправить исключение, переместив session.OpenSession() и session.close() в методы setup() и exit () и объявив объект Session в качестве переменной экземпляра. По-видимому, метод добавления нового приложения к пользователю работает, я проверил это, вызвав затем метод printAllAppsForaUser(), но проблема все еще существует. Моя база данных не будет добавлять запись (user_id, app_id) в таблицу application_user, которая была создана с помощью добавления @ManyToMany. Есть идеи?
2. @ManyToMany (mappedBy=»список пользователей») закрытый список<Приложение> applist = новый список массивов<Приложение>(); @ManyToMany частный список<Пользователь> список пользователей = новый список массивов<Пользователь>(); Для этого я объявил 2 списка в классах User и Application
Ответ №2:
поскольку нет транзакции для извлечения отложенного listofApplication в user, вам нужно сначала извлечь его. для этого вы можете изменить 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);
});