在Hibernate中实现复杂的数据映射

在第一篇文章中,我们对一个表进行了简单的封装。在这篇文章中,我们讨论更加复杂的情况。

在这个例子中,将考虑到表之间的一对一、一对多、多对多的情况。如图1所示。

图1 实体之间的映射关系
图1 实体之间的映射关系

在上面的数据模型图中,Student是所有表的核心,它和Classes表是一对多的关系,和Course表是多对多的关系(通过Student_Course_Link表来链接),和Address表是一对一的关系。

通过分析,我们可以把上面的数据模型转换成如下的Java持久对象,如图2所示。

图2 持久对象之间的关系
图2 持久对象之间的关系

可以看出,数据模型图和Java持久对象的类图有非常大的相似性,但是不完全相同。比如Classes表和Student表是一对多的关系;在类图中,两者仍然是一对多的关系,但是在Classes类中添加了一个student属性,属性的类型是java.util.Set,它表示Classes对象中包含的所有Student对象。

创建Hibernate持久对象

已经对数据模型经过了分析,现在就可以创建持久对象了。持久对象之间的关系由图2所示的类图指定。

我们首先来看Student类,它是这个关系映射的核心,代码如例程1所示。

例程1 Student持久对象(Student.java)

 package com.hellking.study.hibernate;  


import java.util.Set; /**  *在hibernate中代表了Students表的类。  */ 


public class Student  {    /**属性,和students表中的字段对应**/    


private String id;    


private String name;    /**和其它类之间的映射关系**/    


private Set courses;    


private Classes classes;    


private Address address;         /**属性的访问方法,必须是公共的方法**/    


 public void setId(String string) {   id = string;  }  


  public String getId() {   return id;  }    


public void setName(String name)  {   this.name=name;  }  


public String getName()  {   return this.name;  }    /**操作和其它对象之间的关系**/  


public void setCourses(Set co)  {   this.courses=co;  }


  public Set getCourses()  {   return this.courses;  } 


 public void setAddress(Address ad)  {   this.address=address;  }  


public Address getAddress()  {   return this.address;  } 


 public void setClasses(Classes c)  {   this.classes=c;  } 


 public Classes getClasses()  {   return this.classes;  }   }   

在Student类中,由于Students表和Classes的表是多对一的关系,故它包含了一个类型为Classes的classes属性,它的实际意义是一个学生可以有一个班级;Students表和Address的表是一对一的关系,同样也包含了一个类型为Address的address属性,它的实际意义是一个学生有一个地址;Students表和Course是多对多的关系,故它包含了一个类型为java.util.Set的course属性,它的实际意义是一个学生可以学习多门课程,同样,某个课程可以由多个学生来选修。

Classes对象和Student对象是一对多的关系。Classes对象包含一个类型为java.util.Set的students属性,它的代码如例程2所示。

例程2 Classes持久对象(Classes.java)

 package com.hellking.study.hibernate; 


 import java.util.Set; /**  *在hibernate中代表了Classes表的类。  */ 


public class Classes  {    /**属性,和classes表的字段一致**/   


 private String id;     


 private String name;    /**和其它类之间的映射关系**/    


private Set students;        /**属性的访问方法,必须是公共的方法**/   


 public void setId(String string) {   id = string;  }   


 public String getId() {   return id;  }   


 public void setName(String name)  {   this.name=name;  } 


 public String getName()  {   return this.name;  }    /**操作和其它对象之间的关系**/ 


 public void setStudents(Set stud)  {   this.students=stud;  } 


 public Set getStudents()  {   return this.students;  } }   

Course持久对象在前一篇文章已经介绍,在这里就不再列举。Address持久对象比较简单,除了表字段定义的属性外,没有引入其它的属性,请参考本文的代码。

描述对象之间的关系

现在我们已经编写好了持久对象,下面的任务就是描述它们之间的关系。首先我们看Student持久对象的描述,如例程3所示。

