2008年12月9日星期二

彻底掌握hibernate对象关联映射

    有很长时间没写博客了,前些天主要忙孩子的事情,我终于当爹啦,心情难以平复啊.他是个很可爱的小胖小子,很可惜,现在他并不在我身边,真是让我感觉寂寞难耐啊,做什么事情都没什么心情,心思都在我那可爱的儿子那里呢,真盼望元旦快些到来啊,我好回去亲亲我的好儿子.
    回到正题来,今天我们的目的是要掌握hibernate对象关联映射,设计对象关联映射是hibernate开发过程中的主要内容,老是有许多同学对这块理解、实践不够。那么我这次正好再详细的讲解一次,图文并茂,希望大家认真练习,把它彻底掌握。

要求基础:《Struts/Spring/Hibernate/Ajax的网上信息发布平台》(y2学期)


首先,我们来看一下此次设计的数据库概念模型图(使用PowerDesigner12.5建立的CDM):














    下面我来描述一下这个概念模型,一共4个表,雇员表,雇员薪水表,部门表,项目表。其中雇员表和雇员薪水表是一对一的关系,雇员表与部门表是多对一的关系,部门表和项目表是多对多的关系。
下面是物理模型图(使用PowerDesigner12.5建立的PDM):



以下是生成的数据库脚本:

/*==============================================================*/
/* DBMS name: Microsoft SQL Server 2005 */
/* Created on: 2008-12-11 13:43:14 */
/*==============================================================*/


if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Employees') and o.name = 'FK_EMPLOYEE_EMPLOYEES_SECTOR')
alter table Employees
drop constraint FK_EMPLOYEE_EMPLOYEES_SECTOR
go

if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Salary') and o.name = 'FK_SALARY_EMPLOYEE__EMPLOYEE')
alter table Salary
drop constraint FK_SALARY_EMPLOYEE__EMPLOYEE
go

if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Sector_Project') and o.name = 'FK_SECTOR_P_M_PROJECT')
alter table Sector_Project
drop constraint FK_SECTOR_P_M_PROJECT
go

if exists (select 1
from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F')
where r.fkeyid = object_id('Sector_Project') and o.name = 'FK_SECTOR_P_N_SECTOR')
alter table Sector_Project
drop constraint FK_SECTOR_P_N_SECTOR
go

if exists (select 1
from sysindexes
where id = object_id('Employees')
and name = 'Employees_Sector_FK'
and indid > 0
and indid < 255)
drop index Employees.Employees_Sector_FK
go

if exists (select 1
from sysobjects
where id = object_id('Employees')
and type = 'U')
drop table Employees
go

if exists (select 1
from sysindexes
where id = object_id('Salary')
and name = 'Employee_Salary_FK'
and indid > 0
and indid < 255)
drop index Salary.Employee_Salary_FK
go

if exists (select 1
from sysobjects
where id = object_id('Salary')
and type = 'U')
drop table Salary
go

if exists (select 1
from sysobjects
where id = object_id('Sector')
and type = 'U')
drop table Sector
go

if exists (select 1
from sysindexes
where id = object_id('Sector_Project')
and name = 'm_FK'
and indid > 0
and indid < 255)
drop index Sector_Project.m_FK
go

if exists (select 1
from sysindexes
where id = object_id('Sector_Project')
and name = 'n_FK'
and indid > 0
and indid < 255)
drop index Sector_Project.n_FK
go

if exists (select 1
from sysobjects
where id = object_id('Sector_Project')
and type = 'U')
drop table Sector_Project
go

if exists (select 1
from sysobjects
where id = object_id('project')
and type = 'U')
drop table project
go

/*==============================================================*/
/* Table: Employees */
/*==============================================================*/
create table Employees (
employeeId int identity,
sectorId int not null,
employeeName varchar(30) null,
regTime datetime null,
constraint PK_EMPLOYEES primary key nonclustered (employeeId)
)
go

declare @CurrentUser sysname
select @CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'雇员表,雇员的基本信息',
'user', @CurrentUser, 'table', 'Employees'
go

