简体   繁体   中英

Hibernate opening a bunch of connections with MySQL

I've got another weird Hibernate issue. I've googled and searched on SO for an answer, but couldn't find anything that I understood.

When one person navigates to our home page, it opens anywhere from 1 to 59 connections/sessions. It is never consistent. They usually don't close, but sometimes do. It's not noticeably consistent in that respect either. My attempt to use getStatistics returns nothing but zeroes.

DAO.java

package com.grandcircus.spring.util;

import com.grandcircus.spring.controller.HomeController;
import com.grandcircus.spring.models.FamiliesEntity;
import com.grandcircus.spring.models.UsersEntity;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Restrictions;
import org.hibernate.stat.Statistics;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;

/**
 * Class description
 *
 * @author Sarah Guarino
 * @version 1.0
 */

@Repository
@Transactional
public class DAO {
    private static Configuration configurationObject = new Configuration().configure("hibernate.cfg.xml");
    private static SessionFactory sessionFactory = configurationObject.buildSessionFactory();

    public static FamiliesEntity newFamily(String famName) {
        Session browsingSession = sessionFactory.openSession();
        Transaction databaseTransaction = browsingSession.beginTransaction();

        FamiliesEntity newFamily = new FamiliesEntity();
        newFamily.setName(famName);

        browsingSession.save(newFamily);
        databaseTransaction.commit();
        browsingSession.close();

        return newFamily;
    }

    public static void newUser(String fName, String lName,
                               String email, String password,
                               int usergroup, int familyid) {
        Session browsingSession = sessionFactory.openSession();
        Transaction databaseTransaction = browsingSession.beginTransaction();
        UsersEntity user = new UsersEntity();

        user.setFname(fName);
        user.setLname(lName);
        user.setEmail(email);
        user.setUsergroup(usergroup);
        user.setPassword(password);
        user.setFamilyid(familyid);

        browsingSession.save(user);
        databaseTransaction.commit();
        browsingSession.close();
    }

    public static void updateUserCoordinates(String checkinLat,
                                             String checkinLong,
                                             String userId) {
        Session browsingSession = sessionFactory.openSession();
        Transaction myTransaction = browsingSession.beginTransaction();

        Criteria criteria = browsingSession.createCriteria(UsersEntity.class);
        UsersEntity personCheckingIn = (UsersEntity) criteria
                .add(Restrictions.eq("userid", Integer.parseInt(userId)))
                .uniqueResult();

        personCheckingIn.setLastlat(checkinLat);
        personCheckingIn.setLastlong(checkinLong);
        personCheckingIn.setLasttime(HomeController.getCurrentTime());

        browsingSession.save(personCheckingIn);
        myTransaction.commit();
        browsingSession.close();
    }

    public static boolean doesUserExist(String email) {
        // this will pass if the email exists, or fail if the user does not exist.
        boolean doesThisExist = true;
        Session browsingSession = sessionFactory.openSession();
        Criteria usersCriteria = browsingSession.createCriteria(UsersEntity.class);

        try {
            UsersEntity newUser = (UsersEntity) usersCriteria
                    .add(Restrictions.eq("email", email))
                    .uniqueResult();
            if(newUser.getEmail() == null) {
                doesThisExist = false;
            }
        } catch (Exception e) {
            doesThisExist = false;
        } finally {
            browsingSession.close();
        }

        return doesThisExist;
    }

    public static boolean doesFamilyExist(int famId) {
        boolean doesThisExist = true;
        Session browsingSession = sessionFactory.openSession();
        Criteria familyCriteria = browsingSession.createCriteria(FamiliesEntity.class);

        try {
            FamiliesEntity family = (FamiliesEntity) familyCriteria
                    .add(Restrictions.eq("familyid", famId))
                    .uniqueResult();
            if(family.getFamilyid() == 0) {
               doesThisExist = false;
            }
        } catch (NullPointerException e) {
            doesThisExist = false;
        } finally {
            browsingSession.close();
        }

        return doesThisExist;
    }

    public static UsersEntity getUserByEmail(String email) {
        Session browsingSession = sessionFactory.openSession();
        Criteria userCriteria = browsingSession.createCriteria(UsersEntity.class);

        UsersEntity user = (UsersEntity) userCriteria
                .add(Restrictions.eq("email", email))
                .uniqueResult();

        browsingSession.close();

        return user;
    }

    public static UsersEntity loadThisAccount(String userId) {
        Session browsingSession = sessionFactory.openSession();
        Criteria userCriteria = browsingSession.createCriteria(UsersEntity.class);

        UsersEntity user = (UsersEntity) userCriteria
                .add(Restrictions.eq("userid",
                        Integer.parseInt(userId)))
                .uniqueResult();

        browsingSession.close();

        return user;
    }

    public static ArrayList<UsersEntity> loadChildAccounts(int familyId) {
        Session browsingSession = sessionFactory.openSession();

        Criteria childCriteria = browsingSession.createCriteria(UsersEntity.class);

        ArrayList<UsersEntity> children = (ArrayList<UsersEntity>) childCriteria
                .add(Restrictions.eq("familyid", familyId))
                .add(Restrictions.eq("usergroup", 1))
                .list();

        browsingSession.close();

        return children;
    }

