4.8 事 件 机 制
通常,Hibernate执行持久化过程中,应用程序无法参与其中。所有的数据持久化操作,对用户都是透明的,用户无法插入自己的动作。
通过事件框架,Hibernate允许应用程序能响应特定的内部事件,从而允许实现某些通用的功能,或对Hibernate功能进行扩展。
Hibernate的事件框架由两个部分组成:
   ● 拦截器机制,对于特定动作拦截,回调应用中的特定动作。
   ● 事件系统,重写Hibernate的事件监听器。
4.8.1 拦截器
通过Interceptor接口,可以从Session中回调应用程序的特定方法,这种回调机制可让应用程序在持久化对象被保存、更新、删除或加载之前,检查并修改其属性。
通过Interceptor接口,可以在数据进入数据库之间,对数据进行最后的检查,如果数据不符合要求,可以修改数据,从而避免非法数据进入数据库。当然,通常无须这样做,只是在某些特殊的场合下,才考虑使用拦截器完成检查功能。
使用拦截器可按如下步骤进行:
(1)定义实现Interceptor接口的拦截器类;
(2)通过Session启用拦截器,或者通过Configuration启用全局拦截器。
下面是一个拦截器的示例代码,该拦截器没有进行任何实际的操作,仅仅打印出标志代码:
public class MyInterceptor extends EmptyInterceptor
{
    //更新的次数
    private int updates;
    //插入的次数
    private int creates;
    //删除数据时,将调用onDelete方法
    public void onDelete(Object entity,Serializable id,Object[]
    state,String[] propertyNames, Type[] types)
    {
        //do nothing
    }
    //同步Session和数据库中的数据
    public boolean onFlushDirty(Object entity, Serializable id, Object[]
    currentState, Object[] previousState, String[] propertyNames, Type[]
                            types)
    {
        //每同步一次,修改的累加器加1
        updates++;
        for ( int i=0; i < propertyNames.length; i++ )
        {
            if ( "lastUpdateTimestamp".equals( propertyNames[i] ) )
            {
                currentState[i] = new Date();
                return true;
            }
        }
        return false;
        }
    //加载持久化实例时,调用该方法
    public boolean onLoad(Object entity,Serializable id,Object[]
    state,String[] propertyNames,Type[] types)
    {
        System.out.println("========================");
        for ( int i=0; i < propertyNames.length; i++ )
        {
            if ( "name".equals( propertyNames[i] ) )
            {
                System.out.println(state[i]);
                state[i] = "aaa";
                return true;
            }
        }
        return false;
    }
    //保存持久化实例时,调用该方法
    public boolean onSave(Object entity,Serializable id,Object[]
    state,String[] propertyNames,Type[] types)
    {
        creates++;
        for ( int i=0; i<propertyNames.length; i++ )
        {
            if ( "createTimestamp".equals( propertyNames[i] ) )
            {
                state[i] = new Date();
                return true;
            }
        }
        return false;
    }
    //提交刷新
    public void postFlush(Iterator entities)
    {
        System.out.println("创建的次数: " + creates + ", 更新的次数: " +
    updates);
    }
    public void preFlush(Iterator entities)
    {
        updates=0;
        creates=0;
    }
    //事务提交前,触发该方法
    public void beforeTransactionCompletion(Transaction tx)
    {
        System.out.println("事务即将结束");
    }
    //事务提交后,触发该方法
    public void afterTransactionCompletion(Transaction tx)
    {
        System.out.println("事务已经结束");
    }
}
在上面的拦截器实现类中,实现了很多方法,这些方法都是在Hibernate执行特定动作时自动调用。
完成了拦截器的定义,下面是关于拦截器的使用。拦截器的使用有两种方法:
   ● 通过SessionFactory的openSession(Interceptor in)方法打开一个带局部拦截器的Session。
   ● 通过Configuration的setInterceptor(Interceptor in)方法设置全局拦截器。
