如何修复 Java 中的 LazyInitializationException?
How to fix LazyInitializationException in Java?
我在做一个小项目,我有 2 个 table,用户和应用程序。一个用户可以有多个应用,一个应用可能被多个用户使用,所以它们之间是多对多的关系。每个 table 都有一些字段(id、名称、密码、技术等),我还在用户和应用程序 class 中使用 @ManyToMany 注释声明了 2 个数组列表。问题是,在我的业务层中,我编写了一个方法,该方法应该向用户添加一个应用程序,当我尝试执行 user.getListOfApplications().add(app) 时,它给了我那个异常...
publicclassHibernate 管理器
{
私有 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 是在另一个 class 中编写的,称为 ControllerHibernate。只有else分支重要,剩下的就是检查参数是否确实存在于数据库中
当您使用以下方法加载数据时出现问题 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 框架只会加载用户对象。由于您已选择在模型中使用延迟加载 class,因此仅当您尝试访问列表时才会加载应用程序值。由于您已经关闭了会话,框架无法再获取应用程序列表,从而导致延迟加载异常。
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 中将延迟加载更改为预加载 class(因为您使用的是多对多关系,因此不建议使用这种方式)
由于没有用于获取用户中的惰性 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);
});
我在做一个小项目,我有 2 个 table,用户和应用程序。一个用户可以有多个应用,一个应用可能被多个用户使用,所以它们之间是多对多的关系。每个 table 都有一些字段(id、名称、密码、技术等),我还在用户和应用程序 class 中使用 @ManyToMany 注释声明了 2 个数组列表。问题是,在我的业务层中,我编写了一个方法,该方法应该向用户添加一个应用程序,当我尝试执行 user.getListOfApplications().add(app) 时,它给了我那个异常...
publicclassHibernate 管理器 { 私有 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 是在另一个 class 中编写的,称为 ControllerHibernate。只有else分支重要,剩下的就是检查参数是否确实存在于数据库中
当您使用以下方法加载数据时出现问题 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 框架只会加载用户对象。由于您已选择在模型中使用延迟加载 class,因此仅当您尝试访问列表时才会加载应用程序值。由于您已经关闭了会话,框架无法再获取应用程序列表,从而导致延迟加载异常。
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 中将延迟加载更改为预加载 class(因为您使用的是多对多关系,因此不建议使用这种方式)
由于没有用于获取用户中的惰性 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);
});