Hibernate的事件机制

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/>元素中使用了相同的类,则每一个引用都将产生一个新的拦截器实例。如果需要在多个事件之间共享监听器的实例,则必须使用编程方式注册事件监听器。

注意:虽然监听器类实现了特定监听器的接口,在注册的时候还要明确指出注册的事件。这是因为一个类可能实现多个监听器的接口,注册时明确指定要监听的事件,可以使得启用或者禁用某个事件监听的配置工作更简单。

转载于:https://www.cnblogs.com/jadmin/archive/2009/07/19/2206094.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/276434.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快速使用Vue3最新的15个常用API

之前我写了一篇博客介绍了Vue3的新特性&#xff0c;简单了解了一下Vue3都有哪些特色&#xff0c;并且在文末带大家稍微体验了一下Vue3中 Compsition API 的简单使用上一篇文章地址&#xff1a;紧跟尤大的脚步提前体验Vue3新特性&#xff0c;你不会还没了解过Vue3吧因为这个月的…

超级马里奥代码_任天堂的源码泄露,揭示超级马里奥的前世之生

被黑客盯上的任天堂任天堂遭到了史上最大规模的黑客攻击&#xff0c;Wii 完整源码、设计以及《宝可梦》多部作品的信息遭到泄露&#xff0c;而此次泄露事件的后续影响似乎也爆发了出来。《马里奥赛车》和《超级马里奥世界2》(耀西岛)的早期原型视频&#xff0c;以及《超级马里奥…

行者寂寞

公元2009年7月20日。闰五月廿八。炎日&#xff0c;汗如雨。晨行。病卧于京西客站。是夜&#xff0c;不能寐。 公元2009年7月21日。闰五月廿九。戏于清华&#xff0c;游于星巴克。汗如雨。是夜&#xff0c;困于京国际机场5小时。 公元2009年7月22日。六月初一。晨时抵宁。大雨。…

Azure PowerShell (1) PowerShell整理

《Windows Azure Platform 系列文章目录》 把之前Azure ASM的PowerShell都整理好了。 https://github.com/leizhang1984/AzureChinaPowerShell

漫画 | 前端发展史的江湖恩怨情仇

时间总是过得很快&#xff0c; 似乎快得让人忘记了昨天&#xff0c;前端WEB领域的发展更是如此&#xff0c;转眼间已是近30年&#xff0c;时光荏苒&#xff0c;初心不变&#xff0c;在一代又一代前端人的努力下&#xff0c;前端已经是互联网不可或缺的一部分。然而很多前端打工…

jquery1.9 下检测浏览器类型和版本

原文链接&#xff1a;http://blog.csdn.net/lyc_2011_acm/article/details/8749177 Jquery1.9版本中$.browser已被剔除&#xff1a; 判断浏览器类型&#xff1a; $.browser.mozilla /firefox/.test(navigator.userAgent.toLowerCase()); $.browser.webkit /webkit/.test(nav…

python可迭代对象 迭代器生成器_Python可迭代对象、迭代器和生成器

8.1 可迭代对象(Iterable)大部分对象都是可迭代&#xff0c;只要实现了__iter__方法的对象就是可迭代的。__iter__方法会返回迭代器(iterator)本身&#xff0c;例如&#xff1a;>>> lst [1,2,3]>>> lst.__iter__()Python提供一些语句和关键字用于访问可迭代…

Windows Mobile下使用CppUnitLite输出测试结果

背景 TDD测试驱动开发是当前流行的开发方法及模式。遵循TDD的方法对开发程序库(Library)特别有用&#xff0c;因为Library就是为第三方提供一定功能接口的实现&#xff0c;使用TDD的方法可以预先为定义的接口提供测试案例&#xff0c;保证实现代码能通过测试&#xff0c;保证Li…

sql注意事项2点

①对Null的判断,如果要用<>与null判断,则都会得到否定结果②insert into时,要把字段显示指出,不然会因字段位置变化出错③-一个数时,如果有可能存在Null,则结果会被置为Null解决方法,select出来的结果,最好加isnull判断转载于:https://www.cnblogs.com/lishenglyx/archiv…

PHP IE中下载附件问题

重点&#xff1a; 1、在IE中下载附件之前要清空缓存。 2、中文文件名要用urlencode编码。 Header("Pragma: "); //不加的话&#xff0c;IE中会提示目标主机无法访问 Header("Cache-Control: "); //不加的话&#xff0c;IE中会提示目标…

