当前位置: 移动技术网 > IT编程>开发语言>Java > 详解Java的Hibernate框架中的Interceptor和Collection

详解Java的Hibernate框架中的Interceptor和Collection

2019年07月22日  | 移动技术网IT编程  | 我要评论

interceptor
讲到interceptor,相信熟悉struts2的童鞋肯定不会陌生了,struts2可以自定义拦截器进行自己想要的一系列相关的工作。而这里我们说的interceptor也是差不多相似的功能。
 废话不说,直接来代码:
 下面这个是myinterceptor类,它实现了interceptor接口:

public string onpreparestatement(string arg0) { 
  return arg0; 
} 
 
public boolean onsave(object arg0, serializable arg1, object[] arg2, 
    string[] arg3, type[] arg4) throws callbackexception { 
  if (arg0 instanceof user) { 
    system.out.println("user to be saved=>"+((user)arg0).getname()); 
  } 
  return false; 
} 

其他方法就不看了,按默认实现就行,我们只需要改这两个方法,需要把onpreparestatement中的返回值改一下,改成返回当前的sql语句,参数中就是传入的执行的sql语句,我们直接返回就可以打印出该语句。
 而在onsave中,看名字就可以知道是在保存的时候进行调用的。我们可以进行一系列保存前的工作。
 相信大家看参数名称就可以看明白了吧。
 serializable是指序列号的参数,在这里是指跟数据库id进行映射的属性
 object[]这是一系列的状态,暂时没怎么用到,以后用到再研究,但api中说明了,不管用何种方式修改了这个数组中的值,这个onsave方法必须返回true。
 string[]是指属性的名称
 而type[]也就是相应属性的类型。
 
 1)这个interceptor可以在保存数据库前和后做一些相应的操作。比如想对数据进行修改,添加前缀或后缀的,都可以用它来实现,下面我们来看一下。

public boolean onsave(object arg0, serializable arg1, object[] arg2, 
    string[] arg3, type[] arg4) throws callbackexception { 
  if (arg0 instanceof user) { 
    system.out.println("user to be saved=>"+((user)arg0).getname()); 
  } 
  //我们在这里添加123作为名字的前缀 
  user user = (user)arg0; 
  user.setname("123"+user.getname()); 
  return false; 
} 

我们看一下测试方法:

public static void main(string[] args) { 
 
  configuration cfg = new configuration().configure(); 
  sessionfactory sessionfactory = cfg.buildsessionfactory(); 
  interceptor interceptor = new myinteceptor(); 
  session session = sessionfactory.opensession(interceptor); 
     
  user user = new user(); 
  user.setname("shun"); 
     
  transaction tx = session.begintransaction(); 
  session.save(user); 
     
  tx.commit(); 
  session.close(); 
     
} 

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

user to be saved=>shun 
hibernate: insert into user (user_name, age) values (?, ?) 
hibernate: update user set user_name=?, age=? where user_id=? 

  它会在最后进行更新姓名和年龄的操作,主要是因为我们在onsave方法中进行了修改。
201613141819453.png (976×23)
我们看到数据库中的值已经修改为有123前缀的了。
 
 2)同样道理,我们可以在加载时修改属性的值:
public boolean onload(object arg0, serializable arg1, object[] arg2, 
    string[] arg3, type[] arg4) throws callbackexception { 
     
  if (arg0 instanceof user) { 
    system.out.println("user to be loaded=>"+(arg2[0]+":"+arg2[1])); 
  } 
  user user = (user)arg0; 
  //判断哪个属性是name 
  for (int i = 0; i < arg3.length; i ++){ 
    if (arg3[i].equals("name")){ 
      user.setname(((string)arg2[i]).replace("123","")); 
      arg2[i] = ((string)arg2[i]).replace("123",""); 
    } 
  } 
  return false; 
} 

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

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

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

public static void main(string[] args) { 
 
  configuration cfg = new configuration().configure(); 
  sessionfactory sessionfactory = cfg.buildsessionfactory(); 
  interceptor interceptor = new myinteceptor(); 
  session session = sessionfactory.opensession(interceptor); 
     
  user user = (user)session.load(user.class,new long(39)); 
     
  system.out.println("user name:"+user.getname()); 
  session.close(); 
     
} 

  看结果,我们得到了:

hibernate: select user0_.user_id as user1_0_0_, user0_.user_name as user2_0_0_, user0_.age as age0_0_ from user user0_ where user0_.user_id=? 
user to be loaded=>123shun:0 
user name:shun 

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