下面是使用局部拦截器的示例代码:
public class HibernateUtil
{
    //静态类属性 SessionFactory
    public static final SessionFactory sessionFactory;
    //静态初始化块,完成静态属性的初始化
    static
    {
        try
        {
            //采用默认的hibernate.cfg.xml来启动一个Configuration的实例
            Configuration configuration=new Configuration().configure();
            //由Configuration的实例来创建一个SessionFactory实例
            sessionFactory = configuration.buildSessionFactory();
        }
        catch (Throwable ex)
        {
            System.err.println("初始化sessionFactory失败." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    //ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要
    对线程同步   
    public static final ThreadLocal session = new ThreadLocal();
    //不加拦截器的打开Session方法
    public static Session currentSession() throws HibernateException
    {
        Session s = (Session) session.get();
        //如果该线程还没有Session,则创建一个新的Session
        if (s == null)
        {
            s = sessionFactory.openSession();
            //将获得的Session变量存储在ThreadLocal变量的Session里
            session.set(s);
        }
        return s;
    }
    //加拦截器的打开Session方法
    public static Session currentSession(Interceptor it) throws
    HibernateException
    {
        Session s = (Session) session.get();
        //如果该线程还没有Session,则创建一个新的Session
        if (s == null)
        {
            //以拦截器创建Session对象
            s = sessionFactory.openSession(it);
            //将获得的Session变量存储在ThreadLocal变量的Session里
            session.set(s);
            }
        return s;
    }
    //关闭Session对象
    public static void closeSession() throws HibernateException
    {
        Session s = (Session) session.get();
        if (s != null)
            s.close();
        session.set(null);
    }
}
上面的Hibernate工具类提供了两个currentSession方法,分别用于不使用拦截器获取Session对象和使用拦截器获取Session对象。
下面是主程序使用拦截器的代码片段:
private void testUser()
{
    //以拦截器开始Session
    Session session = HibernateUtil.currentSession(new MyInterceptor());
    //开始事务
    Transaction tx = session.beginTransaction();
    //执行下面的代码时,可以看到系统回调onSave等方法
    /*
    User u = new User();
    u.setName("Yeeku Lee");
    u.setAge(28);
    u.setNationality("中国");
    session.persist(u);
    u.setAge(29);
    u.setAge(30);
    session.persist(u);
    */
    //执行下面的代码时,可以看到系统回调onLoad等方法
    Object o = session.load(User.class , new Integer(1));
    System.out.println(o);
    User u = (User)o;
    System.out.println(u.getName());
    //提交事务时,可以看到系统回调事务相关方法
    tx.commit();
    HibernateUtil.closeSession();
}
4.8.2 事件系统
Hibernate 3的事件系统是功能更强大的事件框架,事件系统可以替代拦截器,也可以作为拦截器的补充来使用。
基本上,Session接口的每个方法都有对应的事件。如LoadEvent和FlushEvent等。当Session调用某个方法时,Hibernate Session会生成对应的事件,并激活对应的事件监听器。
系统默认监听器实现的处理过程,完成了所有的数据持久化操作,包括插入和修改等操作。如果用户定义了自己的监听器,则意味着用户必须完成对象的持久化操作。
例如,可以在系统中实现并注册LoadEventListener监听器,该监听器负责处理所有调用Session的load()方法的请求。
监听器是单态模式对象,即所有同类型的事件处理共享同一个监听器实例,因此监听器不应该保存任何状态,即不应该使用成员变量。
使用事件系统可按如下步骤进行:
(1)实现自己的事件监听器类;
(2)注册自定义事件监听器,代替系统默认的事件监听器。
实现用户的自定义监听器有如下3个方法:
   ● 实现对应的监听器接口,这是不可思议的,实现接口必须实现接口内的所有方法,关键是必须实现Hibernate对应的持久化操作,即数据库访问,这意味着程序员完全取代了Hibernate的底层操作。
   ● 继承事件适配器,可以选择性地实现需要关注的方法,但依然试图取代Hibernate完成数据库的访问,这也不太现实。
   ● 继承系统默认的事件监听器,扩展特定方法。
实际上,前两种方法很少使用。因为Hibernate的持久化操作也是通过这些监听器实现的,如果用户取代了这些监听器,则应该自己实现所有的持久化操作,这意味着用户放弃了Hibernate的持久化操作,而改为自己完成Hibernate的核心操作。
通常推荐采用第三种方法实现自己的事件监听器。Hibernate默认的事件监听器都被声明成non-final,从而方便用户继承。
下面是用户自定义监听器的示例:
//自定义LoadListener,继承默认的DefaultLoadEventListener实现类
public class MyLoadListener extends DefaultLoadEventListener
{
    //在LoadEventListener接口仅仅定义了这个方法
    public Object onLoad(LoadEvent event, LoadEventListener.LoadType
    loadType)throws HibernateException
    {
        //先调用父类的onLoad方法,从而完成默认的持久化操作
        Object o = super.onLoad(event, loadType);
        //加入用户的自定义处理
        System.out.println("自定义的load事件");
        System.out.println(event.getEntityClassName() + "==========" +
        event.getEntityId());
        return o;
    }
}
下面还有一个MySaveListener,用于监听SaveEvent事件:
//自定义SavaListener,继承默认的DefaultSaveEventListener实现类
public class MySaveListener extends DefaultSaveEventListener
{
    //该方法完成实际的数据插入动作
    protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event)
    {
        //先执行用户自定义的操作
        System.out.println(event.getObject());
        //调用父类的默认持久化操作
        return super.performSaveOrUpdate(event);
    }
}
注意:扩展用户自定义监听器时,别忘了在方法中调用父类的对应方法。
注册用户自定义监听器也有两种方法:
   ● 编程式,通过使用Configuration对象编程注册。
   ● 声明式,在Hibernate的XML格式配置文件中进行声明,使用Properties格式的配置文件将无法配置自定义监听器。
下面的示例代码,通过编程方式使用自定义监听器:
public class HibernateUtil2
{
    //静态类属性 SessionFactory
    public static final SessionFactory sessionFactory;
    //静态初始化块,完成静态属性的初始化
    static
    {
        try
        {
            Configuration cfg = new Configuration();
            //注册loadEventListener监听器
            cfg.getSessionEventListenerConfig().setLoadEventListener
            ( new MyLoadListener() );
            //注册saveListener监听器
            cfg.getSessionEventListenerConfig().setSaveEventListener
            (new MySaveListener() );
            //由Configuration实例来创建一个SessionFactory实例
            sessionFactory = cfg.configure().buildSessionFactory();
        }
        catch (Throwable ex)
        {
            System.err.println("初始化sessionFactory失败." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    //ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要
    对线程同步
    public static final ThreadLocal session = new ThreadLocal();
    //不加拦截器的打开Session方法
    public static Session currentSession() throws HibernateException
    {
        Session s = (Session) session.get();
        //如果该线程还没有Session,则创建一个新的Session
        if (s == null)
        {
            s = sessionFactory.openSession();
            //将获得的Session变量存储在ThreadLocal变量的Session里
            session.set(s);
        }
        return s;
    }
    //关闭Session对象
    public static void closeSession() throws HibernateException
    {
        Session s = (Session) session.get();
        if (s != null)
            s.close();
        session.set(null);
    }
}
如果不想修改代码,也可以在配置文件中使用事件监听器,注册事件监听器的Hibernate配置文件代码如下:
<?xml version='1.0' encoding='GBK'?>
<!-- Hibernate配置文件的文件头,包含DTD等信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.
        dtd">
<!-- Hibernate配置文件的根元素 -->
<hibernate-configuration>
    <session-factory>
        <!—设置数据库驱动 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver
        </property>
        <!-- 数据库服务的url -->
        <property name="connection.url">jdbc:mysql://localhost/hibernate
        </property>
        <!-- 数据库服务的用户名 -->
        <property name="connection.username">root</property>
        <!-- 数据库服务的密码 -->
        <property name="connection.password">32147</property>
        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">5</property>
        <!-- 设置数据库方言 -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect
        </property>
        <!-- 显示Hibernate生成的SQL语句 -->
        <property name="show_sql">true</property>
        <!-- 配置应用启动时,是否启动自动建表 -->
        <property name="hbm2ddl.auto">update</property>
        <!-- 列出所有的持久化映射文件 -->
        <mapping resource="User.hbm.xml"/>
        <!-- 注册事件监听器 -->
        <listener type="load" class="lee.MyLoadListener"/>
        <listener type="save" class="lee.MySaveListener"/>
    </session-factory>
</hibernate-configuration>
使用配置文件注册事件监听器虽然方便,但也有不利之处,通过配置文件注册的监听器不能共享实例。如果多个<listener/>元素中使用了相同的类,则每一个引用都将产生一个新的拦截器实例。如果需要在多个事件之间共享监听器的实例,则必须使用编程方式注册事件监听器。
注意:虽然监听器类实现了特定监听器的接口,在注册的时候还要明确指出注册的事件。这是因为一个类可能实现多个监听器的接口,注册时明确指定要监听的事件,可以使得启用或者禁用某个事件监听的配置工作更简单。
	posted on 2009-07-19 09:42 
jadmin 阅读(94) 
评论(0)  编辑  收藏