配色: 字号:
详解Java的Hibernate框架中的Interceptor和Collection
2016-10-21 | 阅:  转:  |  分享 
  
详解Java的Hibernate框架中的Interceptor和Collection

这篇文章主要介绍了Java的Hibernate框架中的Interceptor和Collection,Hibernate是Java的SSH三大web开发框架之一,需要的朋友可以参考下



Interceptor

讲到Interceptor,相信熟悉struts2的童鞋肯定不会陌生了,struts2可以自定义拦截器进行自己想要的一系列相关的工作。而这里我们说的Interceptor也是差不多相似的功能。

废话不说,直接来代码:

下面这个是MyInterceptor类,它实现了Interceptor接口:



publicStringonPrepareStatement(Stringarg0){returnarg0;}publicbooleanonSave(Objectarg0,Serializablearg1,Object[]arg2,String[]arg3,Type[]arg4)throwsCallbackException{if(arg0instanceofUser){System.out.println("Usertobesaved=>"+((User)arg0).getName());}returnfalse;}



其他方法就不看了,按默认实现就行,我们只需要改这两个方法,需要把onPrepareStatement中的返回值改一下,改成返回当前的SQL语句,参数中就是传入的执行的SQL语句,我们直接返回就可以打印出该语句。

而在onSave中,看名字就可以知道是在保存的时候进行调用的。我们可以进行一系列保存前的工作。

相信大家看参数名称就可以看明白了吧。

Serializable是指序列号的参数,在这里是指跟数据库ID进行映射的属性

Object[]这是一系列的状态,暂时没怎么用到,以后用到再研究,但API中说明了,不管用何种方式修改了这个数组中的值,这个onSave方法必须返回true。

String[]是指属性的名称

而Type[]也就是相应属性的类型。



1)这个Interceptor可以在保存数据库前和后做一些相应的操作。比如想对数据进行修改,添加前缀或后缀的,都可以用它来实现,下面我们来看一下。



publicbooleanonSave(Objectarg0,Serializablearg1,Object[]arg2,String[]arg3,Type[]arg4)throwsCallbackException{if(arg0instanceofUser){System.out.println("Usertobesaved=>"+((User)arg0).getName());}//我们在这里添加123作为名字的前缀Useruser=(User)arg0;user.setName("123"+user.getName());returnfalse;}



我们看一下测试方法:



publicstaticvoidmain(String[]args){Configurationcfg=newConfiguration().configure();SessionFactorysessionFactory=cfg.buildSessionFactory();Interceptorinterceptor=newMyInteceptor();Sessionsession=sessionFactory.openSession(interceptor);Useruser=newUser();user.setName("shun");Transactiontx=session.beginTransaction();session.save(user);tx.commit();session.close();}



很简单,我们只是进行了简单的保存而已。这里就没给出映射文件和实体类,大家随便弄个试一下就行。

运行它,我们可以看到:



Usertobesaved=>shunHibernate:insertintoUSER(USER_NAME,age)values(?,?)Hibernate:updateUSERsetUSER_NAME=?,age=?whereUSER_ID=?



它会在最后进行更新姓名和年龄的操作,主要是因为我们在onSave方法中进行了修改。





我们看到数据库中的值已经修改为有123前缀的了。



2)同样道理,我们可以在加载时修改属性的值:



