Hibernate用Mysql数据库时链接关闭异常的解决

在一个项目中,客户要求除操作系统外全部使用免费软件,因此我使用了Mysql 4.0作为数据库服务器,其JDBC驱动为3.0.9版本,在给客户安装后调试一切正常。可是到了第二天,只要一登录就提示“No operations allowed after connection closed”异常,显示在浏览器上。在经过一番检查后我发现,在这种情况下只要重新启动Tomcat就恢复正常,然而到了第二天问题依旧。

在网上查找一下,原来Mysql在经过8小时不使用后会自动关闭已打开的连接,摘录原文如下:

I have a servlet/application that works fine for a day, and then stops working overnight

MySQL closes connections after 8 hours of inactivity. You either need to use a connection pool that handles stale connections or use the "autoReconnect" parameter (see "Developing Applications with MySQL Connector/J").

Also, you should be catching SQLExceptions in your application and dealing with them, rather than propagating them all the way until your application exits, this is just good programming practice. MySQL Connector/J will set the SQLState (see java.sql.SQLException.getSQLState() in your APIDOCS) to "08S01" when it encounters network-connectivity issues during the processing of a query. Your application code should then attempt to re-connect to MySQL at this point.

在客户那边,晚上时间是不会有人使用这个系统的,就造成了系统中原先没有考虑到的这个情况。

为此我试验了三种方法:

1、在数据库的url中加入&autoReconnect=true

2、在每次调用getSession()方法时判断session.isClosed()是否为真,若为真则调用session.reconnect();

3、在经过两天,事实证明前两种方法都不起作用的情况下,我在这个页面找到了第三种方法,即不使用Hibernate内置的连接池(Hibernate强烈推荐不使用但我以前一直在用),改用C3P0连接池,这个连接池会自动处理数据库连接被关闭的情况。要使用C3P0很简单,先从Hibernate里把c3p0-0.8.3.jar复制到项目的lib目录中,再在hibernate.properties里去掉hibernate.c3p0开头的那些属性的注释(使用缺省值或自己需要的数值),这样Hibernate就会自动使用C3P0代替内置的连接池了。到目前为止前面的问题没有再出现过。

以前对Hibernate警告不要使用内置连接池作产品用途没有太放在心上,这次是一个教训,所以不论从稳定还是性能的考虑,都应该选择相对更加成熟的连接池。

Update: 除了连接池的原因,原先写的HibernateDAO类也有问题,在有些情况下一个session会被多个请求反复使用,现在已改正。另外,c3p0这个名字不是星球大战里那个机器人么?

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/10/75145.html

[Hibernate]xDoclet生成hbm的一个bug

做示范中心项目时遇到的,类Teacher实现接口BusinessObject,在接口里用@hibernate.class,在类里用@hibernate.joined-subclass-key column="oid"和@hibernate.joined-subclass,执行ant任务时只生成了BusinessObject.hbm.xml,而且在里面没有关于Teacher的定义。为此折腾了好一阵,后在在网上找到一个贴子说的是同一个问题,还提供了一个patch,不过还没试好不好使,内容如下。(快该回家了,晚上继续写)

diff -u -1 -b -p -r1.20 HibernateTagsHandler.java
--- HibernateTagsHandler.java   14 Jun 2003 13:58:10 -0000      1.20
+++ HibernateTagsHandler.java   3 Nov 2003 00:58:27 -0000
@@ -285,3 +285,7 @@ public class HibernateTagsHandler
                 }