/*==============================================================*/
/* Index: Employees_Sector_FK */
/*==============================================================*/
create index Employees_Sector_FK on Employees (
sectorId ASC
)
go

/*==============================================================*/
/* Table: Salary */
/*==============================================================*/
create table Salary (
salaryId int identity,
employeeId int not null,
money money null,
constraint PK_SALARY primary key nonclustered (salaryId)
)
go

/*==============================================================*/
/* Index: Employee_Salary_FK */
/*==============================================================*/
create index Employee_Salary_FK on Salary (
employeeId ASC
)
go

/*==============================================================*/
/* Table: Sector */
/*==============================================================*/
create table Sector (
sectorId int identity,
sectorName varchar(30) null,
constraint PK_SECTOR primary key nonclustered (sectorId)
)
go

declare @CurrentUser sysname
select @CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'部门表',
'user', @CurrentUser, 'table', 'Sector'
go

/*==============================================================*/
/* Table: Sector_Project */
/*==============================================================*/
create table Sector_Project (
sectorId int not null,
projectId int not null,
constraint PK_SECTOR_PROJECT primary key (sectorId, projectId)
)
go

/*==============================================================*/
/* Index: n_FK */
/*==============================================================*/
create index n_FK on Sector_Project (
sectorId ASC
)
go

/*==============================================================*/
/* Index: m_FK */
/*==============================================================*/
create index m_FK on Sector_Project (
projectId ASC
)
go

/*==============================================================*/
/* Table: project */
/*==============================================================*/
create table project (
projectId int identity,
projectName varchar(30) null,
constraint PK_PROJECT primary key nonclustered (projectId)
)
go

alter table Employees
add constraint FK_EMPLOYEE_EMPLOYEES_SECTOR foreign key (sectorId)
references Sector (sectorId)
go

alter table Salary
add constraint FK_SALARY_EMPLOYEE__EMPLOYEE foreign key (employeeId)
references Employees (employeeId)
go

alter table Sector_Project
add constraint FK_SECTOR_P_M_PROJECT foreign key (projectId)
references project (projectId)
go

alter table Sector_Project
add constraint FK_SECTOR_P_N_SECTOR foreign key (sectorId)
references Sector (sectorId)
go



下面我们看看sql2005里的数据库关系图:





好了,现在我们建立一个hibernate工程。(步骤省略,这里实在太简单了,我们还是直接切入正题)



下面,我们进行数据库的反向工程,这样类和映射文件,以及dao(数据访问对象)都可以被创建好。我们根据映射文件来学习对象关联映射。



反向工程步骤:



1.先建立个数据库连接,使用sqlserver2005摸板,别忘了加数据库驱动的jar包,设置完后点击test driver按钮测试一下连接能否连通。




2.找到相应的库表:



3.开始反向工程:








这里Java src folder是你项目的源文件夹,Java package是你要生成实体和映射文件的地方。
然后点击下一步:




我们有多对多的表,所以这里要钩上Enable many-to-many detection ,然后直接点击完成即可。


然后回到我们的项目下,看看都生成了哪些东西呢?





我们可以看到,在com包生成了hibernateSessionFactory工具类,entity包里生成了实体类以及相应的配置文件,而且我们看到右边hibernate.cfg.xml配置文件已经加入了新建的实体映射文件。这个工具真的是很方便,减少了不少麻烦,至少目前为止,你一行代码都没写过呢!!!

在这里,我们还要在配置文件里新建个属性,以便于一会我们进行对象关联持久化的时候可以看到hibernate生成的语句。

我们在下面点击Add按钮新建个属性:

1.


2.


加入show_sql属性,并且赋值为true就可以了。


准备工作都做好了,下面我们开始学习对象关联映射:

一、一对一映射:

我们知道原先设计的时候,雇员和薪水表是一对一的关系,那么我们看看反向回来的Employees与Salary

首先是Employees的映射文件:Employees.hbm.xml