collection
记得我们在以前例子中一对多中用到的set,还有印象么,如果没有赶快去查一下资料,回顾一下。今天我们就围绕着这些collection来进行学习。
 还是不废话了,我们直接进入正题。
 1)首先我们来学习一下set。大家都知道java util包里面也有一个set,那么hibernate里面的set和java的set和什么区别和联系呢?我们打开hibernate的api,找到set,可以看到。

201613141916915.png (494×271)

我们看到的就是这样一个hibernate的集合的父类,它是一个抽象类,有一系列具体的实现类,我们继续看到下面的方法时,发现这个类实现上是对java集合的封装,这样我们就明白啦,所谓的hibernate的set实际上也只是封装了java的set。
 那么,set中不允许重复元素的这个特点是否也在hibernate中呢?答案当然是肯定啦。
 我们这里不看这些,我们以前在学习映射时是直接把属性和所关联的类进行关联,但今天我们不这样啦,我们用另外一种方法,只是关联一个字符串,看看有什么问题。
 但在看这个问题前,我们先来看看,java中的string比较。
我们看到的就是这样一个hibernate的集合的父类,它是一个抽象类,有一系列具体的实现类,我们继续看到下面的方法时,发现这个类实现上是对java集合的封装,这样我们就明白啦,所谓的hibernate的set实际上也只是封装了java的set。
 那么,set中不允许重复元素的这个特点是否也在hibernate中呢?答案当然是肯定啦。
 我们这里不看这些,我们以前在学习映射时是直接把属性和所关联的类进行关联,但今天我们不这样啦,我们用另外一种方法,只是关联一个字符串,看看有什么问题。
 但在看这个问题前,我们先来看看,java中的string比较。

public static void main(string[] args) { 
 
  string s1 = "shun1"; 
  string s2 = "shun1"; 
  system.out.println("s1==s2:"+(s1==s2)); 
     
} 

    相信很多童鞋都知道答案是true。
在进行例子前先看一下我们的映射文件,映射类那些就不写了:
 这是tuser的映射文件:

<class name="tuser" table="t_user" dynamic-insert="true" dynamic-update="true"> 
  <id name="id" column="id"> 
    <generator class="native" /> 
  </id> 
  <property name="name" type="java.lang.string" column="name"/> 
  <property name="age" type="java.lang.integer" column="age"/> 
  <set name="addresses" cascade="all" table="t_address"> 
    <key column="user_id" /> 
    <!-- <one-to-many class="address"/> --> 
    <element column="address" type="string" /> 
  </set> 
   
</class> 

  接下来是address的映射文件:

<class name="address" table="t_address" dynamic-insert="false" dynamic-update="false"> 
  <id name="id" column="id" type="java.lang.integer"> 
    <generator class="native" /> 
  </id> 
  <property name="address" column="address" type="java.lang.string" /> 
  <many-to-one name="user" class="tuser"  
    column="user_id" not-null="true"></many-to-one> 
 
</class> 

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

201613142004701.png (281×148)

下面是t_user表:

201613142019445.png (264×111)

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

public static void main(string[] args) { 
     
  configuration cfg = new configuration().configure(); 
  sessionfactory sessionfactory = cfg.buildsessionfactory(); 
  session session = sessionfactory.opensession(); 
     
  tuser user = (tuser)session.load(tuser.class,new integer(4)); 
  set set = user.getaddresses(); 
  session.close();   
  system.out.println("address size:"+set.size()); 
} 

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

address size:1 

  这是结果!
 你肯定会说,肯定错了吧,是hibernate的bug。这里肯定高兴啦,总算可以提交一个bug了,以前跳槽的时候可以大声说我为hibernate提交过bug。哈哈,但很遗憾,这并不是bug。
 刚才说了我们前面的那个字符串比较的是为这里作铺垫的,那么怎么铺呢?
 我们在配置文件中用set,并且是通过string字符来进行关联的,那么它首先在数据库中取出放进set中的时候会先判断该关联字符的值是否是相等的,这里由于我们的值都是相等的(这里我们暂时不深究它是怎么进行比较的),我们只需要知道当我们用字符串来进行比较的时候,我们又陷入了java中的字符串陷阱了。查出来只有一条,那么删除呢,删除的时候就比较麻烦啦,它会把所有相同的记录都删除。
 那么我们来看一下删除的:

tuser user = (tuser)session.load(tuser.class,new integer(4)); 
     
transaction tx = session.begintransaction(); 
object obj = user.getaddresses().iterator().next(); 
     
user.getaddresses().remove(obj); 
     
