为什么hibernate尝试获取多个连接?

时间:2017-06-30 15:52:04

标签: java hibernate

我之前的项目是用hibernate 4.3做的,并没有任何问题。这次我正在使用hibernate 5.2。当我收到此错误时,我正面临问题:

ERROR: Collection leak detected: there are 1 unclosed connections upon shutting down pool

经过调查,我注意到save hibernate试图打开2个连接。为什么?我一直在使用这些设置,但之前从未遇到过问题:

<property name="hibernate.connection.pool_size">1</property>

MainApp.java:

package home.accounting;

import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

import home.accounting.DA.LanguagesDA;
import home.accounting.model.Languages;
import javafx.application.Application;
import javafx.stage.Stage;

public class MainApp extends Application {
    private SessionFactory sessionFactory;

    @Override
    public void start(Stage primaryStage) {

        LanguagesDA.setMainApp(this);

        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        try {
            sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
            LanguagesDA.save(new Languages("english","en"));
        }
        catch (Exception e) {
            // The registry would be destroyed by the SessionFactory, but we had trouble building the SessionFactory
            // so destroy it manually.
            StandardServiceRegistryBuilder.destroy( registry );
        }
    }

     /**
     * Initialises the root layout.
     */
    public void initRootLayout() {

    }

    public static void main(String[] args) {
        launch(args);
    }

    public SessionFactory getSessionFactory(){
        return sessionFactory;
    }

    @Override
    public void stop() throws Exception {
        sessionFactory.close();
    }
}

LanguagesDA.java

package home.accounting.DA;

import org.hibernate.Session;
import org.hibernate.Transaction;

import home.accounting.MainApp;
import home.accounting.model.Languages;

public class LanguagesDA {

    private static MainApp mainApp;

    public static void save(Languages language) {
        Session sess = mainApp.getSessionFactory().openSession();
        System.out.println("Session opened!");
        Transaction tx = null;
         try {
             tx = sess.beginTransaction();
             sess.save(language);
             tx.commit();
         }
         catch (Exception e) {
             if (tx!=null) tx.rollback();
             System.out.println(e);
             throw e;
         }
         finally {
             sess.close();
             System.out.println("Session closed!");
         }
    }

    public static void setMainApp(MainApp app) {
        mainApp = app;
    }
}

Languages.java

package home.accounting.model;
import javax.persistence.*;
@Entity
@Table(name="languages")
public class Languages {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="language_id")
    private int id;

    @Column(name="language_name")
    private String name;

    @Column(name="language_code")
    private String code;

    public Languages(){}

    public Languages(String name, String code){
        this.name = name;
        this.code = code;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }

}

hibernate.cfg.xml中

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="hibernate.connection.driver_class">org.sqlite.JDBC</property>
        <property name="hibernate.connection.url">jdbc:sqlite:src/home/accounting/DB/Accounting.sqlite</property>
        <property name="hibernate.connection.username"></property>
        <property name="hibernate.connection.password"></property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="hibernate.connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="hibernate.dialect">org.hibernate.dialect.SQLiteDialect</property>

        <!-- Disable the second-level cache  -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="hibernate.show_sql">true</property>

        <property name="hibernate.format_sql">false</property>
        <property name="hibernate.connection.release_mode">on_close</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hibernate.hbm2ddl.auto">none</property>

        <!-- Names the annotated entity class -->
        <mapping class="home.accounting.model.Languages"/>

    </session-factory>

</hibernate-configuration>

SQLiteDialect.java

/*
 * The author disclaims copyright to this source code.  In place of
 * a legal notice, here is a blessing:
 *
 *    May you do good and not evil.
 *    May you find forgiveness for yourself and forgive others.
 *    May you share freely, never taking more than you give.
 *
 */
package org.hibernate.dialect;

import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.JDBCException;
import org.hibernate.ScrollMode;
import org.hibernate.dialect.function.AbstractAnsiTrimEmulationFunction;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.SQLiteDialectIdentityColumnSupport;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.dialect.unique.DefaultUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.type.StandardBasicTypes;

/**
 * An SQL dialect for SQLite 3.
 */
public class SQLiteDialect extends Dialect {
    private final UniqueDelegate uniqueDelegate;

    public SQLiteDialect() {
        registerColumnType( Types.BIT, "boolean" );
        //registerColumnType(Types.FLOAT, "float");
        //registerColumnType(Types.DOUBLE, "double");
        registerColumnType( Types.DECIMAL, "decimal" );
        registerColumnType( Types.CHAR, "char" );
        registerColumnType( Types.LONGVARCHAR, "longvarchar" );
        registerColumnType( Types.TIMESTAMP, "datetime" );
        registerColumnType( Types.BINARY, "blob" );
        registerColumnType( Types.VARBINARY, "blob" );
        registerColumnType( Types.LONGVARBINARY, "blob" );

        registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "", "||", "" ) );
        registerFunction( "mod", new SQLFunctionTemplate( StandardBasicTypes.INTEGER, "?1 % ?2" ) );
        registerFunction( "quote", new StandardSQLFunction( "quote", StandardBasicTypes.STRING ) );
        registerFunction( "random", new NoArgSQLFunction( "random", StandardBasicTypes.INTEGER ) );
        registerFunction( "round", new StandardSQLFunction( "round" ) );
        registerFunction( "substr", new StandardSQLFunction( "substr", StandardBasicTypes.STRING ) );
        registerFunction( "trim", new AbstractAnsiTrimEmulationFunction() {
            protected SQLFunction resolveBothSpaceTrimFunction() {
                return new SQLFunctionTemplate( StandardBasicTypes.STRING, "trim(?1)" );
            }

            protected SQLFunction resolveBothSpaceTrimFromFunction() {
                return new SQLFunctionTemplate( StandardBasicTypes.STRING, "trim(?2)" );
            }

            protected SQLFunction resolveLeadingSpaceTrimFunction() {
                return new SQLFunctionTemplate( StandardBasicTypes.STRING, "ltrim(?1)" );
            }

            protected SQLFunction resolveTrailingSpaceTrimFunction() {
                return new SQLFunctionTemplate( StandardBasicTypes.STRING, "rtrim(?1)" );
            }

            protected SQLFunction resolveBothTrimFunction() {
                return new SQLFunctionTemplate( StandardBasicTypes.STRING, "trim(?1, ?2)" );
            }

            protected SQLFunction resolveLeadingTrimFunction() {
                return new SQLFunctionTemplate( StandardBasicTypes.STRING, "ltrim(?1, ?2)" );
            }

            protected SQLFunction resolveTrailingTrimFunction() {
                return new SQLFunctionTemplate( StandardBasicTypes.STRING, "rtrim(?1, ?2)" );
            }
        } );
        uniqueDelegate = new SQLiteUniqueDelegate( this );
    }

    // database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public String getCastTypeName(int code) {
        // FIXME
        return super.getCastTypeName( code );
    }

    // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    private static final SQLiteDialectIdentityColumnSupport IDENTITY_COLUMN_SUPPORT = new SQLiteDialectIdentityColumnSupport();
    @Override
    public IdentityColumnSupport getIdentityColumnSupport() {
        return IDENTITY_COLUMN_SUPPORT;
    }

    // limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    private static final AbstractLimitHandler LIMIT_HANDLER = new AbstractLimitHandler() {
        @Override
        public String processSql(String sql, RowSelection selection) {
            final boolean hasOffset = LimitHelper.hasFirstRow( selection );
            return sql + (hasOffset ? " limit ? offset ?" : " limit ?");
        }

        @Override
        public boolean supportsLimit() {
            return true;
        }

        @Override
        public boolean bindLimitParametersInReverseOrder() {
            return true;
        }
    };

    @Override
    public LimitHandler getLimitHandler() {
        return LIMIT_HANDLER;
    }

    // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    @Override
    public boolean supportsLockTimeouts() {
        // may be http://sqlite.org/c3ref/db_mutex.html ?
        return false;
    }

    @Override
    public String getForUpdateString() {
        return "";
    }

    @Override
    public boolean supportsOuterJoinForUpdate() {
        return false;
    }

  /*
    @Override
  public boolean dropTemporaryTableAfterUse() {
    return true; // temporary tables are only dropped when the connection is closed. If the connection is pooled...
  }
  */

    // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public boolean supportsCurrentTimestampSelection() {
        return true;
    }

    public boolean isCurrentTimestampSelectStringCallable() {
        return false;
    }

    @Override
    public String getCurrentTimestampSelectString() {
        return "select current_timestamp";
    }

    // SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    private static final int SQLITE_BUSY = 5;
    private static final int SQLITE_LOCKED = 6;
    private static final int SQLITE_IOERR = 10;
    private static final int SQLITE_CORRUPT = 11;
    private static final int SQLITE_NOTFOUND = 12;
    private static final int SQLITE_FULL = 13;
    private static final int SQLITE_CANTOPEN = 14;
    private static final int SQLITE_PROTOCOL = 15;
    private static final int SQLITE_TOOBIG = 18;
    private static final int SQLITE_CONSTRAINT = 19;
    private static final int SQLITE_MISMATCH = 20;
    private static final int SQLITE_NOTADB = 26;

    @Override
    public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
        return new SQLExceptionConversionDelegate() {
            @Override
            public JDBCException convert(SQLException sqlException, String message, String sql) {
                final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
                if (errorCode == SQLITE_TOOBIG || errorCode == SQLITE_MISMATCH) {
                    return new DataException( message, sqlException, sql );
                }
                else if (errorCode == SQLITE_BUSY || errorCode == SQLITE_LOCKED) {
                    return new LockAcquisitionException( message, sqlException, sql );
                }
                else if ((errorCode >= SQLITE_IOERR && errorCode <= SQLITE_PROTOCOL) || errorCode == SQLITE_NOTADB) {
                    return new JDBCConnectionException( message, sqlException, sql );
                }

                // returning null allows other delegates to operate
                return null;
            }
        };
    }

    public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
        return EXTRACTER;
    }

    private static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
        @Override
        protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
            final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
            if (errorCode == SQLITE_CONSTRAINT) {
                return extractUsingTemplate( "constraint ", " failed", sqle.getMessage() );
            }
            return null;
        }
    };

    // union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public boolean supportsUnionAll() {
        return true;
    }

    // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public boolean canCreateSchema() {
        return false;
    }

    @Override
    public boolean hasAlterTable() {
        // As specified in NHibernate dialect
        return false;
    }

    @Override
    public boolean dropConstraints() {
        return false;
    }

    @Override
    public boolean qualifyIndexName() {
        return false;
    }

    @Override
    public String getAddColumnString() {
        return "add column";
    }

    @Override
    public String getDropForeignKeyString() {
        throw new UnsupportedOperationException( "No drop foreign key syntax supported by SQLiteDialect" );
    }

    @Override
    public String getAddForeignKeyConstraintString(String constraintName,
            String[] foreignKey, String referencedTable, String[] primaryKey,
            boolean referencesPrimaryKey) {
        throw new UnsupportedOperationException( "No add foreign key syntax supported by SQLiteDialect" );
    }

    @Override
    public String getAddPrimaryKeyConstraintString(String constraintName) {
        throw new UnsupportedOperationException( "No add primary key syntax supported by SQLiteDialect" );
    }

    @Override
    public boolean supportsCommentOn() {
        return true;
    }

    @Override
    public boolean supportsIfExistsBeforeTableName() {
        return true;
    }

  /* not case insensitive for unicode characters by default (ICU extension needed)
    public boolean supportsCaseInsensitiveLike() {
    return true;
  }
  */

    @Override
    public boolean doesReadCommittedCauseWritersToBlockReaders() {
        // TODO Validate (WAL mode...)
        return true;
    }

    public boolean doesRepeatableReadCauseReadersToBlockWriters() {
        return true;
    }

    @Override
    public boolean supportsTupleDistinctCounts() {
        return false;
    }

    public int getInExpressionCountLimit() {
        // Compile/runtime time option: http://sqlite.org/limits.html#max_variable_number
        return 1000;
    }

    @Override
    public UniqueDelegate getUniqueDelegate() {
        return uniqueDelegate;
    }
    private static class SQLiteUniqueDelegate extends DefaultUniqueDelegate {
        public SQLiteUniqueDelegate(Dialect dialect) {
            super( dialect );
        }
        @Override
        public String getColumnDefinitionUniquenessFragment(Column column) {
            return " unique";
        }
    }

    @Override
    public String getSelectGUIDString() {
        return "select hex(randomblob(16))";
    }

    @Override
    public ScrollMode defaultScrollMode() {
        return ScrollMode.FORWARD_ONLY;
    }
}