〈hibernate-mapping〉
〈class catalog="hibernate" schema="dbo" table="Employees" name="entity.Employees"〉
〈id name="employeeId" type="java.lang.Integer"〉
〈column name="employeeId"〉
〈generator class="native"〉
〈/id〉
〈many-to-one class="entity.Sector" name="sector" fetch="select"〉
〈column name="sectorId"/〉
〈/MANY-TO-ONE〉
〈property name="employeeName" type="java.lang.String"〉
〈column name="employeeName" length="30"〉
〈/property〉
〈property name="regTime" type="java.util.Date"〉
〈column name="regTime" length="23"〉
〈/property〉
〈set name="salaries" inverse="true"〉
〈key〉
〈column name="employeeId" not-null="true"〉
〈/key〉
〈one-to-many class="entity.Salary"〉
〈/set〉
〈/class〉
〈/HIBERNATE-MAPPING〉

恩,这里出现了2个不寻常的标签,一个是many-to-one,一个是set。many-to-one从字面上我们就可以理解为是多对一,那么雇员表和哪个表是多对一个关系呢?是的,与部门表是多对一个的关系。现在我们要研究的是一对一的关系,那么先不理它。看下面的set的标签,这又是为什么呢?因为set是一对多的一的那一端,我们想要一对一,所以这个标签也不符合。(因为一对一在表里表示出来的物理结构和一对多很像,所以工具就直接生成为一对多了,我们需要在这里改成一对一)那么,我们把set去掉,换成one-to-one标签。(改完后的样子如下:
〈one-to-one name="salaries" property-ref="employees"〉
)把set去掉了,我们不仅仅要改映射文件,我们还需要改实体类文件。原实体类文件如下:

package entity;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
* Employees entity.
*
* @author MyEclipse Persistence Tools
*/

public class Employees implements java.io.Serializable {

// Fields

private Integer employeeId;
private Sector sector;
private String employeeName;
private Date regTime;
private Set salaries = new HashSet(0);

// Constructors

/** default constructor */
public Employees() {
}

/** minimal constructor */
public Employees(Integer employeeId, Sector sector) {
this.employeeId = employeeId;
this.sector = sector;
}

/** full constructor */
public Employees(Integer employeeId, Sector sector, String employeeName,
Date regTime, Set salaries) {
this.employeeId = employeeId;
this.sector = sector;
this.employeeName = employeeName;
this.regTime = regTime;
this.salaries = salaries;
}

// Property accessors

public Integer getEmployeeId() {
return this.employeeId;
}

public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}

public Sector getSector() {
return this.sector;
}

public void setSector(Sector sector) {
this.sector = sector;
}

public String getEmployeeName() {
return this.employeeName;
}

public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}

public Date getRegTime() {
return this.regTime;
}

public void setRegTime(Date regTime) {
this.regTime = regTime;
}

public Set getSalaries() {
return this.salaries;
}

public void setSalaries(Set salaries) {
this.salaries = salaries;
}

}

因为去掉了set标签,又因为我们与薪水表是一对一的关系,薪水表的实体类是Salary,所以改之后的Employees实体类如下:

package entity;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
* Employees entity.
*
* @author MyEclipse Persistence Tools
*/

public class Employees implements java.io.Serializable {

// Fields

private Integer employeeId;
private Sector sector;
private String employeeName;
private Date regTime;
private Salary salaries;

// Constructors

/** default constructor */
public Employees() {
}

/** minimal constructor */
public Employees(Integer employeeId, Sector sector) {
this.employeeId = employeeId;
this.sector = sector;
}

/** full constructor */
public Employees(Integer employeeId, Sector sector, String employeeName,
Date regTime, Salary salaries) {
this.employeeId = employeeId;
this.sector = sector;
this.employeeName = employeeName;
this.regTime = regTime;
this.salaries = salaries;
}

// Property accessors

public Integer getEmployeeId() {
return this.employeeId;
}

public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}

public Sector getSector() {
return this.sector;
}

public void setSector(Sector sector) {
this.sector = sector;
}

public String getEmployeeName() {
return this.employeeName;
}

public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}

public Date getRegTime() {
return this.regTime;
}

public void setRegTime(Date regTime) {
this.regTime = regTime;
}

public Salary getSalaries() {
return this.salaries;
}