publicbooleanonLoad(Objectarg0,Serializablearg1,Object[]arg2,String[]arg3,Type[]arg4)throwsCallbackException{if(arg0instanceofUser){System.out.println("Usertobeloaded=>"+(arg2[0]+":"+arg2[1]));}Useruser=(User)arg0;//判断哪个属性是namefor(inti=0;i


加载时修改属性的值是写在onLoad方法内。

这里的arg0就是我们的User对象,这里它还没有值,这个方法在load方法之后才进行调用,所以我们此时对user进行操作已经是于事无补了,而且我们这里的user.setName是没用的操作。主要在:



arg2[i]=((String)arg2[i]).replace("123","");





这句代码改变了返回的属性的值,那么我们在程序中拿到的user对象中的值也会改变,我们运行测试方法看看:



publicstaticvoidmain(String[]args){Configurationcfg=newConfiguration().configure();SessionFactorysessionFactory=cfg.buildSessionFactory();Interceptorinterceptor=newMyInteceptor();Sessionsession=sessionFactory.openSession(interceptor);Useruser=(User)session.load(User.class,newLong(39));System.out.println("Username:"+user.getName());session.close();}



看结果,我们得到了:



Hibernate:selectuser0_.USER_IDasUSER1_0_0_,user0_.USER_NAMEasUSER2_0_0_,user0_.ageasage0_0_fromUSERuser0_whereuser0_.USER_ID=?Usertobeloaded=>123shun:0Username:shun



我们已经把原来的123给去掉了,在真正加载后进行了相关的处理,不过这个并不是真正加载前的处理,有点投机的嫌疑。但也不失为一个考虑的方案。Interceptor也许用得最多的还是在日志的相关处理上,比如我们需要对每次操作都进行相应的日志记录,那么Interceptor是一个很好的选择。



Collection

记得我们在以前例子中一对多中用到的Set,还有印象么,如果没有赶快去查一下资料,回顾一下。今天我们就围绕着这些Collection来进行学习。

还是不废话了,我们直接进入正题。

1)首先我们来学习一下Set。大家都知道JAVAutil包里面也有一个Set,那么hibernate里面的set和java的set和什么区别和联系呢?我们打开hibernate的API,找到Set,可以看到。



我们看到的就是这样一个hibernate的集合的父类,它是一个抽象类,有一系列具体的实现类,我们继续看到下面的方法时,发现这个类实现上是对java集合的封装,这样我们就明白啦,所谓的hibernate的Set实际上也只是封装了java的Set。

那么,Set中不允许重复元素的这个特点是否也在hibernate中呢?答案当然是肯定啦。

我们这里不看这些,我们以前在学习映射时是直接把属性和所关联的类进行关联,但今天我们不这样啦,我们用另外一种方法,只是关联一个字符串,看看有什么问题。

但在看这个问题前,我们先来看看,java中的String比较。

我们看到的就是这样一个hibernate的集合的父类,它是一个抽象类,有一系列具体的实现类,我们继续看到下面的方法时,发现这个类实现上是对java集合的封装,这样我们就明白啦,所谓的hibernate的Set实际上也只是封装了java的Set。

那么,Set中不允许重复元素的这个特点是否也在hibernate中呢?答案当然是肯定啦。

我们这里不看这些,我们以前在学习映射时是直接把属性和所关联的类进行关联,但今天我们不这样啦,我们用另外一种方法,只是关联一个字符串,看看有什么问题。

但在看这个问题前,我们先来看看,java中的String比较。



publicstaticvoidmain(String[]args){Strings1="shun1";Strings2="shun1";System.out.println("s1==s2:"+(s1==s2));}



相信很多童鞋都知道答案是true。

在进行例子前先看一下我们的映射文件,映射类那些就不写了:

这是TUser的映射文件:







接下来是Address的映射文件:







童鞋们看清楚了,我在TUser中的Set里面把one-to-many注释了而用了element,这里先不管它有什么问题,我们先看数据库:

这是t_address表:



下面是t_user表:



我们可以看到id为4的User对应了三个地址,接下来,我们来看一下测试方法:



publicstaticvoidmain(String[]args){Configurationcfg=newConfiguration().www.visa158.com.configure();SessionFactorysessionFactory=cfg.buildSessionFactory();Sessionsession=sessionFactory.openSession();TUseruser=(TUser)session.load(TUser.class,newInteger(4));Setset=user.getAddresses();session.close();Systwww.hunanwang.netrintln("addresssize:"+set.size());}



很简单的一个查询类,只是取出了这个结果而已,我们看到一个奇怪的现象:



addresssize:1



这是结果!

你肯定会说,肯定错了吧,是hibernate的bug。这里肯定高兴啦,总算可以提交一个bug了,以前跳槽的时候可以大声说我为hibernate提交过bug。哈哈,但很遗憾,这并不是bug。

刚才说了我们前面的那个字符串比较的是为这里作铺垫的,那么怎么铺呢?

我们在配置文件中用Set,并且是通过String字符来进行关联的,那么它首先在数据库中取出放进Set中的时候会先判断该关联字符的值是否是相等的,这里由于我们的值都是相等的(这里我们暂时不深究它是怎么进行比较的),我们只需要知道当我们用字符串来进行比较的时候,我们又陷入了JAVA中的字符串陷阱了。查出来只有一条,那么删除呢,删除的时候就比较麻烦啦,它会把所有相同的记录都删除。