    public static FamiliesEntity loadFamily(int familyId) {
        Session browsingSession = sessionFactory.openSession();
        Criteria familyCriteria = browsingSession.createCriteria(FamiliesEntity.class);

        FamiliesEntity family = (FamiliesEntity) familyCriteria
                .add(Restrictions.eq("familyid", familyId))
                .uniqueResult();

        browsingSession.close();

        return family;
    }

    public static UsersEntity loadParentAccount(int familyId) {
        Session browsingSession = sessionFactory.openSession();
        Criteria adminCriteria = browsingSession.createCriteria(UsersEntity.class);
        UsersEntity parent = (UsersEntity) adminCriteria
                .add(Restrictions.eq("familyid", familyId))
                .add(Restrictions.eq("usergroup", 0))
                .uniqueResult();

        browsingSession.close();

        return parent;
    }
}

Hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="connection.url">jdbc:mysql://localhost:3306/checkin</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>

        <property name="hibernate.connection.username">*****</property>
        <property name="hibernate.connection.password">*****</property>

        <property name="hibernate.c3p0.min_size">5</property>
        <property name="hibernate.c3p0.max_size">20</property>
        <property name="hibernate.c3p0.timeout">300</property>
        <property name="hibernate.c3p0.max_statements">50</property>
        <property name="hibernate.c3p0.idle_test_period">3000</property>

        <mapping class="com.grandcircus.spring.models.FamiliesEntity"/>
        <mapping class="com.grandcircus.spring.models.LocationsEntity"/>
        <mapping class="com.grandcircus.spring.models.UsersEntity"/>

        <mapping resource="FamiliesEntity.hbm.xml"/>
        <mapping resource="LocationsEntity.hbm.xml"/>
        <mapping resource="UsersEntity.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

You have a couple of issues here. The main issue and I think what is causing you session leaks is that in doesUserExist and doesFamilyExist you are getting NullPointerException quite often and never closing session. Here the rest of issues:

1) Your DAO uses Transactional annotation but all of methods are static. Either remove the annotation or make methods instance scope and inject session factory to the DAO. The best practice is to rely on transactional annotation and let container like Spring or EJB3 to handle transaction begin/commit calls.

2) You are not closing sessions properly, you need to wrap them in try/finally blocks and close the session in finally block. The best practice again is to rely on Spring or EJB3 to handle session management.

3) Never ever catch NullPointerException. Always check if object is null and add appropriate logic there.

Update:

Here is modified version of some of your DAO methods. This will be the easiest fix for you but I suggest to improve your DAO at some point with proper Spring, Hibernate and JPA integration:

    public static FamiliesEntity newFamily(String famName) {
        Session browsingSession = null;
        Transaction databaseTransaction = null;

        try{
            browsingSession = sessionFactory.openSession();
            databaseTransaction = browsingSession.beginTransaction();

            FamiliesEntity newFamily = new FamiliesEntity();
            newFamily.setName(famName);

            browsingSession.save(newFamily);
            databaseTransaction.commit();

            return newFamily;
        }catch(RuntimeException e){
            if (databaseTransaction != null){
                try{ databaseTransaction.rollback(); }
                catch(RuntimeException ex){throw ex;}
            }
            throw e;
        }finally{
            if (browsingSession != null){
                browsingSession.close();
            }
        }            
    }       

    public static boolean doesUserExist(String email) {
        Session browsingSession = null;

        try{
            browsingSession = sessionFactory.openSession();

            UsersEntity newUser = (UsersEntity) usersCriteria
            .add(Restrictions.eq("email", email))
            .uniqueResult();

            if(newUser.getEmail() == null) {
                return false;
            }else{
                return true;
            }                
        }finally{
            if (browsingSession != null){
                browsingSession.close();
            }
        }
    }

    public static UsersEntity getUserByEmail(String email) {
        Session browsingSession = null;

        try{
            browsingSession = sessionFactory.openSession();
            Criteria userCriteria = browsingSession.createCriteria(UsersEntity.class);

            UsersEntity user = (UsersEntity) userCriteria
                    .add(Restrictions.eq("email", email))
                    .uniqueResult();
        }finally{
            if (browsingSession != null){
                browsingSession.close();
            }
        }

        return user;
    }

Here is what has changed:

1) Session is defined outside of try block and is closed in finally block. Make sure to always check if session has actually been initialized before closing it. Otherwise in case sessionFactory.openSession() throwing exception or returning null you will get NPE in your finally block.

2) Transaction, just like session is defined outside of try block, initialized inside try and is committed inside try block. But we still need to handle the case that if any code before commit throws exception we wont keep the transaction open and that's why we need to roll it back inside catch block. Again transaction can be null in catch block due to session creation throwing exception.

建议使用连接池您不必自己打开会话

Tsolakp has great advice, and it resolved a lot of the loose connection leaks I was having. However, this was not the actual answer.

If your project is on AWS, you should also totally make sure that your WAR archive containing your database username and password weren't pushed to Git or elsewhere. This usually results in your connections getting attacked by a pretty aggressive source.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM