JPA、Hibernate 与 c3p0 和 Postgres。检测数据库连接问题
JPA, Hibernate with c3p0 and Postgres. Detect database connectivity issues
我正在编写一个应用程序,它使用 c3p0 池通过 Hibernate 连接到 Postgres DB。在主界面出现之前,我想检测任何有效的数据库连接设置和可能的数据库连接。如果设置无效,我想向用户显示一条消息并建议更改设置或关闭应用程序。
但问题是 EntityManagerFactory
不会抛出异常甚至 return 连接不成功后
这是一个代码示例,它会因错误的连接设置而产生错误:
public void connect(ConnectionSettingsModel conSet) throws Exception {
Map<String, String> connectionProperties = new HashMap<>();
connectionProperties.put("javax.persistence.jdbc.url", conSet.getUrl());
connectionProperties.put("javax.persistence.jdbc.user", conSet.getUser());
connectionProperties.put("javax.persistence.jdbc.password", conSet.getPassword());
connectionProperties.put("hibernate.default_schema", conSet.getSchema());
System.out.println("Before creating EM");
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("postgres-connect", connectionProperties);
EntityManager entityManager = entityManagerFactory.createEntityManager();
System.out.println("After creating EM");
}
c3p0 配置在 persistence.xml
:
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
<property name="hibernate.c3p0.min_size" value="0"/>
<property name="hibernate.c3p0.max_size" value="10"/>
<property name="hibernate.c3p0.timeout" value="300"/>
<property name="hibernate.c3p0.idle_test_period" value="3000"/>
<property name="hibernate.c3p0.max_statements" value="50"/>
<property name="hibernate.c3p0.acquireRetryAttempts" value="1"/>
用户名不存在的日志示例:
Before creating EM
14:47:07,009 INFO [com.mchange.v2.log.MLog] - MLog clients using log4j logging.
14:47:07,239 INFO [com.mchange.v2.c3p0.C3P0Registry] - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
14:47:07,303 INFO [com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource] - Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@e945fe41 [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@d1b84bda [ acquireIncrement -> 3, acquireRetryAttempts -> 1, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, identityToken -> 1hge3hi9nk1ku80noopkx|3185ce3, idleConnectionTestPeriod -> 3000, initialPoolSize -> 0, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 300, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 50, maxStatementsPerConnection -> 0, minPoolSize -> 0, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@9480ea7b [ description -> null, driverClass -> null, factoryClassLocation -> null, forceUseNamedDriverClass -> false, identityToken -> 1hge3hi9nk1ku80noopkx|26e664, jdbcUrl -> jdbc:postgresql://localhost:5432/postgres, properties -> {user=******, password=******} ], preferredTestQuery -> null, privilegeSpawnedThreads -> false, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, extensions -> {}, factoryClassLocation -> null, identityToken -> 1hge3hi9nk1ku80noopkx|3d02a858, numHelperThreads -> 3 ]
квіт. 07, 2017 2:47:07 PM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "user"
--code omitted
квіт. 07, 2017 2:47:07 PM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "user"
--code omitted
-- few attempts to connect
14:47:55,009 WARN [com.mchange.v2.resourcepool.BasicResourcePool] - Having failed to acquire a resource, com.mchange.v2.resourcepool.BasicResourcePool@4519c236 is interrupting all Threads waiting on a resource to check out. Will try again in response to new client requests.
在此消息之后,它不会抛出任何异常或 return
您可以考虑修改
<property name="hibernate.c3p0.acquireRetryAttempts" value="1"/>
回到默认值 30(或更高),然后尝试
<property name="hibernate.c3p0.breakAfterAcquireFailure" value="true"/>
然后,如果一个完整的尝试从数据库获取连接的周期失败(同样,这应该不止一次,否则您的应用程序将非常脆弱),您的 c3p0 DataSource
将简单地中断, 进一步尝试检查 Connection
将立即失败。这样做的缺点是,在临时网络或数据库中断后,您将失去 c3p0 的容量 "self-heal"。如果临时中断导致采集周期失败,您将不得不自己重建数据源(或重新启动您的应用程序)。
如果您想要两全其美,请将 hibernate.c3p0.acquireRetryAttempts
设置回 30-ish,将 hibernate.c3p0.breakAfterAcquireFailure
保留为默认值 false
,但编写您自己的自定义代码进行测试数据库的可用性。修改上面的测试 connect()
函数(需要重新组织,您只想创建一次 EntityManagerFactory
),可能很简单...
public void connect(ConnectionSettingsModel conSet) throws Exception {
try( Connection con = DriverManager.getConnection( conSet.getUrl(), conSet.getUser(), conSet.getPassword() ) ) {
/* Nothing to do here, really... */
} catch ( Exception e ) {
System.out.println("Trouble connecting with DBMS, please check database url, username, and password.");
throw e;
}
Map<String, String> connectionProperties = new HashMap<>();
connectionProperties.put("javax.persistence.jdbc.url", conSet.getUrl());
connectionProperties.put("javax.persistence.jdbc.user", conSet.getUser());
connectionProperties.put("javax.persistence.jdbc.password", conSet.getPassword());
connectionProperties.put("hibernate.default_schema", conSet.getSchema());
System.out.println("Before creating EM");
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("postgres-connect", connectionProperties);
EntityManager entityManager = entityManagerFactory.createEntityManager();
System.out.println("After creating EM");
}
我正在编写一个应用程序,它使用 c3p0 池通过 Hibernate 连接到 Postgres DB。在主界面出现之前,我想检测任何有效的数据库连接设置和可能的数据库连接。如果设置无效,我想向用户显示一条消息并建议更改设置或关闭应用程序。
但问题是 EntityManagerFactory
不会抛出异常甚至 return 连接不成功后
这是一个代码示例,它会因错误的连接设置而产生错误:
public void connect(ConnectionSettingsModel conSet) throws Exception {
Map<String, String> connectionProperties = new HashMap<>();
connectionProperties.put("javax.persistence.jdbc.url", conSet.getUrl());
connectionProperties.put("javax.persistence.jdbc.user", conSet.getUser());
connectionProperties.put("javax.persistence.jdbc.password", conSet.getPassword());
connectionProperties.put("hibernate.default_schema", conSet.getSchema());
System.out.println("Before creating EM");
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("postgres-connect", connectionProperties);
EntityManager entityManager = entityManagerFactory.createEntityManager();
System.out.println("After creating EM");
}
c3p0 配置在 persistence.xml
:
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
<property name="hibernate.c3p0.min_size" value="0"/>
<property name="hibernate.c3p0.max_size" value="10"/>
<property name="hibernate.c3p0.timeout" value="300"/>
<property name="hibernate.c3p0.idle_test_period" value="3000"/>
<property name="hibernate.c3p0.max_statements" value="50"/>
<property name="hibernate.c3p0.acquireRetryAttempts" value="1"/>
用户名不存在的日志示例:
Before creating EM
14:47:07,009 INFO [com.mchange.v2.log.MLog] - MLog clients using log4j logging.
14:47:07,239 INFO [com.mchange.v2.c3p0.C3P0Registry] - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
14:47:07,303 INFO [com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource] - Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@e945fe41 [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@d1b84bda [ acquireIncrement -> 3, acquireRetryAttempts -> 1, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, identityToken -> 1hge3hi9nk1ku80noopkx|3185ce3, idleConnectionTestPeriod -> 3000, initialPoolSize -> 0, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 300, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 50, maxStatementsPerConnection -> 0, minPoolSize -> 0, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@9480ea7b [ description -> null, driverClass -> null, factoryClassLocation -> null, forceUseNamedDriverClass -> false, identityToken -> 1hge3hi9nk1ku80noopkx|26e664, jdbcUrl -> jdbc:postgresql://localhost:5432/postgres, properties -> {user=******, password=******} ], preferredTestQuery -> null, privilegeSpawnedThreads -> false, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, extensions -> {}, factoryClassLocation -> null, identityToken -> 1hge3hi9nk1ku80noopkx|3d02a858, numHelperThreads -> 3 ]
квіт. 07, 2017 2:47:07 PM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "user"
--code omitted
квіт. 07, 2017 2:47:07 PM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "user"
--code omitted
-- few attempts to connect
14:47:55,009 WARN [com.mchange.v2.resourcepool.BasicResourcePool] - Having failed to acquire a resource, com.mchange.v2.resourcepool.BasicResourcePool@4519c236 is interrupting all Threads waiting on a resource to check out. Will try again in response to new client requests.
在此消息之后,它不会抛出任何异常或 return
您可以考虑修改
<property name="hibernate.c3p0.acquireRetryAttempts" value="1"/>
回到默认值 30(或更高),然后尝试
<property name="hibernate.c3p0.breakAfterAcquireFailure" value="true"/>
然后,如果一个完整的尝试从数据库获取连接的周期失败(同样,这应该不止一次,否则您的应用程序将非常脆弱),您的 c3p0 DataSource
将简单地中断, 进一步尝试检查 Connection
将立即失败。这样做的缺点是,在临时网络或数据库中断后,您将失去 c3p0 的容量 "self-heal"。如果临时中断导致采集周期失败,您将不得不自己重建数据源(或重新启动您的应用程序)。
如果您想要两全其美,请将 hibernate.c3p0.acquireRetryAttempts
设置回 30-ish,将 hibernate.c3p0.breakAfterAcquireFailure
保留为默认值 false
,但编写您自己的自定义代码进行测试数据库的可用性。修改上面的测试 connect()
函数(需要重新组织,您只想创建一次 EntityManagerFactory
),可能很简单...
public void connect(ConnectionSettingsModel conSet) throws Exception {
try( Connection con = DriverManager.getConnection( conSet.getUrl(), conSet.getUser(), conSet.getPassword() ) ) {
/* Nothing to do here, really... */
} catch ( Exception e ) {
System.out.println("Trouble connecting with DBMS, please check database url, username, and password.");
throw e;
}
Map<String, String> connectionProperties = new HashMap<>();
connectionProperties.put("javax.persistence.jdbc.url", conSet.getUrl());
connectionProperties.put("javax.persistence.jdbc.user", conSet.getUser());
connectionProperties.put("javax.persistence.jdbc.password", conSet.getPassword());
connectionProperties.put("hibernate.default_schema", conSet.getSchema());
System.out.println("Before creating EM");
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("postgres-connect", connectionProperties);
EntityManager entityManager = entityManagerFactory.createEntityManager();
System.out.println("After creating EM");
}