我在使用纯Servlet和JSP开发的Web应用程序中使用Hibernate。我正面临一个大麻烦"有时"当我执行代码。发生的事情是我从Hibernate收到Too many Connections
错误。
我经历了很多寻求答案的Stackoverflow问题,并找到了不同的解决方案。有人建议使用第三方池系统,有些建议使用线程安全,有些人建议使用一个SessionFactory
等,因此我不确定哪一个适用于我的。
以下是我的数据库层的一部分。
package dao;
import java.util.List;
import model.main.Familyvisa;
import model.main.Familyvisa;
import model.main.Familyvisa;
import model.main.Pensionhistory;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
/**
*
* @author user
*/
public class FamilyVisaImpl implements FamilyVisaInterface
{
private Session currentSession;
private Transaction currentTransaction;
public Session openCurrentSession() {
currentSession = getSessionFactory().openSession();
return currentSession;
}
public Session openCurrentSessionwithTransaction() {
currentSession = getSessionFactory().openSession();
currentTransaction = currentSession.beginTransaction();
return currentSession;
}
public void closeCurrentSession() {
currentSession.close();
}
public void closeCurrentSessionwithTransaction() {
currentTransaction.commit();
currentSession.close();
}
private static SessionFactory getSessionFactory() {
Configuration configuration = new Configuration().configure();
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties());
SessionFactory sessionFactory = configuration.buildSessionFactory(builder.build());
return sessionFactory;
}
public Session getCurrentSession() {
return currentSession;
}
public void setCurrentSession(Session currentSession) {
this.currentSession = currentSession;
}
public Transaction getCurrentTransaction() {
return currentTransaction;
}
public void setCurrentTransaction(Transaction currentTransaction) {
this.currentTransaction = currentTransaction;
}
@Override
public void save(Familyvisa entity) {
getCurrentSession().save(entity);
}
@Override
public void update(Familyvisa entity) {
getCurrentSession().update(entity);
}
@Override
public Familyvisa findById(int id) {
Familyvisa book = (Familyvisa) getCurrentSession().get(Familyvisa.class, id);
return book;
}
@Override
public void delete(Familyvisa entity) {
getCurrentSession().delete(entity);
}
@Override
public List<Familyvisa> findAll() {
List<Familyvisa> remDur = (List<Familyvisa>) getCurrentSession().createQuery("from Familyvisa").list();
return remDur;
}
public Familyvisa findByForiegnKey_Family(int idFamily)
{
String hql = "FROM Familyvisa WHERE idFamily = :famId";
//String hql = "FROM Visa WHERE idFamily = :famId";
Query q = getCurrentSession().createQuery(hql);
q.setParameter("famId", idFamily);
Familyvisa v = new Familyvisa();
if(!q.list().isEmpty())
{
v = (Familyvisa)q.list().get(0);
}
return v;
}
@Override
public void saveOrUpdate(Familyvisa p)
{
getCurrentSession().saveOrUpdate(p);
}
@Override
public List<Object[]> findReminderActiveVisaWithFamilyAndEmployee()
{
String sql = "";
SQLQuery createSQLQuery = getCurrentSession().createSQLQuery(sql);
return createSQLQuery.list();
}
@Override
public void batchUpdate(List<Familyvisa> list)
{
for(int i=0;i<list.size();i++)
{
getCurrentSession().update(list.get(i));
}
}
}
以下是与上述代码相关的服务层。
package service;
import dao.FamilyVisaImpl;
import java.util.List;
import model.main.Familyvisa;
/**
*
* @author user
*/
public class FamilyVisaService
{
private FamilyVisaImpl familyVisaImpl;
public FamilyVisaService()
{
familyVisaImpl = new FamilyVisaImpl();
}
public Familyvisa findByForiegnKey_Family(int idFamily)
{
familyVisaImpl.openCurrentSession();
Familyvisa findByForiegnKey_Family = familyVisaImpl.findByForiegnKey_Family(idFamily);
familyVisaImpl.closeCurrentSession();
return findByForiegnKey_Family;
}
public List<Object[]> findReminderActiveVisaWithFamilyAndEmployee()
{
familyVisaImpl.openCurrentSession();
List<Object[]> visa = familyVisaImpl.findReminderActiveVisaWithFamilyAndEmployee();
familyVisaImpl.closeCurrentSession();
return visa;
}
public void batchUpdate(List<Familyvisa> list)
{
familyVisaImpl.openCurrentSessionwithTransaction();
familyVisaImpl.batchUpdate(list);
familyVisaImpl.closeCurrentSessionwithTransaction();
}
}
以下是来自Servlet的代码,它解释了我如何执行代码。
private void updateDatabase(List<VisaWithFamilyAndEmployeeBean> reminderSentList)
{
FamilyVisaService service = new FamilyVisaService();
List<Familyvisa> visa = new ArrayList<Familyvisa>();
for(int i=0;i<reminderSentList.size();i++)
{
Familyvisa familyVisa = service.findByForiegnKey_Family(reminderSentList.get(i).getIdFamily());
familyVisa.setNumberOfReminders(familyVisa.getNumberOfReminders()+1);
familyVisa.setLastReminderSent(Common.getCurrentDateSQL());
visa.add(familyVisa);
}
service.batchUpdate(visa);
}
我在三层(servlet,DAO,Service)中有很多类,并且都遵循完全相同的结构,用于不同的目的,但方法看起来几乎相同(如更新,插入等)。
请注意代码,关键词,访问说明符的使用等。在其他一些类中,在服务层,我将它的IMPL定义为static
,例如: private static EmployeeImpl employeeimpl;
你能找到这里发生的错误吗?因为它只发生&#34;有时&#34;并且在任何代码中(不仅在这里,但其他类也相同,唯一的区别是它们调用不同的表)所以我可以搞清楚。
更新
考虑到评论和答案,我将代码更改为以下内容。请告诉我它是否符合质量要求。
FamilyVisaService service = new FamilyVisaService();
Session session = service.openCurrentSession(); //This method will call openCurrentSession() in Impl class
try {
for(int i=0;i<reminderSentList.size();i++)
{
/* findByForiegnKey_Family() has Session argument now! */
Familyvisa familyVisa = service.findByForiegnKey_Family(session, reminderSentList.get(i).getIdFamily());
familyVisa.setNumberOfReminders(familyVisa.getNumberOfReminders()+1);
familyVisa.setLastReminderSent(Common.getCurrentDateSQL());
visa.add(familyVisa);
}
} catch (Exception ex) {
System.out.println("ERROR:"+ex);
} finally {
session.close();
}
答案 0 :(得分:1)
你的代码在很多方面都是错误的:
代码不是线程安全的,正如您已经承认的那样:
private Session currentSession;
private Transaction currentTransaction;
public Session openCurrentSession() {
currentSession = getSessionFactory().openSession();
return currentSession;
}
public Session openCurrentSessionwithTransaction() {
currentSession = getSessionFactory().openSession();
currentTransaction = currentSession.beginTransaction();
return currentSession;
}
public void closeCurrentSession() {
currentSession.close();
}
public void closeCurrentSessionwithTransaction() {
currentTransaction.commit();
currentSession.close();
}
服务层单例永远不应该存储状态,因为它们是由并发请求访问的。如果您有当前正在运行的会话而第二个请求也会打开一个新会话怎么办?第一个线程永远不会有机会关闭他的会话,但它会尝试关闭最后打开的会话(例如currentSession)。
Session
甚至不是线程安全的,因此您将遇到各种奇怪的并发修改或更改可见性错误。
您应该遵循Hibernate会话管理最佳实践,并选择由ThreadLocal会话存储支持的session-per-request解决方案。
添加Spring Transaction Management是处理连接/会话/事务管理的一种简单有效的方法。
答案 1 :(得分:1)
您的代码片段:
for(int i=0;i<reminderSentList.size();i++)
{
Familyvisa familyVisa = service.findByForiegnKey_Family(reminderSentList.get(i).getIdFamily());
familyVisa.setNumberOfReminders(familyVisa.getNumberOfReminders()+1);
familyVisa.setLastReminderSent(Common.getCurrentDateSQL());
visa.add(familyVisa);
}
在使用service.findByForeignKey_Family()
函数执行期间多次打开和关闭循环内的会话。
会话开放和结束可能需要一些时间,但循环足够快。这就是为什么多个会话可以开放的原因:它只需要关闭时间。在你的代码中,它是实际的。这就是为什么&#34;太多连接&#34;发生错误。
换句话说,将session
传递给service.findByForiegnKey_Family()
作为参数,而不是打开和关闭此内部函数。
像这样:
Session session = ...
try {
for(int i=0;i<reminderSentList.size();i++)
{
/* findByForiegnKey_Family() has Session argument now! */
Familyvisa familyVisa = service.findByForiegnKey_Family(session, reminderSentList.get(i).getIdFamily());
familyVisa.setNumberOfReminders(familyVisa.getNumberOfReminders()+1);
familyVisa.setLastReminderSent(Common.getCurrentDateSQL());
visa.add(familyVisa);
}
} catch (Exception ex) {
System.out.println("ERROR:"+ex);
} finally {
session.close();
}
以上示例是线程安全的。因为您在单个函数内打开,操作和关闭会话。
即使是读取操作,Hibernate也需要事务块。所以你必须修改你的代码:
Session session = ...
try {
session.beginTransaction();
...
Your Loop
...
session.getTransaction.commit();
...