Слишком много ошибок соединения: Hibernate

1

Я использую Hibernate в своем веб-приложении, которое разработано с использованием чистого сервлета и JSP. Когда я выполняю код, я сталкиваюсь с большой проблемой "иногда". Что происходит, я получаю Too many Connections ошибок Too many Connections из 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), и все они соответствуют той же структуре, что и в разных целях, но методы выглядят почти такими же (как обновление, вставка и т.д.).

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

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

ОБНОВИТЬ

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

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();
  }
Теги:
database
servlets
hibernate
connection-pooling

2 ответа

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() и цикла. + отказаться от нетранзакционных операций. Вот и все
Показать ещё 11 комментариев
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();
    }
    

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

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

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

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

  • 0
    1. Это не потокобезопасно из-за переменных class level ? Но дело не в Servlet а в другом классе? 2. Не понял смысла. 3. Похоже на «Закрытие сессий внутри Finally block finally». 4. Думаю, мы опоздали. Наконец, как насчет создания SessionFactory ? Как кажется, он каждый раз открывает новые экземпляры в цикле updateDatabase
  • 0
    1. Несколько запросов могут вызывать openCurrentSession и, следовательно, устанавливать session , и каждый сеанс должен быть связан с вызывающим потоком. В вашем случае session содержит последний открытый сеанс, и в любом случае он даже не является поточно-ориентированным. 3. Даже если вы вызываете close на блок finally, вы уверены, что закрыли session который вы открыли в первую очередь?
Показать ещё 12 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню