我在我的应用程序中使用Hibernate,并注意到每当抛出Hibernate错误时,应用程序最终会因与DB相关的错误而崩溃。从我读过的,问题是Hibernate会话处于某种不可用的状态,因此需要被丢弃。但是,它将被返回到池中并在下一次调用时重新使用,而不是被丢弃。
在每个函数开始时,确保会话可用的正确方法是什么,如果没有,确保它被正确丢弃并启动新会话?
我的Hibernate配置文件包括以下内容:
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.timeout">60</property>
<property name="hibernate.c3p0.idle_test_period">45</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.preferredTestQuery">SELECT 1;</property>
<property name="hibernate.c3p0.testConnectionOnCheckout">true</property>
<property name="hibernate.c3p0.acquireRetryAttempts">3</property>
我的代码有这个错误(完整的函数调用)是:
public static ReturnCodes newUser(Long id, String username, String country) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
//check for duplicate user
User checkUser = getUserByExternalID(id, true);
if (checkUser != null && checkUser.getId().intValue() == id.intValue()) {
transaction.commit();
return ReturnCodes.DUPLICATE;
}
User newUser = new User();
newUser.setUsername(username);
newUser.setExternalID(id);
newUser.setCountry(country);
session.save(newUser);
transaction.commit();
return ReturnCodes.SUCCESS;
} catch (ConstraintViolationException e) {
log.info("The user must already exists, so cleanup and return DUPLICATE.");
return ReturnCodes.DUPLICATE;
} catch (Exception e) {
log.info("An unknown error occurred, so cleanup and return GENERAL FAILURE.");
return ReturnCodes.GENERAL_FAILURE;
}
}
public static User getUserByExternalID(Long userid, boolean leaveTransaction) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
User user = null;
try {
session.beginTransaction();
Criteria c = session.createCriteria(User.class);
c.add(Restrictions.eq("externalID", userid));
user = (User)c.uniqueResult();
if (!leaveTransaction)
session.getTransaction().commit();
} catch (Exception e) {
log.error("Could not retrieve user with External ID " + userid, e);
}
return user;
}
答案 0 :(得分:1)
会话和事务管理有点复杂。
Spring通过使用@Transactional
注释方法来实现它。
如果您想手动处理,read this。您可以使用Filter
启动会话,或使用某个代理和ThreadLocal
。最常见的是会话是按请求创建的。
答案 1 :(得分:1)
最常见的方法是管理服务中的事务和Hibernate会话,DAO之上的层。在这种情况下,您可以在同一事务中执行多个数据库操作。例如,在您的情况下,您可以在尝试创建新用户之前显式检查具有给定用户名的用户是否已存在。因此,您可以让Hibernate处理所有SQLExceptions并将它们转换为运行时异常。
这通常通过依赖注入库注入事务管理代码来完成。最受欢迎的DI库是Spring。
答案 2 :(得分:0)
我最终做的是将以下行添加到每个catch块:
session.clear();
,根据文件,
彻底清除会话。退出所有已加载的实例并取消所有挂起的保存,更新和删除。不要关闭打开的迭代器或ScrollableResults的实例。
结果是会话返回到可用状态,并且以前的错误不会影响同一会话的未来使用。
当然,这并不能解决任何有关正确划分事务范围的问题,Olaf和Bozho正确解释的问题最好在任何特定DAO范围之外处理。