SQLiteDialectIdentityColumnSupport.java

package org.hibernate.dialect.identity;

public class SQLiteDialectIdentityColumnSupport extends IdentityColumnSupportImpl {
    @Override
    public boolean supportsIdentityColumns() {
        return true;
    }

  /*
    public boolean supportsInsertSelectIdentity() {
    return true; // As specified in NHibernate dialect
  }
  */

    @Override
    public boolean hasDataTypeInIdentityColumn() {
        // As specified in NHibernate dialect
        // FIXME true
        return false;
    }

  /*
    public String appendIdentitySelectToInsert(String insertString) {
    return new StringBuffer(insertString.length()+30). // As specified in NHibernate dialect
      append(insertString).
      append("; ").append(getIdentitySelectString()).
      toString();
  }
  */

    @Override
    public String getIdentitySelectString(String table, String column, int type) {
        return "select last_insert_rowid()";
    }

    @Override
    public String getIdentityColumnString(int type) {
        // return "integer primary key autoincrement";
        // FIXME "autoincrement"
        return "integer";
    }
}

我的语言表格结构:

CREATE TABLE "languages"(
language_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
language_name VARCHAR(50) NOT NULL,
language_code VARCHAR(3) NOT NULL)

通过使用try catch块,我设法得到了这一行:

Session opened!
org.hibernate.HibernateException: The internal connection pool has reached its maximum size and no connection is currently available!
Jun 30, 2017 4:55:50 PM org.hibernate.engine.internal.StatisticalLoggingSessionEventListener end
INFO: Session Metrics {
    72982 nanoseconds spent acquiring 2 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    0 nanoseconds spent preparing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}

使用的库sqlite-jdbc-3.16.1.jar和hibernate 5.2.10

1 个答案:

答案 0 :(得分:1)

我最终通过将@GeneratedValue(strategy = GenerationType.AUTO)替换为@GeneratedValue(strategy = GenerationType.IDENTITY)来解决了这个问题。