2015-03-23 3 views
1

Я использую Hibernate в своем веб-приложении, которое разработано с использованием чистого сервлета и JSP. Когда я выполняю код, я сталкиваюсь с большой проблемой «иногда». Что происходит, я получаю ошибку Too many Connections из Hibernate.Слишком много соединений Ошибка: Hibernate

Я прошел через множество вопросов, связанных с 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(); 
    } 
} 

Ниже приведен код сервлета, в котором объясняется, как я выполняю код.

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); 
    } 

У меня есть много классов в трех слоях (сервлет, DAO, Service) и все следует точно такой же структуре, обслуживая разные цели, но метода выглядит почти так же (как обновление, вставка и т.д.).

Пожалуйста, обратите некоторые достойные внимания к коду, ключевым словам, использование спецификаторов доступа и т.д. В некоторых других классах, в уровне услуг, я определяю это осуще в static, а например: private static EmployeeImpl employeeimpl;

Можете ли вы найти то, что здесь происходит неправильно? Так как это происходит только «иногда» и в любом из кода (не только здесь, но и в других классах, то только различие в том, что они обращаются к разным таблицам), поэтому я могу понять это.

UPDATE

Учитывая замечания и ответы, я изменил код ниже. Пожалуйста, дайте мне знать, находится ли он на уровне качества.

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(); 
    } 

ответ

2

Ваш фрагмент кода:

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() функции.

Снятие и закрытие сеанса может занять некоторое время, но цикл достаточно быстрый. Вот почему можно было открыть несколько сеансов: просто нужно закрыть время. И в вашем коде это актуально. Вот почему возникает ошибка «Слишком много подключений».

Другими словами, передайте 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(); 
... 
+0

Интересно, но похоже, что я должен внести массу изменений? –

+0

@JustCause, на самом деле вам нужно просто открыть и закрыть сеанс вне функции .findByForiegnKey_Family() 'и цикла. + отказаться от операций без транзакций. Это все – WildDev

+0

Хорошо, не могли бы вы сделать его потокобезопасным? –

1

Ваш код неправильно во многих отношениях:

  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(); 
    } 
    

    служебный слой одиночек никогда не должен хранить state, потому что к ним обращаются параллельные запросы. Что делать, если у вас текущий сеанс сеанса, а второй запрос также открывает новый сеанс? В первом потоке никогда не будет возможности закрыть сеанс, но он попытается закрыть последний открытый сеанс (например, currentSession).

  2. Session не является даже потокобезопасным, поэтому вы столкнетесь со всеми видами странных параллельных изменений или изменений видимости видимости.

  3. Вы должны следовать рекомендациям управления сеансом Hibernate и выбирать для решения session-per-request, поддерживаемого хранилищем SessionLocal Session.

  4. Добавление Spring Transaction Management - простой и эффективный способ обработки управления соединением/сеансом/транзакциями.

+0

1. Это не потокобезопасность из-за переменных класса? Но это не в «Сервлетах», а в другом классе? 2. Не понял. 3. Похоже, что «Закрытие сессий внутри« Наконец-то »? 4. Думаю, мы опоздали. Наконец, как насчет создания' SessionFactory'? Кажется, он открывает новые экземпляры каждый раз в цикле из 'updateDatabase' –

+0

1. Несколько запросов могут вызывать' openCurrentSession' и поэтому устанавливать 'session', и каждый сеанс должен быть привязан к вызывающему потоку. В вашем случае' session' содержит последний открытый сеанс, и это даже не в любом случае. 3. Даже если вы закроете окончательный блок, вы уверены, что закрываете «сеанс», который вы открыли в первую очередь? –

+0

«1. Несколько запросов могут вызывать openCurrentSession и поэтому устанавливать сеанс, и каждый сессия должна быть привязана к вызывающему потоку' - Как я могу это сделать? .. И PLS проверить 'SessionFactory', а также, я определяю его внутри metho d .. –

Смежные вопросы