Too Many Connections Error: Hibernate
Too Many Connections Error: Hibernate
我在使用纯 Servlet 和 JSP 开发的 Web 应用程序中使用 Hibernate。当我执行代码时,我遇到了一个大麻烦 "sometimes"。我从 Hibernate 收到 Too many Connections
错误。
我浏览了很多 Whosebug 问题来寻找答案,并且找到了不同的解决方案。有人建议使用第三方池系统,有人建议线程安全,有人建议使用一个 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、服务)中有很多 类,它们都遵循完全相同的结构,服务于不同的目的,但方法看起来几乎相同(如更新、插入等)。
请注意代码、关键字、访问说明符的使用等。在其他一些类,在服务层,我也将它的 IMPL 定义为 static
例如:private static EmployeeImpl employeeimpl;
你能找到这里出了什么问题吗?因为它只出现 "sometimes" 并且在任何代码中(不仅在这里,而且其他 类 也一样,唯一的区别是它们调用不同的表)所以我可以弄清楚。
更新
考虑到评论和答案,我将代码更改为以下。请让我知道它是否处于质量水平。
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();
}
您的代码在很多方面都是错误的:
代码不是线程安全的,正如您已经承认的那样:
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是处理connection/session/transaction管理的一种简单有效的方法。
您的代码片段:
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()
函数在循环内多次打开和关闭会话。
会话打开和关闭可能需要一些时间,但循环足够快。这就是为什么可以打开多个会话:只是需要关闭的时间。在你的代码中它是真实的。这就是 "Too Many Connections" 错误发生的原因。
换句话说,将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();
...
我在使用纯 Servlet 和 JSP 开发的 Web 应用程序中使用 Hibernate。当我执行代码时,我遇到了一个大麻烦 "sometimes"。我从 Hibernate 收到 Too many Connections
错误。
我浏览了很多 Whosebug 问题来寻找答案,并且找到了不同的解决方案。有人建议使用第三方池系统,有人建议线程安全,有人建议使用一个 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、服务)中有很多 类,它们都遵循完全相同的结构,服务于不同的目的,但方法看起来几乎相同(如更新、插入等)。
请注意代码、关键字、访问说明符的使用等。在其他一些类,在服务层,我也将它的 IMPL 定义为 static
例如:private static EmployeeImpl employeeimpl;
你能找到这里出了什么问题吗?因为它只出现 "sometimes" 并且在任何代码中(不仅在这里,而且其他 类 也一样,唯一的区别是它们调用不同的表)所以我可以弄清楚。
更新
考虑到评论和答案,我将代码更改为以下。请让我知道它是否处于质量水平。
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();
}
您的代码在很多方面都是错误的:
代码不是线程安全的,正如您已经承认的那样:
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是处理connection/session/transaction管理的一种简单有效的方法。
您的代码片段:
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()
函数在循环内多次打开和关闭会话。
会话打开和关闭可能需要一些时间,但循环足够快。这就是为什么可以打开多个会话:只是需要关闭的时间。在你的代码中它是真实的。这就是 "Too Many Connections" 错误发生的原因。
换句话说,将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();
...