-                else if (clazz.getSuperclass() != null && clazz.getSuperclass().getQualifiedName().equals(typeName)) {
+                else if ((clazz.getSuperclass() != null &&
+                    clazz.getSuperclass().getQualifiedName().equals(typeName))
+                    ||
+                    (getCurrentClass().isInterface() &&
+                    clazz.isImplementingInterface(typeName))) {
                     log.debug("is a subclass");

现在决定不用这个patch的方法了,改源码得重新build,而且以后就不能用通用包了。暂时拿抽象类代替接口吧,差不多。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/13/33157.html

[Hibernate]使用XDoclet生成hbm.xml

Hibernate真是受欢迎,有那么多工具为它服务,XDoclet、MiddleGen、各种插件。。。

用XDoclet生成hbm.xml就是在.java文件里写入一些元数据,XDoclet会从这些数据以及类本身得到足够的信息来生成目标文件。当然,除了用于hibernate,XDoclet还可以用于web、ejb等等很多用途。

XDoclet要从sourceforge上下载,包含了很多jar包、文档和例子,我觉得文档做得还是不错的,查起来比较方便。要使用XDoclet,一般要通过ant来完成,也就是在ant脚本里加入XDoclet的内容。

由于eclipse已经包含了ant支持,因此我没有专门去下载一个ant回来,而是直接使用eclipse带的,版本是1.5.3。

创建一个名为build.xml的脚本(其实应该换个名,比如gen-hbm.xml,看起来比较明白),内容如下:

<?xml version="1.0" encoding="ISO-8859-1"?>

<project name="XDoclet Examples" default="hibernate" basedir=".">
    <property name="xdoclet.root.dir" value="c:/xdoclet-1.2.1"/>
    <property name="xdoclet.lib.dir" value="${xdoclet.root.dir}/lib"/>
    <path id="myclasspath">
        <fileset dir="${xdoclet.lib.dir}">
            <include name="*.jar"/>
        </fileset>
    </path>
     <taskdef
        name="hibernatedoclet"
        classname="xdoclet.modules.hibernate.HibernateDocletTask"
        classpathref="myclasspath"
        />
    <target name="hibernate" description="Generate mapping documents">

        <echo>+---------------------------------------------------+</echo>
        <echo>|                                                   |</echo>
        <echo>| R U N N I N G   H I B E R N A T E D O C L E T     |</echo>
        <echo>|                                                   |</echo>
        <echo>+---------------------------------------------------+</echo>

        <hibernatedoclet
            destdir="./src"
            excludedtags="@version,@author,@todo,@see"
            addedtags="@xdoclet-generated at ${TODAY},@copyright The XDoclet Team,@author XDoclet,@version ${version}"
            force="false"
            verbose="true">

            <fileset dir="./src">
                <include name="org/haree/struts/form/UserForm.java"/>
            </fileset>

            <hibernate version="2.0"/>

        </hibernatedoclet>
    </target>
</project>

我曾经卡住的一个地方就是在taskdef里的classpathref属性。一开始我在eclipse的ant运行参数里设置了XDoclet相关的包,总是提示:

Can't create a hibernate element under hibernatedoclet. Make sure the jar file
containing the corresponding subtask class is on the classpath specified in the
<taskdef> that defined {2}.

后来如上设置了classpathref,即包含了XDoclet使用到的包,并将eclipse的ant里关于XDoclet的包都去掉,竟然就成功了。其实现在也不明白为什么会这样。。。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/07/28/28072.html

[Hibernate]关于ID的一个容易混淆的地方

用了这么久的Hibernate了,今天却遇到一个从未遇到的问题,幸好我思维敏捷,善于联想,才得以在短时间内发现并解决了问题。以下是具体描述。

我在HibernateDAO这个类里增加了一个方法如下:

public Object getById(Class clazz, String id) throws HibernateException{
   return session.find("from "+clazz.getName()+" o where o.id=?",id,Hibernate.STRING).get(0);
}

你知道,我的PO类的主键都是名为oid的。凑巧的是,有一些PO类除了具有oid属性外,还具有名为id的属性,用来表示业务编号,例如教师编号、文化程度的编号等等。这些类在使用这个方法时总报下面的异常:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

郁闷了半个小时,终于想到,会不会是查询语句中的o.id,Hibernate认为这里的id表示的是主键oid呢?在debug里把参数按oid的值一改,发现果然如此!

解决方法:暂时还不知道有什么方法能起到转义的作用,不过id这个属性确实有点容易产生歧义,还是改名为code吧。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/07/28/28064.html

[转载]HibernatePlugIn for Struts

这个Plugin的作用是在Struts应用程序启动时进行hibernate的初始化操作,原文请移步 HibernatePlugIn for Struts

步骤很简单:

1、在struts-config.xml里增加:

<plug-in className="org.haree.struts.HibernatePlugIn"> 
  <!-- 'path-to-config-file' is relative to the root of the class 
       path.  It MUST start with a '/'. The default is 
       "/hibernate.cfg.xml --> 
  <set-property property="configFilePath" value="path-to-config-file" /> 
  <set-property property="storeInServletContext" value="true-or-false" /> 
</plug-in> 

2、HibernatePlugIn.java的内容

package org.haree.struts; 

import java.net.URL; 

import javax.servlet.ServletContext; 
import javax.servlet.ServletException; 

import net.sf.hibernate.SessionFactory; 
import net.sf.hibernate.cfg.Configuration; 

import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
import org.apache.struts.action.ActionServlet; 
import org.apache.struts.action.PlugIn; 
import org.apache.struts.config.ModuleConfig; 

/** 
 * Implements the <code>PlugIn</code> interface to configure the Hibernate 
 * data persistence library.  A configured 
 * <code>net.sf.hibernate.SessionFactory</code> is stored in the 
 * <code>ServletContext</code> of the web application unless the property 
 * <code>storedInServletContext</code> is set to <code>false</code>. 
 * 
 * <p> 
 * <plugin class="net.sf.hibernate.plugins.struts.HibernatePlugIn"> 
 *   <set-property name="configFilePath&quot" 
 *                value="path-to-config-file"/> 
 *   <set-property name="storedInServletContext&quot" 
 *                value="true-or-false"/> 
 * </plugin> 
 * 
 * @author  <a href="mailto:bhandy@users.sf.net">Bradley M. Handy</a> 
 * @version 1.0 
 */ 
public class HibernatePlugIn implements PlugIn { 

    /** 
     * the key under which the <code>SessionFactory</code> instance is stored 
     * in the <code>ServletContext</code>. 
     */ 
    public static final String SESSION_FACTORY_KEY 
            = SessionFactory.class.getName(); 

    private static Log _log = LogFactory.getLog(HibernatePlugIn.class); 

    /** 
     * indicates whether the <code>SessionFactory</code> instance will be stored 
     * in the <code>ServletContext</code>, or not. 
     */ 
    private boolean _storedInServletContext = true; 

    /** 
     * the path to the xml configuration file.  the path should start with a 
     * '/' character and be relative to the root of the class path. 
     * (DEFAULT:  "/hibernate.cfg.xml") 
     */ 
    private String _configFilePath = "/hibernate.cfg.xml"; 

    private ActionServlet _servlet = null; 
    private ModuleConfig _config = null; 
    private SessionFactory _factory = null; 

    /** 
     * Destroys the <code>SessionFactory</code> instance. 
     */ 
    public void destroy() { 
        _servlet = null; 
        _config = null; 

        try { 
            _log.debug("Destroying SessionFactory"); 

            _factory.close(); 

            _log.debug("SessionFactory destroyed"); 
        } catch (Exception e) { 
            _log.error("Unable to destroy SessionFactory(exception ignored)", 
                    e); 
        } 
    } 

    /** 
     * Initializes the <code>SessionFactory</code>. 
     * @param servlet the <code>ActionServlet</code> instance under which the 
     *        plugin will run. 
     * @param config the <code>ModuleConfig</code> for the module under which 
     *        the plugin will run. 
     */ 
    public void init(ActionServlet servlet, ModuleConfig config) 
    throws ServletException { 
        _servlet = servlet; 
        _config = config; 

        initHibernate(); 
    } 

    /** 
     * Initializes Hibernate with the config file found at 
     * <code>configFilePath</code>. 
     */ 
    private void initHibernate() throws ServletException { 
        Configuration configuration = null; 
        URL configFileURL = null; 
        ServletContext context = null; 

        try { 
            configFileURL = HibernatePlugIn.class.getResource(_configFilePath); 

            context = _servlet.getServletContext(); 

            if (_log.isDebugEnabled()) { 
                _log.debug("Initializing Hibernate from " 
                        + _configFilePath + ""); 
            } 

            configuration = (new Configuration()).configure(configFileURL); 
            _factory = configuration.buildSessionFactory(); 

            if (_storedInServletContext) { 
                _log.debug("Storing SessionFactory in ServletContext"); 

                context.setAttribute(SESSION_FACTORY_KEY, _factory); 
            } 

        } catch (Throwable t) { 
            _log.error("Exception while initializing Hibernate."); 
            _log.error("Rethrowing exception", t); 

            throw (new ServletException(t)); 
        } 
    } 

    /** 
     * Setter for property configFilePath. 
     * @param configFilePath New value of property configFilePath. 
     */ 
    public void setConfigFilePath(String configFilePath) { 
        if ((configFilePath == null) || (configFilePath.trim().length() == 0)) { 
            throw new IllegalArgumentException( 
                    "configFilePath cannot be blank or null."); 
        } 

        if (_log.isDebugEnabled()) { 
            _log.debug("Setting 'configFilePath' to '" 
                    + configFilePath + "'"); 
        } 

        _configFilePath = configFilePath; 
    } 

    /** 
     * Setter for property storedInServletContext. 
     * @param storedInServletContext New value of property storedInServletContext. 
     */ 
    public void setStoredInServletContext(String storedInServletContext) { 
        if ((storedInServletContext == null) 
                || (storedInServletContext.trim().length() == 0)) { 
            storedInServletContext = "false"; 
        } 

        if (_log.isDebugEnabled()) { 
            _log.debug("Setting 'storedInServletContext' to '" 
                    + storedInServletContext + "'"); 
        } 

        _storedInServletContext 
                = new Boolean(storedInServletContext).booleanValue(); 
    } 

}