tx.commit(); 
session.close(); 

  这里hibernate输出的语句是:

hibernate: delete from t_address where user_id=? 

  相信什么时候大家都知道了,是删除该用户下的所有地址。这没得选择,只能全部都删除。
 所以在真正的开发中需要注意。
 
 2)上面我们讲了set,好像用着不怎么爽啊,有那么个陷阱,但没办法,set是我们用得最多的,而且一般也不会有人直接去关联字符串吧。但很多人还是会不爽,那么hibernate也就应大家要求搞多了一个bag(也许不是应要求,可能它们里面也有人不满,哈哈)。
 我们先来看看它的基本用法:
 首先我们需要把前面的tuser的映射文件中的set标签修改为:

<bag name="addresses" lazy="true" table="t_address"> 
  <key column="user_id" /> 
  <element type="string" column="address" /> 
</bag> 

  并且相应的实体类需要把addresses的类型修改为list类型。
 这里我们重新添加三个地址:

201613142111318.png (283×144)

我们运行测试代码:

public static void main(string[] args) { 
   
  configuration cfg = new configuration().configure(); 
  sessionfactory sessionfactory = cfg.buildsessionfactory(); 
  session session = sessionfactory.opensession(); 
     
  tuser user = (tuser)session.load(tuser.class,new integer(4)); 
     
  system.out.println("address size:"+user.getaddresses().size()); 
  session.close(); 
} 

 
   这里我们看到了:

address size:3 

  这次我们已经全部都可以看到了,不管有没有重复。
 
 但我们刚才看了一个删除的问题,bag在这里还是没有解决,需要借助idbag。我们看到配置文件,需要如下的修改:

idbag name="addresses" table="t_address" lazy="true"> 
  <collection-id type="int" column="id"> 
    <generator class="identity" /> 
  </collection-id> 
  <key column="user_id" /> 
  <element type="string" column="address" /> 
</idbag> 

  我们看到它只比bag多了一个collection-id进行表明要删除的记录号。
 当我们重新运行删除的代码:

tuser user = (tuser)session.load(tuser.class,new integer(4)); 
   
transaction tx = session.begintransaction(); 
object obj = user.getaddresses().iterator().next(); 
user.getaddresses().remove(obj); 
     
tx.commit(); 

  我们看到输出语句为:

hibernate: delete from t_address where id=? 

  这次并不是通过user_id来进行删除,而是根据t_address的id来进行删除,这说明它真正删除我们需要删除的那条记录。
 我们看到数据库,现在记录是:

201613142156751.png (279×123)

我们已经把第一条记录给删了,正确了。
 
 3)看了上面两种方法,我们再来看一下map,它跟上面两个最大的不同就是可以进行键值的对应。直接看代码,直观点:
 首先,我们需要修改配置文件:

<map name="addresses" table="t_address" lazy="true"> 
  <key column="user_id" /> 
  <index type="string" column="type" /> 
  <element type="string" column="address" /> 
</map> 

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

201613142220775.png (371×146)

这里我们看到有两个office和一个home,那么office是拿哪个呢?
 不要急,我们运行一下测试代码就知道了:

tuser user = (tuser)session.load(tuser.class,new integer(4)); 
   
system.out.println(user.getaddresses().get("home")); 
system.out.println(user.getaddresses().get("office")); 
shanwei 
shanghai 

  对,如结果可知,我们取得的是后面那个,这跟map的原理一样,后面存入的值会覆盖前面的值(如果它们是同一个key的情况下)。
 map是比较简单的,相当前两个来说。
 
 4)最后一个我们来看一下list。list与前几种又有不同,不同在它可以进行排序。
 我们来看一下它是怎么实现的:
 首先我们还是修改一下映射文件:

<list name="addresses" table="t_address" lazy="true"> 
  <key column="user_id" /> 
  <index type="string" column="idx" /> 
  <element type="string" column="address" /> 
</list> 

  它和map的配置差不多,但index的属性是不一样的,map中的index是作为key来取得值,而list的index是作为排序的。
 我们看数据库:

201613142257115.png (444×147)

我们设了三个值,顺序分别为0,1,2。
 下面我们运行代码来更改0,2的值:

tuser user = (tuser)session.load(tuser.class,new integer(4)); 
   
transaction tx = session.begintransaction(); 
object obj1 = user.getaddresses().get(0); 
object obj2 = user.getaddresses().get(2); 
     
user.getaddresses().set(0,obj2); 
user.getaddresses().set(2,obj1); 
     
tx.commit(); 

   我们看到结果:

201613142322811.png (443×147)

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网