Existem essencialmente duas formas distintas de usar Hibernate com MySQL (ou qualquer outra base-de-dados, para este ponto em concreto é indiferente), definindo todas as propriedades da ligação no próprio ficheiro de configuração do Hibernate, ou dizendo apenas que se pretende usar uma datasource JNDI pré-existente.
A primeira opção é a escolha típica numa aplicação stand-alone, mas a opção de utilizar uma datasource JNDI é a que faz mais sentido para application servers. Portanto, o ficheiro de configuração do Hibernate há-de ter, parcialmente, este aspecto:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.datasource">
java:my_app_name
</property>
<property name="hibernate.setup">true</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLInnoDBDialect
</property>
<property name="transaction.factory_class">
org.hibernate.transaction.JDBCTransactionFactory
</property>
<property name="current_session_context_class">
thread
</property>
</session-factory>
</hibernate-configuration>
Depois basta definir a datasource com o nome JNDI correspondente, por exemplo assim:
<datasources>
<local-tx-datasource>
<jndi-name>my_app_name</jndi-name>
<connection-url>jdbc:mysql://host_or_ip/db_name</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>username</user-name>
<password>password</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>20</max-pool-size>
</local-tx-datasource>
</datasources>
Agora que está tudo montado como manda a documentação, sugiro uma experiência:
- Ligar servidor BD.
- Ligar application server.
- Testar aplicação que se liga à base de dados (funciona)
- Reiniciar servidor BD (ou esperar 8 horas).
- Testar aplicação que se liga à base de dados (não funciona!)
Então… tanto trabalho para abstrair a ligação a base de dados e um simples restart da base de dados deixa as ligações geridas pelo Hibernate todas inválidas? E, pior, não há recuperação automática?
Pois… parece que não. E acrescentar o parâmetro autoReconnect=true ao URL JDBC também não resolve, aliás, a utilização desse parâmetro até é desaconselhada, como se pode ver pelo que diz no manual:
Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for a queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don’t handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly. — MySQL 5.0 Reference Manual
A solução que encontrei para MySQL (e que também funciona para MS SQL Server), é acrescentar a seguinte linha ao ficheiro de configuração da datasource:
<check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
Esta opção faz mesmo aquilo que parece fazer, ou seja, antes de usar uma ligação, a query ali especificada é executada como forma de verificar se a ligação é usável. Se não ocorrer nenhum erro, a instrução SQL que realmente se pretende executar é enviada pela mesma ligação, se houver um erro, a ligação é descartada e é criada uma nova.
Claro que isto tem um impacto sério na performance uma vez que se está a duplicar o número de queries, o que pode ser particularmente grave se o acesso à base de dados for feito através de um canal de elevada latência (rede, por exemplo).
Aparentemente existem soluções melhores para outras bases de dados, como o seguinte comentário parece sugerir:
Whilst you can use the old “select 1 from dual” trick, the downside with this is that it issues an extra query each and every time you borrow a connection from the pool. For high volumes, this is wasteful.
JBoss provides a special connection validator which should be used for Oracle:
org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker
This makes use of the proprietary ping() method on the Oracle JDBC Connection class, and uses the driver’s underlying networking code to determine if the connection is still alive. — stackoverflow.com
Alguém conhece uma alternativa semelhante para MySQL?
Publicado por Manuel Padilha 