举例讲解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)