举例讲解Java的Hibernate框架中的多对一和一对多映射
多对一(Many-to-One)映射
多对一(many-to-one)关联是最常见的关联关系,其中一个对象可以与多个对象相关联。例如,一个相同的地址对象可以与多个雇员的对象相关联。
定义RDBMS表:
考虑一个情况,我们需要员工记录存储在EMPLOYEE表,将有以下结构:
createtableEMPLOYEE( idINTNOTNULLauto_increment, first_nameVARCHAR(20)defaultNULL, last_nameVARCHAR(20)defaultNULL, salaryINTdefaultNULL, addressINTNOTNULL, PRIMARYKEY(id) );
此外,许多员工都可以有相同的地址,所以这种关联可以使用许多一对一的关联呈现。我们将存储地址相关的信息在一个单独的表,该表具有以下结构:
createtableADDRESS( idINTNOTNULLauto_increment, street_nameVARCHAR(40)defaultNULL, city_nameVARCHAR(40)defaultNULL, state_nameVARCHAR(40)defaultNULL, zipcodeVARCHAR(10)defaultNULL, PRIMARYKEY(id) );
同时创建RBDMS表,并让他们准备下一个实现。
定义POJO类:
让我们实现一个POJO类员工将被用于保存与EMPLOYEE表的对象和其地址类型的变量。
importjava.util.*; publicclassEmployee{ privateintid; privateStringfirstName; privateStringlastName; privateintsalary; privateAddressaddress; publicEmployee(){} publicEmployee(Stringfname,Stringlname, intsalary,Addressaddress){ this.firstName=fname; this.lastName=lname; this.salary=salary; this.address=address; } publicintgetId(){ returnid; } publicvoidsetId(intid){ this.id=id; } publicStringgetFirstName(){ returnfirstName; } publicvoidsetFirstName(Stringfirst_name){ this.firstName=first_name; } publicStringgetLastName(){ returnlastName; } publicvoidsetLastName(Stringlast_name){ this.lastName=last_name; } publicintgetSalary(){ returnsalary; } publicvoidsetSalary(intsalary){ this.salary=salary; } publicAddressgetAddress(){ returnaddress; } publicvoidsetAddress(Addressaddress){ this.address=address; } }
我们需要定义相应的地址表,这样地址对象可以存储和检索到地址表中的另一个POJO类。
importjava.util.*; publicclassAddress{ privateintid; privateStringstreet; privateStringcity; privateStringstate; privateStringzipcode; publicAddress(){} publicAddress(Stringstreet,Stringcity, Stringstate,Stringzipcode){ this.street=street; this.city=city; this.state=state; this.zipcode=zipcode; } publicintgetId(){ returnid; } publicvoidsetId(intid){ this.id=id; } publicStringgetStreet(){ returnstreet; } publicvoidsetStreet(Stringstreet){ this.street=street; } publicStringgetCity(){ returncity; } publicvoidsetCity(Stringcity){ this.city=city; } publicStringgetState(){ returnstate; } publicvoidsetState(Stringstate){ this.state=state; } publicStringgetZipcode(){ returnzipcode; } publicvoidsetZipcode(Stringzipcode){ this.zipcode=zipcode; } }
定义Hibernate映射文件:
开发我们的映射文件,可指示Hibernate如何定义的类映射到数据库表。<many-to-one>进行元素将被用来定义规则建立Employee和Address实体之间的多对一关系。
<?xmlversion="1.0"encoding="utf-8"?> <!DOCTYPEhibernate-mappingPUBLIC "-//Hibernate/HibernateMappingDTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <classname="Employee"table="EMPLOYEE"> <metaattribute="class-description"> Thisclasscontainstheemployeedetail. </meta> <idname="id"type="int"column="id"> <generatorclass="native"/> </id> <propertyname="firstName"column="first_name"type="string"/> <propertyname="lastName"column="last_name"type="string"/> <propertyname="salary"column="salary"type="int"/> <many-to-onename="address"column="address" class="Address"not-null="true"/> </class> <classname="Address"table="ADDRESS"> <metaattribute="class-description"> Thisclasscontainstheaddressdetail. </meta> <idname="id"type="int"column="id"> <generatorclass="native"/> </id> <propertyname="street"column="street_name"type="string"/> <propertyname="city"column="city_name"type="string"/> <propertyname="state"column="state_name"type="string"/> <propertyname="zipcode"column="zipcode"type="string"/> </class> </hibernate-mapping>
应该保存的映射文件中的格式<classname>.hbm.xml。保存映射文件中的文件Employee.hbm.xml。已经熟悉了大部分的映射细节,但让我们再次看看映射文件中的所有元素:
映射文档是具有<hibernate-mapping>为对应于每一个类包含2个<class>元素的根元素的XML文档。
<class>元素被用于定义数据库表从一个Java类特定的映射。Java类名指定使用class元素的name属性和使用表属性数据库表名指定。
<meta>元素是可选元素,可以用来创建类的描述。
<id>元素映射在类中的唯一ID属性到数据库表的主键。id元素的name属性是指属性的类和column属性是指在数据库表中的列。type属性保存了Hibernate映射类型,这种类型的映射将会从Java转换为SQL数据类型。
id元素内<generator>元素被用来自动生成的主键值。将生成元素class属性设置为原生让Hibernate拾取无论是identity,sequence或者hilo的算法来创建主键根据底层数据库的支持能力。
<property>元素用于一个Java类的属性映射到数据库表中的列。元素的name属性是指属性的类和column属性是指在数据库表中的列。type属性保存了Hibernate映射类型,这种类型的映射将会从Java转换为SQL数据类型。
<many-to-one>进行元素是用来设置EMPLOYEE和地址的实体之间的关系。name属性被设置为在父类中定义的变量,在我们的情况下,它是地址。列属性用于在父表EMPLOYEE集的列名。
最后,我们将创建应用程序类的main()方法来运行应用程序。我们将使用这个应用程序,以节省一些employee连同的记录他们的地址,然后我们将申请CRUD操作上的记录。
importjava.util.*; importorg.hibernate.HibernateException; importorg.hibernate.Session; importorg.hibernate.Transaction; importorg.hibernate.SessionFactory; importorg.hibernate.cfg.Configuration; publicclassManageEmployee{ privatestaticSessionFactoryfactory; publicstaticvoidmain(String[]args){ try{ factory=newConfiguration().configure().buildSessionFactory(); }catch(Throwableex){ System.err.println("FailedtocreatesessionFactoryobject."+ex); thrownewExceptionInInitializerError(ex); } ManageEmployeeME=newManageEmployee(); /*Letushaveoneaddressobject*/ Addressaddress=ME.addAddress("Kondapur","Hyderabad","AP","532"); /*Addemployeerecordsinthedatabase*/ IntegerempID1=ME.addEmployee("Manoj","Kumar",4000,address); /*Addanotheremployeerecordinthedatabase*/ IntegerempID2=ME.addEmployee("Dilip","Kumar",3000,address); /*Listdownalltheemployees*/ ME.listEmployees(); /*Updateemployee'ssalaryrecords*/ ME.updateEmployee(empID1,5000); /*Deleteanemployeefromthedatabase*/ ME.deleteEmployee(empID2); /*Listdownalltheemployees*/ ME.listEmployees(); } /*Methodtoaddanaddressrecordinthedatabase*/ publicAddressaddAddress(Stringstreet,Stringcity, Stringstate,Stringzipcode){ Sessionsession=factory.openSession(); Transactiontx=null; IntegeraddressID=null; Addressaddress=null; try{ tx=session.beginTransaction(); address=newAddress(street,city,state,zipcode); addressID=(Integer)session.save(address); tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } returnaddress; } /*Methodtoaddanemployeerecordinthedatabase*/ publicIntegeraddEmployee(Stringfname,Stringlname, intsalary,Addressaddress){ Sessionsession=factory.openSession(); Transactiontx=null; IntegeremployeeID=null; try{ tx=session.beginTransaction(); Employeeemployee=newEmployee(fname,lname,salary,address); employeeID=(Integer)session.save(employee); tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } returnemployeeID; } /*Methodtolistalltheemployeesdetail*/ publicvoidlistEmployees(){ Sessionsession=factory.openSession(); Transactiontx=null; try{ tx=session.beginTransaction(); Listemployees=session.createQuery("FROMEmployee").list(); for(Iteratoriterator= employees.iterator();iterator.hasNext();){ Employeeemployee=(Employee)iterator.next(); System.out.print("FirstName:"+employee.getFirstName()); System.out.print("LastName:"+employee.getLastName()); System.out.println("Salary:"+employee.getSalary()); Addressadd=employee.getAddress(); System.out.println("Address"); System.out.println("Street:"+add.getStreet()); System.out.println("City:"+add.getCity()); System.out.println("State:"+add.getState()); System.out.println("Zipcode:"+add.getZipcode()); } tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } } /*Methodtoupdatesalaryforanemployee*/ publicvoidupdateEmployee(IntegerEmployeeID,intsalary){ Sessionsession=factory.openSession(); Transactiontx=null; try{ tx=session.beginTransaction(); Employeeemployee= (Employee)session.get(Employee.class,EmployeeID); employee.setSalary(salary); session.update(employee); tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } } /*Methodtodeleteanemployeefromtherecords*/ publicvoiddeleteEmployee(IntegerEmployeeID){ Sessionsession=factory.openSession(); Transactiontx=null; try{ tx=session.beginTransaction(); Employeeemployee= (Employee)session.get(Employee.class,EmployeeID); session.delete(employee); tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } } }
编译和执行:
下面是步骤来编译并运行上述应用程序。请确保已在进行的编译和执行之前,适当地设置PATH和CLASSPATH。
- 创建hibernate.cfg.xml配置文件中配置章节解释。
- 创建Employee.hbm.xml映射文件,如上图所示。
- 创建Employee.java源文件,如上图所示,并编译它。
- 创建Address.java源文件,如上图所示,并编译它。
- 创建ManageEmployee.java源文件,如上图所示,并编译它。
- 执行ManageEmployee二进制文件来运行程序。
在屏幕上获得以下结果,并同时记录会在员工和地址表创建。
$javaManageEmployee
.......VARIOUSLOGMESSAGESWILLDISPLAYHERE........ FirstName:ManojLastName:KumarSalary:4000 Address Street:Kondapur City:Hyderabad State:AP Zipcode:532 FirstName:DilipLastName:KumarSalary:3000 Address Street:Kondapur City:Hyderabad State:AP Zipcode:532 FirstName:ManojLastName:KumarSalary:5000 Address Street:Kondapur City:Hyderabad State:AP Zipcode:532
如果检查员工和地址表,就应该记录下了:
mysql>select*fromEMPLOYEE;
+----+------------+-----------+--------+---------+ |id|first_name|last_name|salary|address| +----+------------+-----------+--------+---------+ |1|Manoj|Kumar|5000|5| +----+------------+-----------+--------+---------+ 1rowinset(0.00sec)
mysql>select*fromADDRESS;
+----+-------------+-----------+------------+---------+ |id|street_name|city_name|state_name|zipcode| +----+-------------+-----------+------------+---------+ |1|Kondapur|Hyderabad|AP|532| +----+-------------+-----------+------------+---------+ 1rowinset(0.00sec)
一对多(One-to-Many)映射
一对多的映射可以使用一组Java集合不包含任何重复的元素来实现。我们已经看到了如何设置映射集合在Hibernate中,所以如果你已经学会了集合(Set)映射,那么所有设置可用于一对多的映射。
集合被映射到与映射表中<set>元素,并java.util.HashSet中初始化。您可以使用Set集合在类中,有一个集合中不需要重复的元素。
RDBMS表与POJO类我们依然采用上面例子中定义好的,
定义Hibernate映射文件:
让我们指示Hibernate如何定义的类映射到数据库表的映射文件。
<?xmlversion="1.0"encoding="utf-8"?> <!DOCTYPEhibernate-mappingPUBLIC "-//Hibernate/HibernateMappingDTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <classname="Employee"table="EMPLOYEE"> <metaattribute="class-description"> Thisclasscontainstheemployeedetail. </meta> <idname="id"type="int"column="id"> <generatorclass="native"/> </id> <setname="certificates"cascade="all"> <keycolumn="employee_id"/> <one-to-manyclass="Certificate"/> </set> <propertyname="firstName"column="first_name"type="string"/> <propertyname="lastName"column="last_name"type="string"/> <propertyname="salary"column="salary"type="int"/> </class> <classname="Certificate"table="CERTIFICATE"> <metaattribute="class-description"> Thisclasscontainsthecertificaterecords. </meta> <idname="id"type="int"column="id"> <generatorclass="native"/> </id> <propertyname="name"column="certificate_name"type="string"/> </class> </hibernate-mapping>
应该保存的映射文件中的格式<classname>.hbm.xml。我们保存映射文件中的文件Employee.hbm.xml。你已经熟悉了大部分的映射细节,但让我们再次看看映射文件中的所有元素:
映射文档是具有<hibernate-mapping>为对应于每一个类包含2个<class>元素的根元素的XML文档。
<class>元素被用于定义数据库表从一个Java类特定的映射。Java类名指定使用class元素的name属性和使用表属性数据库表名指定。
<meta>元素是可选元素,可以用来创建类的描述。
<id>元素映射在类中的唯一ID属性到数据库表的主键。id元素的name属性是指属性的类和column属性是指在数据库表中的列。type属性保存了Hibernate映射类型,这种类型的映射将会从Java转换为SQL数据类型。
id元素内的<generator>元素被用来自动生成的主键值。将生成元素的class属性设置为原生让Hibernate拾取identity,sequence或者hilo中的算法来创建主键根据底层数据库的支持能力。
<property>元素用于一个Java类的属性映射到数据库表中的列。元素的name属性是指属性的类和column属性是指在数据库表中的列。type属性保存了Hibernate映射类型,这种类型的映射将会从Java转换为SQL数据类型。
<set>元素设置证书和Employee类之间的关系。我们使用cascade属性中<set>元素来告诉Hibernate来保存证书的对象,同时为Employee对象。name属性被设置为在父类中定义的变量集,在我们的例子是证书。对于每一组变量,我们需要定义在映射文件中单独的一组元素。
<key>元素是包含外键的父对象,即在证书表中的列。表EMPLOYEE。
<one-to-many>元素表示一个Employee对象涉及到很多证书的对象。
创建应用程序类:
最后,我们将创建应用程序类的main()方法来运行应用程序。我们将使用这个应用程序,以节省一些员工连同记录证书,然后我们将应用上CRUD操作记录。
importjava.util.*; importorg.hibernate.HibernateException; importorg.hibernate.Session; importorg.hibernate.Transaction; importorg.hibernate.SessionFactory; importorg.hibernate.cfg.Configuration; publicclassManageEmployee{ privatestaticSessionFactoryfactory; publicstaticvoidmain(String[]args){ try{ factory=newConfiguration().configure().buildSessionFactory(); }catch(Throwableex){ System.err.println("FailedtocreatesessionFactoryobject."+ex); thrownewExceptionInInitializerError(ex); } ManageEmployeeME=newManageEmployee(); /*Letushaveasetofcertificatesforthefirstemployee*/ HashSetset1=newHashSet(); set1.add(newCertificate("MCA")); set1.add(newCertificate("MBA")); set1.add(newCertificate("PMP")); /*Addemployeerecordsinthedatabase*/ IntegerempID1=ME.addEmployee("Manoj","Kumar",4000,set1); /*Anothersetofcertificatesforthesecondemployee*/ HashSetset2=newHashSet(); set2.add(newCertificate("BCA")); set2.add(newCertificate("BA")); /*Addanotheremployeerecordinthedatabase*/ IntegerempID2=ME.addEmployee("Dilip","Kumar",3000,set2); /*Listdownalltheemployees*/ ME.listEmployees(); /*Updateemployee'ssalaryrecords*/ ME.updateEmployee(empID1,5000); /*Deleteanemployeefromthedatabase*/ ME.deleteEmployee(empID2); /*Listdownalltheemployees*/ ME.listEmployees(); } /*Methodtoaddanemployeerecordinthedatabase*/ publicIntegeraddEmployee(Stringfname,Stringlname, intsalary,Setcert){ Sessionsession=factory.openSession(); Transactiontx=null; IntegeremployeeID=null; try{ tx=session.beginTransaction(); Employeeemployee=newEmployee(fname,lname,salary); employee.setCertificates(cert); employeeID=(Integer)session.save(employee); tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } returnemployeeID; } /*Methodtolistalltheemployeesdetail*/ publicvoidlistEmployees(){ Sessionsession=factory.openSession(); Transactiontx=null; try{ tx=session.beginTransaction(); Listemployees=session.createQuery("FROMEmployee").list(); for(Iteratoriterator1= employees.iterator();iterator1.hasNext();){ Employeeemployee=(Employee)iterator1.next(); System.out.print("FirstName:"+employee.getFirstName()); System.out.print("LastName:"+employee.getLastName()); System.out.println("Salary:"+employee.getSalary()); Setcertificates=employee.getCertificates(); for(Iteratoriterator2= certificates.iterator();iterator2.hasNext();){ CertificatecertName=(Certificate)iterator2.next(); System.out.println("Certificate:"+certName.getName()); } } tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } } /*Methodtoupdatesalaryforanemployee*/ publicvoidupdateEmployee(IntegerEmployeeID,intsalary){ Sessionsession=factory.openSession(); Transactiontx=null; try{ tx=session.beginTransaction(); Employeeemployee= (Employee)session.get(Employee.class,EmployeeID); employee.setSalary(salary); session.update(employee); tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } } /*Methodtodeleteanemployeefromtherecords*/ publicvoiddeleteEmployee(IntegerEmployeeID){ Sessionsession=factory.openSession(); Transactiontx=null; try{ tx=session.beginTransaction(); Employeeemployee= (Employee)session.get(Employee.class,EmployeeID); session.delete(employee); tx.commit(); }catch(HibernateExceptione){ if(tx!=null)tx.rollback(); e.printStackTrace(); }finally{ session.close(); } } }
编译和执行:
$javaManageEmployee
.......VARIOUSLOGMESSAGESWILLDISPLAYHERE........ FirstName:ManojLastName:KumarSalary:4000 Certificate:MBA Certificate:PMP Certificate:MCA FirstName:DilipLastName:KumarSalary:3000 Certificate:BCA Certificate:BA FirstName:ManojLastName:KumarSalary:5000 Certificate:MBA Certificate:PMP Certificate:MCA
如果检查员工和证书表,就应该记录下了:
mysql>select*fromemployee;
+----+------------+-----------+--------+ |id|first_name|last_name|salary| +----+------------+-----------+--------+ |1|Manoj|Kumar|5000| +----+------------+-----------+--------+ 1rowinset(0.00sec)
mysql>select*fromcertificate;
+----+------------------+-------------+ |id|certificate_name|employee_id| +----+------------------+-------------+ |1|MBA|1| |2|PMP|1| |3|MCA|1| +----+------------------+-------------+ 3rowsinset(0.00sec)