那么我们来看一下删除的:



TUseruser=(TUser)session.load(TUser.class,newInteger(4));Transactiontx=session.beginTransaction();Objectobj=user.getAddresses().iterator().next();user.getAddresses().remove(obj);tx.commit();session.close();



这里hibernate输出的语句是:



Hibernate:deletefromt_addresswhereuser_id=?



相信什么时候大家都知道了,是删除该用户下的所有地址。这没得选择,只能全部都删除。

所以在真正的开发中需要注意。



2)上面我们讲了Set,好像用着不怎么爽啊,有那么个陷阱,但没办法,Set是我们用得最多的,而且一般也不会有人直接去关联字符串吧。但很多人还是会不爽,那么hibernate也就应大家要求搞多了一个Bag(也许不是应要求,可能它们里面也有人不满,哈哈)。

我们先来看看它的基本用法:

首先我们需要把前面的TUser的映射文件中的Set标签修改为:







并且相应的实体类需要把addresses的类型修改为List类型。

这里我们重新添加三个地址:









我们运行测试代码:



publicstaticvoidmain(String[]args){Configurationcfg=newConfiguration().configure();SessionFactorysessionFactory=cfg.buildSessionFactory();Sessionsession=sessionFactory.openSession();TUseruser=(TUser)session.load(TUser.class,newInteger(4));System.out.println("addresssize:"+user.getAddresses().size());session.close();}





这里我们看到了:



addresssize:3



这次我们已经全部都可以看到了,不管有没有重复。



但我们刚才看了一个删除的问题,Bag在这里还是没有解决,需要借助idBag。我们看到配置文件,需要如下的修改:



idbagname="addresses"table="t_address"lazy="true">



我们看到它只比bag多了一个collection-id进行表明要删除的记录号。

当我们重新运行删除的代码:



TUseruser=(TUser)session.load(TUser.class,newInteger(4));Transactiontx=session.beginTransaction();Objectobj=user.getAddresses().iterator().next();user.getAddresses().remove(obj);tx.commit();



我们看到输出语句为:



Hibernate:deletefromt_addresswhereid=?



这次并不是通过user_id来进行删除,而是根据t_address的ID来进行删除,这说明它真正删除我们需要删除的那条记录。

我们看到数据库,现在记录是:









我们已经把第一条记录给删了,正确了。



3)看了上面两种方法,我们再来看一下MAP,它跟上面两个最大的不同就是可以进行键值的对应。直接看代码,直观点:

首先,我们需要修改配置文件:







它和前面两个最大的不同就是有一个index,这相当于我们在java中map的key,我们通过这个来取出相对应的记录。记住,改完这里还要改相应的实体类,需要把addresses属性的类型改成Map。

看看数据库的数据:









这里我们看到有两个office和一个home,那么office是拿哪个呢



不要急,我们运行一下测试代码就知道了:



TUseruser=(TUser)session.load(TUser.class,newInteger(4));System.out.println(user.getAddresses().get("home"));System.out.println(user.getAddresses().get("office"));



ShanWeiShangHai



对,如结果可知,我们取得的是后面那个,这跟Map的原理一样,后面存入的值会覆盖前面的值(如果它们是同一个key的情况下)。

Map是比较简单的,相当前两个来说。



4)最后一个我们来看一下List。List与前几种又有不同,不同在它可以进行排序。

我们来看一下它是怎么实现的:

首先我们还是修改一下映射文件:







它和Map的配置差不多,但index的属性是不一样的,Map中的index是作为key来取得值,而List的index是作为排序的。

我们看数据库:









我们设了三个值,顺序分别为0,1,2。

下面我们运行代码来更改0,2的值:



TUseruser=(TUser)session.load(TUser.class,newInteger(4));Transactiontx=session.beginTransaction();Objectobj1=user.getAddresses().get(0);Objectobj2=user.getAddresses().get(2);user.getAddresses().set(0,obj2);user.getAddresses().set(2,obj1);tx.commit();



我们看到结果:









我们看到,0,2已经调换了,当然这也只是调换了idx的值。但这已经基本上实现了排序的功能了。





















献花(0)
+1
(本文系白狐一梦首藏)