public void setSalaries(Salary salaries) {
this.salaries = salaries;
}

}

那么映射文件one-to-one标签里的属性都是什么意思呢?name是Employees类属性salaries的名字,说明对应的另一端类型是Salary,而property-ref属性的意思是另一端类属性(也就是Employees类型的属性,因为是一对一,那么薪水类Salary必然有一个人员类Employees的一个引用,这里是双向关联,那么property-ref填写的就是这个Salary类Employees的一个引用属性的名字)。

现在Employees类以及映射文件已经改好了,Salary不用改。
Salary的映射文件如下:

〈xml version="1.0" encoding="utf-8"?〉
〈!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"〉
〈!--
Mapping file autogenerated by MyEclipse Persistence Tools
--〉
〈hibernate-mapping〉
〈class name="entity.Salary" table="Salary" schema="dbo" catalog="hibernate"〉
〈id name="salaryId" type="java.lang.Integer"〉
〈column name="salaryId" /〉
〈generator class="native" /〉
〈/id〉
〈many-to-one name="employees" class="entity.Employees" fetch="select"〉
〈column name="employeeId" not-null="true" /〉
〈/many-to-one〉
〈property name="money" type="java.lang.Double"〉
〈column name="money" scale="4" /〉
〈/property〉
〈/class〉
〈/hibernate-mapping〉

Salary的实体类如下:

package entity;

/**
* Salary entity.
*
* @author MyEclipse Persistence Tools
*/

public class Salary implements java.io.Serializable {

// Fields

private Integer salaryId;
private Employees employees;
private Double money;

// Constructors

/** default constructor */
public Salary() {
}

/** minimal constructor */
public Salary(Integer salaryId, Employees employees) {
this.salaryId = salaryId;
this.employees = employees;
}

/** full constructor */
public Salary(Integer salaryId, Employees employees, Double money) {
this.salaryId = salaryId;
this.employees = employees;
this.money = money;
}

// Property accessors

public Integer getSalaryId() {
return this.salaryId;
}

public void setSalaryId(Integer salaryId) {
this.salaryId = salaryId;
}

public Employees getEmployees() {
return this.employees;
}

public void setEmployees(Employees employees) {
this.employees = employees;
}

public Double getMoney() {
return this.money;
}

public void setMoney(Double money) {
this.money = money;
}

}

在这里解释一下〈many-to-one name="employees" class="entity.Employees" fetch="select"〉
〈column name="employeeId" not-null="true" /〉
〈/many-to-one〉

不是一对一吗,怎么这里出现了多对一的标签呢?这是因为表结构里反映出来的,Employees和Salary的表逻辑上是一对一,但物理上Salary有一个外健直接指向Employees的主健,这跟逻辑上的一对多的物理表现形式是一样的,所以这里外表Salary是〈many-to-one〉而主表Employees是〈one-to-many〉(主健表维持一对一的关系)〈many-to-one〉的name属性是Salary类中引用Employees的属性名字,那么它对应的是表中的外建,所以下面的column就不用解释了。

我们下面就进行一对一的测试,我们写一个测试类:

import org.hibernate.Session;
import org.hibernate.Transaction;

import entity.Employees;
import entity.Salary;
public class test {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Session s=HibernateSessionFactory.getSession();
Transaction tx=s.beginTransaction();
try
{
Employees em=new Employees();
em.setEmployeeName("yangchao");
em.setRegTime(new java.util.Date());

Salary money=new Salary();
money.setMoney(10000.00);
money.setEmployees(em);

em.setSalaries(money);

s.save(em);
s.save(money);

tx.commit();


}
catch(Exception e)
{
tx.rollback();
e.printStackTrace();
}
finally
{
s.close();
}
}

}

运行后生成的hibernate语句为:

Hibernate: insert into hibernate.dbo.Employees (sectorId, employeeName, regTime) values (?, ?, ?)

Hibernate: insert into hibernate.dbo.Salary (employeeId, money) values (?, ?)

看看数据库里的结果:

Employees表:


为什么这里sectorId为null呢?这里说明一下,sectorId是部门表的主健,是本表的外健(本表与部门表是多对一的关系),由于我们只针对雇员表和薪水表进行一对一的数据插入,没有操作部门表,所以这里为null。


Salary表:

好了,一对一讲到这里相信大家都了解了吧!写了这么多,我BS一下google的博客,贴代码实在是太费劲了,它解析〈〉标签的,一旦有配置文件N多〈〉的,还得手改。。。贴图也不人性化。BS啊,BS!!

接下来,我们开始讲解一对多是如何实现的,这里用雇员表和部门表举实例,那么雇员表的映射文件如上,这里把针对一对多的片段配置公布如下(Sector与Employees是一对多关系):

Employees映射片段:

〈many-to-one name="sector" class="entity.Sector" fetch="select"〉

〈column name="sectorId" /〉

〈/many-to-one〉

Employees实体类片段:

private Sector sector;

这里解释一下,Sector部门与Employees雇员是一对多的关系,那么雇员类里必须有一个部门的引用,代表了这个雇员所属的部门。那么上面的映射文件代表了什么意思呢?因为是一对多关系,那么在Employees表里一定有一个外健引用Sector的主健,并且一对多的关系是由代表一的那一端,也就是Sector来维持的。上面配置里的name属性的值sector正好是Employees类引用Sector类的属性的名字。column标签里的name属性直接是Employees表中的外健名字。

清楚了Employees的实体类和映射文件后,我们来看一下一的这端Sector的映射文件和实体类:

Sector的映射文件:


〈?xml version="1.0" encoding="utf-8"?〉
〈!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"〉
〈!--
Mapping file autogenerated by MyEclipse Persistence Tools
--〉
〈hibernate-mapping〉
〈class name="entity.Sector" table="Sector" schema="dbo" catalog="hibernate"〉
〈id name="sectorId" type="java.lang.Integer"〉
〈column name="sectorId" /〉
〈generator class="native" /〉
〈/id〉
〈property name="sectorName" type="java.lang.String"〉
〈column name="sectorName" length="30" /〉
〈/property〉
〈set name="employeeses" inverse="true"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈one-to-many class="entity.Employees" /〉
〈/set〉
〈set name="projects" inverse="true" table="Sector_Project" schema="dbo" catalog="hibernate"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈many-to-many entity-name="entity.Project"〉
〈column name="projectId" /〉
〈/many-to-many〉
〈/set〉
〈/class〉
〈/hibernate-mapping〉

Sector的实体类:

package entity;

import java.util.HashSet;
import java.util.Set;

/**
* Sector entity.
*
* @author MyEclipse Persistence Tools
*/

public class Sector implements java.io.Serializable {

// Fields

private Integer sectorId;
private String sectorName;
private Set employeeses = new HashSet(0);
private Set projects = new HashSet(0);

// Constructors

/** default constructor */
public Sector() {
}

/** minimal constructor */
public Sector(Integer sectorId) {
this.sectorId = sectorId;
}

/** full constructor */
public Sector(Integer sectorId, String sectorName, Set employeeses,
Set projects) {
this.sectorId = sectorId;
this.sectorName = sectorName;
this.employeeses = employeeses;
this.projects = projects;
}

// Property accessors

public Integer getSectorId() {
return this.sectorId;
}

public void setSectorId(Integer sectorId) {
this.sectorId = sectorId;
}

public String getSectorName() {
return this.sectorName;
}

public void setSectorName(String sectorName) {
this.sectorName = sectorName;
}

public Set getEmployeeses() {
return this.employeeses;
}

public void setEmployeeses(Set employeeses) {
this.employeeses = employeeses;
}

public Set getProjects() {
return this.projects;
}

public void setProjects(Set projects) {
this.projects = projects;
}

}
我们来看这一段映射文件配置:

〈set name="employeeses" inverse="true"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈one-to-many class="entity.Employees" /〉
〈/set〉

set是什么呢?我们以前学过集合吧,恩,是的,这里代表集合。他对应的是Employees的集合,因为Sector与Employees是一对多的关系嘛,那么在一的这端Sector肯定有个集合来保存多个Employees。〈key〉标签表示了Employees表引用的是Sector表里sectorId主健。下面的〈one-to-many〉就很好理解了,说明了对应多的那一端类是什么。(后面还有个set,那是多对多的配置,现在先不管)