10 个你可能还不知道 VS Code 使用技巧

经常帮一些同学 One-on-One 地解决问题&#xff0c;在看部分同学使用 VS Code 的时候&#xff0c;有些蹩脚&#xff0c;实际上一些有用的技巧能够提高我们的日常工作效率。NO.1一、重构代码VS Code 提供了一些快速重构代码的操作&#xff0c;例如&#xff1a;将一整段代码提取为…

使用MAP文件快速定位程序崩溃代码行(转)

作为程序员&#xff0c;平时最担心见到的事情就是程序发生了崩溃&#xff0c;无论是指针越界还是非法操作&#xff0c;都将给我们的应用系统造成巨大的损失。但在一个大型系统的测试过程中&#xff0c;初期出现程序崩溃似乎成了不可避免的事。其实测试中出现程序崩溃并不可怕&a…

构建安全的Xml Web Service系列之如何察看SoapMessage

上一篇文章地址&#xff1a;构建安全的Xml Web Service系列一之初探使用Soap头 (5-22 12:53) 要分析Xml Web Service的安全性&#xff0c;首先要解决的问题是我们能了解和清楚Soap消息的格式和内容&#xff0c;如果获得不了SoapMessage&#xff0c;分析如何能构建安全Xml w…

前端高效开发必备的 js 库梳理

之前有很多人问学好前端需要学习哪些 js 库, 主流框架应该学 vue 还是 react ? 针对这些问题, 笔者来说说自己的看法和学习总结.首先我觉得在学习任何知识之前必须要有一个明确的学习目标, 知道自己为什么要学它, 而不是看网上说的一股脑的给你灌输各种知识, 让你学习各种库, …

交叉报表crosstab隐藏列名显示_SAP软件 报表查询之 输出格式设置

SAP不仅是功能强大、逻辑严谨的ERP软件&#xff0c;还提供了强大的报表查询功能。SAP的ALV报表展示功能是SAP的一大特点&#xff0c;实现了类似于EXCEL的功能。使用好ALV报表功能可以方便用户从SAP中取到想要的数据&#xff0c;尤其是财务用户。大家在使用SAP报表时&#xff0c…

CSS HACK 区别 IE6、IE7、IE8、Firefox兼容性

转载链接&#xff1a;http://developer.51cto.com/art/201009/226787_1.htm 本文向大家描述一下如何使用CSS HACK区别IE6、IE7、IE8、Firefox兼容性问题&#xff0c;针对不同的浏览器写不同的CSS code的过程&#xff0c;就叫CSS hack,也叫写CSS hack&#xff0c;相信你对本文介…

Flex sdk4 布局与更新

在flex中,主要使用LayoutManager来驱动组件的度量和布局策略, LayoutManager实现一个单例,在Application类构造函数中创建: 1 public functionApplication()2 {3 UIComponentGlobals.layoutManager ILayoutManager(4 Singleton.getInstance("mx.managers::ILayoutManager&…

linux PROC文件系统详解

/proc文件系统下的多种文件提供的系统信息不是针对某个特定进程的&#xff0c;而是能够在整个系统范围的上下文中使用。可以使用的文件随系统配置的变化而变化。命令procinfo能够显示基于其中某些文件的多种系统信息。以下详细描述/proc下的文件。----------------------------…

seo每日一贴_白杨SEO:我看ZAC的外贸SEO应该怎样做?(策略篇)

前言&#xff1a;这是白杨SEO公众号更新第64篇。本该写写头条SEO啥的&#xff0c;最近在师徒培训讲站内SEO时有旁听同学提到后面讲讲谷歌SEO怎么样&#xff0c;因为谷歌全世界搜索市场占有率&#xff0c;所以外贸SEO最主要还是做谷歌SEO。以白杨特意又去了前辈ZAC的SEO每日一贴…

Can't connect to local MySQL server through socket '/tmp/mysql.sock'

转载链接&#xff1a;http://blog.csdn.net/ixidof/article/details/5958904 摘要&#xff1a;解决不能通过mysql .sock连接MySQL问题 这个问题主要提示是&#xff0c;不能通过 /tmp/mysql .sock连到服务器&#xff0c;而php标准配置正是用过/tmp/mysql .sock&#xff0c;但是…