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

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方法中进行了修改。

我们看到数据库中的值已经修改为有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,可以看到。

我们看到的就是这样一个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表:

下面是t_user表:

我们可以看到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类型。
 这里我们重新添加三个地址:

我们运行测试代码:

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来进行删除,这说明它真正删除我们需要删除的那条记录。
 我们看到数据库,现在记录是:

我们已经把第一条记录给删了,正确了。
 
 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。
 看看数据库的数据:

这里我们看到有两个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是作为排序的。
 我们看数据库:

我们设了三个值,顺序分别为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();

我们看到结果:

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

(0)

相关推荐

  • Java中的collection集合类型总结

    Java集合是java提供的工具包,包含了常用的数据结构:集合.链表.队列.栈.数组.映射等.Java集合工具包位置是java.util.* Java集合主要可以划分为4个部分:List列表.Set集合.Map映射.工具类(Iterator迭代器.Enumeration枚举类.Arrays和Collections). Java集合工具包框架如下图. 说明:看上面的框架图,先抓住它的主干,即Collection和Map. Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作

  • Java集合框架之Collection接口详解

    Java是一门面向对象的语言,那么我们写程序的时候最经常操作的便是对象了,为此,Java提供了一些专门用来处理对象的类库,这些类库的集合我们称之为集合框架.Java集合工具包位于Java.util包下,包含了很多常用的数据结构,如数组.链表.栈.队列.集合.哈希表等.学习Java集合框架下大致可以分为如下五个部分:List列表.Set集合.Map映射.迭代器(Iterator.Enumeration).工具类(Arrays.Collections). Java的集合类主要由两个接口派生而出:Co

  • 详解java中的Collections类

    一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数组无法完全表达你要表达的东西,而定义一个类Class有太过麻烦,这时候,你可以考虑一下用Java中的Collections类.使用Collections类,必须在文件头声明import java.util.*; 一.动态.有序.可变大小的一维数组Vector与ArrayList  Collectio

  • java 的Collection接口实例详解

    1. Collection是集合类的一个顶级接口,其直接继承接口有List与Set. Collection     |--List:元素是有序的,元素可以重复.因为该集合体系有索引.         |--ArrayList:底层的数据结构使用的是数组结构.特点:查询速度很快.但是增删稍慢.线程不同步.         |--LinkedList:底层使用的链表数据结构.特点:增删速度很快,查询稍慢.线程不同步.         |--Vector:底层是数组数据结构.线程同步.被ArrayLi

  • 全面了解JavaScirpt 的垃圾(garbage collection)回收机制

    一.垃圾回收机制-GC Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存. 原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存. JavaScript垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行. 不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,全局变量的生

  • java Collection 之List学习介绍

    List 一个有序的集合,允许元素的重复:该实现不是同步的,如果多个线程访问一个List实例,而其中至少一个线程从结构上修改了表(添加或删除元素),那么它需要保持外部同步:一般通过对自然封装该列表的对象进行同步操作来完成,如:Collections.synchronizedList()来包装列表: ArrayList List接口的大小可变数组的实现,实现了所有可选列表操作,并且允许包括null在内的所有元素, LinkedList List接口的链接列表实现,允许元素为null,实现所有可选的

  • 浅谈Java中Collection和Collections的区别

    1.java.util.Collection 是一个集合接口.它提供了对集合对象进行基本操作的通用接口方法.Collection接口在Java 类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式. Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set 2.java.util.Collections 是一个包装类.它包含有各种有关集合操作的静态多态方法.此类不能实例化,就像一

  • 用Java集合中的Collections.sort方法如何对list排序(两种方法)

    第一种是list中的对象实现Comparable接口,如下: /** * 根据order对User排序 */ public class User implements Comparable <user> { private String name; private Integer order; public String getName() { return name; } public void setName(String name) { this.name = name; } publi

  • java Collection 之Set使用说明

    Set 一个不包含重复元素的collection,确切的讲,set不包含满足e1.equals(e2)的元素对e1,e2,并且最多包含一个null元素: 此实现不是同步的,如果多个线程同时访问一个set,而其中至少一个线程修改了该set,那么它必须保持外部同步,通常是通过对自然封装该set的对象执行同步操作来完成,如果不存在这样的对象,则:Collections.synchronizedSet(): HashSet 集合中元素无顺序,不重复:数据结构是哈希表: 保证元素唯一性的原理:判断元素的H

  • java TO ArrayCollection

    复制代码 代码如下: private var javalist:ArrayCollection; function resultHandler(event:ResultEvent):void{ javalist=ArrayCollection(event.result); //javalist=event.result as ArrayCollection; javabar.dataProvider=javalist; } JAVA代码返回java.util.List对象, 因为new Arra

随机推荐