我们再写个测试类来针对Sector与Employees进行一对多的数据插入。

测试类片段如下:

Session s=HibernateSessionFactory.getSession();
Transaction tx=s.beginTransaction();
try
{
Sector se=new Sector(); //新建部门
se.setSectorName("北大青鸟学术部");

Employees em=new Employees();
em.setEmployeeName("coco");
em.setRegTime(new java.util.Date());

em.setSector(se); //设置引用的部门

Employees em1=new Employees();
em1.setEmployeeName("yoyo");
em1.setRegTime(new java.util.Date());

em1.setSector(se);//设置引用的部门

se.getEmployeeses().add(em);
se.getEmployeeses().add(em1);
s.save(se);
s.save(em);
s.save(em1);
tx.commit();
}
catch(Exception e)
{
tx.rollback();
e.printStackTrace();
}
finally
{
s.close();
}

我们再来看一下Employees表中的内容:




再看一下Sector表中的内容:


hibernate生成的sql语句:

Hibernate: insert into hibernate.dbo.Sector (sectorName) values (?)
Hibernate: insert into hibernate.dbo.Employees (sectorId, employeeName, regTime) values (?, ?, ?)
Hibernate: insert into hibernate.dbo.Employees (sectorId, employeeName, regTime) values (?, ?, ?)


最后一个内容,虽然理解上复杂点,但实际配置起来也挺容易的。我们再加把劲,把这最后的多对多映射给搞定。

我们通过概念模型了解到了Sector部门表与Project项目表是多对多的关系,这是逻辑上的,实际上我们在数据库里表示多对多关系的时候,都是以两个一对多的物理结构来体现的。还记得刚才讲一对多时Sector映射文件里被忽略掉的最后一个〈set〉标签吗?现在该它登场了!!

Sector的映射文件片段:

〈set name="projects" inverse="true" table="Sector_Project" schema="dbo" catalog="hibernate"〉
〈key〉
〈column name="sectorId" /〉
〈/key〉
〈many-to-many entity-name="entity.Project"〉
〈column name="projectId" /〉
〈/many-to-many〉
〈/set〉

这个就是多对多的一端的配置。我们刚才已经知道了多对多关系在数据库里表现为两个一对多。那么Sector_Project表就是Sector和Project的中间表,我们来看一下Sector_Project表的结构:


这里体现出来什么?两个一对多,也就是Sector表与Sector_Project表是一对多,Project表与Sector_Project表是一对多,Sector_Project的2个字段各是Sector表,Project表主健的外健。
这样同学们就应该理解了吧。在理解的基础之上看上面的配置,是不是一目了然了呢?大家可以再参考一下我们教材<>hibernate关系映射章节的图例以及解释.我就不在这里阐述过多的概念了,我们还是以实例做导向.

那么下面看一下Sector类的片段代码:

private Set projects = new HashSet(0);

这里大家就清楚了吧,这个集合是保存对应的多个Project,那么聪明的你们也一定会想到,既然是多对多,那么Project类里也有个集合是保存多个Sector的,是的,你们真是太聪明了,的确如此啊!!

趁热打铁,我们来先看一下Project实体代码:

package entity;

import java.util.HashSet;
import java.util.Set;

/**
* Project entity.
*
* @author MyEclipse Persistence Tools
*/

public class Project implements java.io.Serializable {

// Fields

private Integer projectId;
private String projectName;
private Set sectors = new HashSet(0);

// Constructors

/** default constructor */
public Project() {
}

/** minimal constructor */
public Project(Integer projectId) {
this.projectId = projectId;
}

/** full constructor */
public Project(Integer projectId, String projectName, Set sectors) {
this.projectId = projectId;
this.projectName = projectName;
this.sectors = sectors;
}

// Property accessors

public Integer getProjectId() {
return this.projectId;
}

public void setProjectId(Integer projectId) {
this.projectId = projectId;
}

public String getProjectName() {
return this.projectName;
}

public void setProjectName(String projectName) {
this.projectName = projectName;
}

public Set getSectors() {
return this.sectors;
}

public void setSectors(Set sectors) {
this.sectors = sectors;
}

}

果然有吧,呵呵.

那么接下来看看Project的映射文件:

〈?xml version="1.0" encoding="utf-8"?〉
〈!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"〉
〈!--
Mapping file autogenerated by MyEclipse Persistence Tools
--〉
〈hibernate-mapping〉
〈class name="entity.Project" table="project" schema="dbo" catalog="hibernate"〉
〈id name="projectId" type="java.lang.Integer"〉
〈column name="projectId" /〉
〈generator class="native" /〉
〈/id〉
〈property name="projectName" type="java.lang.String"〉
〈column name="projectName" length="30" /〉
〈/property〉
〈set name="sectors" table="Sector_Project" schema="dbo" catalog="hibernate"〉
〈key〉
〈column name="projectId" /〉
〈/key〉
〈many-to-many entity-name="entity.Sector"〉
〈column name="sectorId" /〉
〈/many-to-many〉
〈/set〉
〈/class〉
〈/hibernate-mapping〉

聪明的你们一定可以对照Sector类映射文件中的多对多配置来理解Project类映射文件中的多对多配置,好的,我满足你们的愿望,把机会留给你们。我们还是来写个测试类来插入多对多数据:

Session s=HibernateSessionFactory.getSession();
Transaction tx=s.beginTransaction();
try
{
Sector se=new Sector(); //新建部门
se.setSectorName("北大青鸟教学质量部");

Sector se1=new Sector();
se1.setSectorName("北大青鸟研发部");

Project pro=new Project();
pro.setProjectName("S1课程");

Project pro2=new Project(); //新建项目
pro2.setProjectName("S2课程");

Project pro3=new Project();
pro3.setProjectName("Y2课程");

/**
* 假想:北大青鸟教学质量部负责s1,s2,y2课程,北
* 大青鸟研发部负责s2,y2课程
*/
se.getProjects().add(pro);
se.getProjects().add(pro2);
se.getProjects().add(pro3);


se1.getProjects().add(pro2);
se1.getProjects().add(pro3);

/**
* 因为互相引用的关系,所以单方面设置的话,中间表中无数据
*/

pro.getSectors().add(se);
pro2.getSectors().add(se);
pro2.getSectors().add(se1);
pro3.getSectors().add(se);
pro3.getSectors().add(se1);



s.save(pro);
s.save(pro2);
s.save(pro3);
s.save(se);
s.save(se1);

tx.commit();
}
catch(Exception e)
{
tx.rollback();
e.printStackTrace();
}
finally
{
s.close();
}
}


Sector表的结果:



Project表的结果:



中间表Sector_Project的结果:



hibernate生成的sql语句:
Hibernate: insert into hibernate.dbo.project (projectName) values (?)
Hibernate: insert into hibernate.dbo.project (projectName) values (?)
Hibernate: insert into hibernate.dbo.project (projectName) values (?)
Hibernate: insert into hibernate.dbo.Sector (sectorName) values (?)
Hibernate: insert into hibernate.dbo.Sector (sectorName) values (?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)
Hibernate: insert into hibernate.dbo.Sector_Project (projectId, sectorId) values (?, ?)


到此,一对一,一对多,多对多的双向关联映射都全部讲完了。同学们,我希望你们回家按照此文内容多多练习,掌握它其实真的非常简单。如果你们遇到任何问题,到一办找我问就可以。因为此博客超烂,所以不排除有某些代码贴遗漏了,你们可以找我要脚本和示例代码。最后,还是希望大家有效的利用时间来学习,自律才能成功!

5 条评论:

  1. 杨老师 辛苦了 写了这么多 一定很累 大家要认真的看看啊

    回复删除
  2. 杨老师 辛苦了 写了这么多 一定很累 大家要认真的看看啊

    回复删除
  3. 呵呵,终于发力了,一下写了这么长的一篇,看来得下些功夫仔细研究一下了

    回复删除