例程3 Student持久对象的描述(Student.hbm.xml)

 1<hibernate-mapping>
 2<class dynamic-update="false" name="com.hellking.study.hibernate.Student" table="Students">
 3<!-- 描述ID字段-->
 4<id column="StudentId" name="id" type="string" unsaved-value="any">
 5<generator class="assigned"></generator> </id>
 6<!-- 属性-->
 7<property column="Name" insert="true" name="name" type="string" update="true"></property> <!-- 描述Student和Course多对多的关系-->
 8<set cascade="all" inverse="false" lazy="false" name="courses" sort="unsorted" table="Student_Course_Link">
 9<key column="StudentId"></key>
10<many-to-many class="com.hellking.study.hibernate.Course" column="CourseId" outer-join="auto"></many-to-many>
11</set> <!-- 描述Student和Classes之间多对一的关系-->
12<many-to-one cascade="none" class="com.hellking.study.hibernate.Classes" column="ClassesId" insert="true" name="classes" outer-join="auto" update="true"></many-to-one>
13<!-- 描述Student和Address之间一对一的关系-->
14<one-to-one cascade="none" class="com.hellking.study.hibernate.Address" constrained="false" name="address" outer-join="auto"></one-to-one>
15</class> </hibernate-mapping>

在Student.hbm.xml描述符中,共描述了三种关系。第一种是Student和Address之间一对一的关系,它是最简单的关系,使用:

  1<one-to-one class="" name="">   
  2  
  3---  
  4  
  5来描述,这里的name表示的是Student对象中名称为address的属性;class表示的是address属性的类型:com.hellking.study.hibernate.Address。 
  6
  7接下来看Student和Classes之间多对一的关系,使用: 
  8    
  9    
 10     <many-to-one class="com.hellking.study.hibernate.Classes" column="ClassesId" name="classes" …=""></many-to-one>   
 11  
 12---  
 13  
 14来描述。同样,name表示的是Student对象中名称为classes的属性;class表示的是classes属性的类型,column表示Student表引用Classes表使用的外部键名称。对应的,在Classes类中也引用了Student类,它使用了以下的描述符来描述这个关系: 
 15    
 16    
 17    <set cascade="all" inverse="false" lazy="false" name="students" sort="unsorted" table="Students">
 18<key column="ClassesId"></key>
 19<one-to-many class="com.hellking.study.hibernate.Student"></one-to-many></set>           
 20  
 21---  
 22  
 23在描述Student和Course之间多对多关系时,使用了以下的方法: 
 24    
 25    
 26    <set cascade="all" inverse="false" lazy="false" name="courses" sort="unsorted" table="Student_Course_Link">
 27<key column="StudentId"></key>
 28<many-to-many class="com.hellking.study.hibernate.Course" column="CourseId" outer-join="auto"></many-to-many>
 29</set>           
 30  
 31---  
 32  
 33在映射多对多关系时,需要另外使用一个链接表,这个表的名字由table属性指定,链接表包含了两个字段:CourseId和StudentId。以下的描述: 
 34    
 35    
 36     <key column="StudentId">   
 37  
 38---  
 39  
 40指定了Student对象在Student_Course_Link链接表中的外部键。对应的,在Course持久对象使用了以下的描述符来描述这个关系: 
 41    
 42    
 43    <set cascade="all" inverse="false" lazy="false" name="students" sort="unsorted" table="Student_Course_Link">
 44<key column="CourseId"></key>
 45<many-to-many class="com.hellking.study.hibernate.Student" column="StudentId" outer-join="auto"></many-to-many>
 46</set>           
 47  
 48---  
 49  
 50由于其它持久对象的描述基本一样,在这里就不一一列举了,请参考本文的源代码。最后别忘了在hibernate.cfg.xml里增加这几个对象的描述。 
 51    
 52    
 53    <!-- Mapping files -->
 54<mapping resource="Address.hbm.xml"></mapping>
 55<mapping resource="Student.hbm.xml"></mapping>
 56<mapping resource="Classes.hbm.xml"></mapping>        
 57    
 58    
 59     &lt;mapping resource="Course.hbm.xml"/           
 60  
 61---  
 62  
 63使用映射关系 
 64
 65下面我们开发一个简单的实例来测试这个映射。持久对象使用最频繁的操作是增加数据、查询数据、删除数据、更新数据。对于更新数据的操作的情况,多个表的操作和单个表没有两样,在这里不举例了。 
 66
 67添加数据到数据库 
 68
 69我们在这里测试前三种操作,首先来看添加数据到数据库的情况,如例程4所示。 
 70
 71例程4 测试持久对象之间的映射关系之添加数据(MapTestBean.java部分代码) 
 72    
 73    
 74    /**   *在数据库中添加数据   */   
 75    
 76    
 77    public void addData(String studentId,String classesId,String coursesId)  
 78    
 79    
 80    throws HibernateException {         
 81    
 82    
 83    try {  /***以下的代码添加了一个Student,同时为Student指定了 *Address、Courses和Classses。   */       
 84    
 85    
 86    beginTransaction();    
 87    
 88    
 89    //创建一个Student对象 。                 
 90    
 91    
 92     Student student = new Student();          
 93    
 94    
 95     student.setName("hellking2");           
 96    
 97    
 98    student.setId(studentId);                      
 99    
100    
101    //创建一个Address对象。           
102    
103    
104    Address addr=new Address();           
105    
106    
107    addr.setCity("beijing");           
108    
109    
110    addr.setState("bj");           
111    
112    
113    addr.setStreet("tsinghua");           
114    
115    
116    addr.setZip("100083");           
117    
118    
119    addr.setId(student.getId());            //设置Student和address的关系。           
120    
121    
122    student.setAddress(addr);                           
123    
124    
125    Set set=new HashSet();          
126    
127    
128    set.add(student);          //创建一个course对象。          
129    
130    
131    Course course=new  Course  ();          
132    
133    
134    course.setId(coursesId);         
135    
136    
137     course.setName("computer_jsp"); //设置course和student对象之间的关系。         
138    
139    
140     course.setStudents(set);     //创建一个classes对象。          
141    
142    
143    Classes cl=new Classes();          
144    
145    
146    cl.setId(classesId);         
147    
148    
149    cl.setName("engine power");   //设置某个classes对象包含的students对象。         
150    
151    
152    cl.setStudents(set);         //由于是双向的关系,student对象也需要设置一次。       
153    
154    
155    student.setClasses(cl);     //保存创建的对象到session中。         
156    
157    
158     session.save(cl);          
159    
160    
161    session.save(course);          
162    
163    
164    session.save(student);          
165    
166    
167    session.save(addr);          //提交事务,使更改生效。          
168    
169    
170    endTransaction(true);  }        
171    
172    
173    catch(HibernateException e)  {                     
174    
175    
176    System.out.println("在添加数据时出错!");            
177    
178    
179    e.printStackTrace();            
180    
181    
182    throw e;        }     }       
183  
184---  
185  
186在例程4中,添加数据到数据库之前,首先设置持久对象的各个属性,如: 
187    
188    
189     student.setName("hellking2");   
190  
191---  
192  
193这种设置属性的方式和普通的类没有什么区别,设置完所有的属性后,就设置持久对象之间的关系,如: 
194    
195    
196     student.setAddress(addr);   
197  
198---  
199  
200如果存在对象之间的多重关系,那么可能需要把对象保存在Set集合中,然后再进行设置,如: 
201    
202    
203     Set set=new HashSet(); set.add(student); course.setStudents(set);   
204  
205---  
206  
207当设置完所有的属性和对象关系之后,就可以调用: 
208    
209    
210     session.save(persistentObject);   
211  
212---  
213  
214方法把持久对象保存到Hibernate会话中。最后,调用endTransaction来提交事务,并且关闭Hibernate会话。 
215
216数据查询 
217
218在复杂的实体对象映射中,往往查询也比较复杂。作为演示,我们在这里也提供了几个查询方法,如例程5所示。 
219
220例程5 测试持久对象之间的映射关系之查询数据(MapTestBean.java部分代码) 
221    
222    
223         /**      *获得某个给定studentid的Student的地址信息      */     
224    
225    
226    public Address getAddress(String id) throws HibernateException {   
227    
228    
229     beginTransaction();            
230    
231    
232    Student st=(Student)session.load(Student.class,id);           
233    
234    
235    Address addr=(Address)session.load(Address.class,st.getId());       
236    
237    
238    endTransaction(false);     
239    
240    
241    return addr;  } /***获得某个给定studentid的Student的所有课程  */     
242    
243    
244    public Set getCourses(String id)throws HibernateException {  
245    
246    
247    beginTransaction();           
248    
249    
250    Student st=(Student)session.load(Student.class,id);       
251    
252    
253    endTransaction(false);          
254    
255    
256    return st.getCourses();  } /***测试获得某个给定studentid的Student所属的Classes */    
257    
258    
259    public Classes getClasses(String id)throws HibernateException {           
260    
261    
262    beginTransaction();           
263    
264    
265    Student st=(Student)session.load(Student.class,id);       
266    
267    
268    System.out.println(st.getClasses().getId());        
269    
270    
271    endTransaction(false);       
272    
273    
274    return st.getClasses();     }       
275  
276---  
277  
278这里提供了三种查询方法,分别是: 
279
280  * 查询给定id的Student的Address信息; 
281  * 查询给定id的Student的所有Courses信息; 
282  * 查询给定id的Student所属的Classes信息。 
283
284
285
286在查询时,首先使用beginTransaction()方法创建一个Hibernate会话对象,并且开始一个新Hibernate事务;然后通过session.load()方法获得给定ID的Student对象,如: 
287    
288    
289     Student st=(Student)session.load(Student.class,id);   
290  
291---  
292  
293最后调用student.getXXX()方法返回指定的对象。 
294
295删除数据 
296
297在表的关系比较复杂时,要删除数据,往往存在级联删除的情况,由于级联删除的情况比较复杂,在这里就不举例了。假设我们要删除和某个给定id的student对象的所有相关的记录,就可以使用例程6所示的方法。 
298
299例程6 测试持久对象之间的映射关系之删除数据(MapTestBean.java部分代码) 
300    
301    
302    /***删除和某个学生相关的所有信息*(这里只是测试,我们暂且不说这种操作的意义何在)。*/    
303    
304    
305     public void delteStudent(String id)throws HibernateException{      
306    
307    
308    beginTransaction();           
309    
310    
311    Student st=(Student)session.load(Student.class,id);            
312    
313    
314    Address addr=(Address)session.load(Address.class,st.getId()); //删除address信息。      
315    
316    
317    session.delete(addr); //删除classes信息。     
318    
319    
320     session.delete(st.getClasses()); /***逐个删除course。*/        
321    
322    
323     for(Iterator it=st.getCourses().iterator();it.hasNext();) {          
324    
325    
326    Course c=(Course)it.next();          
327    
328    
329    session.delete(c); }//最后,删除student对象。      
330    
331    
332    session.delete(st);      
333    
334    
335    endTransaction(true); }       
336  
337---  
338  
339同样,在执行删除前,首先使用beginTransaction()方法创建一个新Hibernate会话和一个新Hibernate事务,然后把要删除的对象Load进来,接下来调用session.delete()方法来删除指定对象。 
340
341如果要删除的是集合中的对象,那么可以通过一个迭代来逐个删除,如例程6中删除courses的方法。 
342
343测试 
344
345在这里提供了在JSP中调用MapTestBean进行测试的程序,具体代码见maptest.jsp文件。在进行测试前,请确保连接数据库的配置完好,并且每个持久对象的配置都没有错误。如果配置出现困难,请参考本文的源代码。 
346
347行动起来 
348
349经过两篇文章由浅入深的学习,希望能够起到抛砖引玉的作用,相信读者对Hibernate的认识已经有一个整体的把握。Hibernate由于它的易用性和良好的移植性等特点,逐渐在企业级应用开发中广泛使用。Hibernate官方网站提供了非常好的使用手册,您可以参考它。如果您并非精通JDBC并且不想学习它,不妨考虑使用Hibernate;如果您在使用实体Bean之类的持久框架遇到困难,也许Hibernate可以助你一臂之力!</key></one-to-one>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus