Hibernate映射关系全面解析

Hibernate映射关系全面解析,到今天为止,对Hibernate映射的郁闷已经无法再抑制了,也必须该和这个烦人的家伙做个了断了,下面将对Hibernate各种映射做一一细解。


[size=medium][b]Hibernate映射关系到底有多复杂?[/b][/size]

传说中的不就是分为3种嘛,一对多,一对一,多对多,如果你真正用过hibernate你会发现实际的开发过程中遇到的情况要比概念的3种情况更为复杂。

对映关系还会分为单项关联和双项关联,而单项和双项关系,外键在主表还是从表又分两种情况,级联保存2个表的时候,有一种情况会多一条update语句,而有一种情况则只需要2条insert语句,究竟原理和王者解决之道,今天将水落石出了。


[size=medium][b]下面开始正题,首先目标瞄准,[color=red]一对多关联关系[/color]。[/b][/size]

[b][color=darkblue]建表:[/color][/b]
学生表:create table student (id int primary key auto_increment,name varchar(30));

地址表:create table address (id int primary key auto_increment,city varchar(30),student_id int);

一个学生可以有多个地址,形成一对多的关系,而[color=red][b]关联关系的外键由地址表[/b][/color]维护!!


[b][size=small]首先介绍单向一对多关联关系,外键在从表中[/size][/b]

单向学生表PO

package cn.limaoyuan.hibernate.po.single;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="student")
public class Student {
private Integer id;
private String name;

private Set<Address> addressSet = new HashSet<Address>(0);


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}


/**
* 单向一对多,外键在地址表中维护
* */
@OneToMany(cascade=CascadeType.ALL) //CascadeType.All设成级联保存,以便演示更多效果。
//这里没有mappedBy映射而是用@JoinColumn来代替,因为我们用的是单向关联关系,在Address中不存在student属性,只有studentId属性,所以我们用@JoinColumn来映射2张表之间的关系关系。
@JoinColumn(name="student_id")
public Set<Address> getAddressSet() {
return addressSet;
}
public void setAddressSet(Set<Address> addressSet) {
this.addressSet = addressSet;
}
}


单向地址表PO

package cn.limaoyuan.hibernate.po.single;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="address")
public class Address implements Serializable{
private Integer id;
private String city;
private Integer studentId;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="city")
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}

@Column(name="student_id")
public Integer getStudentId() {
return studentId;
}
public void setStudentId(Integer studentId) {
this.studentId = studentId;
}
}

因为是单向一对多关联,所以地址表PO没有更多的注解,只是单纯的映射与数据库之间的基础对应。

学生Dao接口

package cn.limaoyuan.hibernate.dao;
import cn.limaoyuan.hibernate.po.single.Student;

public interface IStudentDao {
public void insert(Student student);
}


学生Dao实现

package cn.limaoyuan.hibernate.dao;

import org.hibernate.Session;
import cn.limaoyuan.hibernate.po.single.Student;
import cn.limaoyuan.hibernate.util.HibernateSessionFactory;

public class StudentDaoImpl
implements IStudentDao{

@Override
public void insert(Student student) {
Session session = HibernateSessionFactory.getSession();
session.beginTransaction();
session.save(student);
session.getTransaction().commit();
session.close();
}
}


测试保存一个学生类:

/**
* 测试插入单向一对多关联关系
* */
public static void insertStudent()
{
IStudentDao studentDao = new StudentDaoImpl();

Student student = new Student();
student.setName("zhang san");

Address address = new Address();
address.setCity("beijing");
address.setStudentId(student.getId());

student.getAddressSet().add(address);

//这里直接插入学生,会级联插入地址
studentDao.insert(student);
}


测试结果没有问题,学生表和地址表分别插入了一条数据,但是我们观察SQL输出了3条语句!

2010-02-23 14:39:54,906 DEBUG [main] SQL.log(401) | insert into student (name) values (?)
2010-02-23 14:39:54,968 DEBUG [main] SQL.log(401) | insert into address (city, student_id) values (?, ?)
2010-02-23 14:39:55,015 DEBUG [main] SQL.log(401) | update address set student_id=? where id=?


我们插入了2条数据,为什么后面多了一条update语句呢?

原因在于,我们这个单项的关联关系在student类表示,系统当保存address类的时候,他不知道studentId这个值是否已经保存过了,所以在2条insert之后会保险的在最后update一下studentId字段,如此说来,我们要让address类知道有student类存在的话,那么address类是不是就不会在最后有这一条update语句了呢?答案是肯定的,这就是我下面要说的双向关联关系。

由于这是技术笔记,双向关联关系的好处就不说太多了,明显的是级联保存时会节省一条UPDATE语句,第2在双方的类中都很容易找到上一级关联对象。

[b][size=small]介绍双向一对多关联关系,外键在从表中[/size][/b]

双向学生表PO

package cn.limaoyuan.hibernate.po.both;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="student")
public class StudentBoth {
private Integer id;
private String name;

private Set<AddressBoth> addressSet = new HashSet<AddressBoth>(0);


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}


/**
* 单向一对多,外键在地址表中
* */
//由于用了双项关联,那么在Address中就会有了student属性,那么我们就可以简单的使用mappedBy来指向address类中的student属性来关联这组关系。
@OneToMany(cascade=CascadeType.ALL,mappedBy="student")
public Set<AddressBoth> getAddressSet() {
return addressSet;
}
public void setAddressSet(Set<AddressBoth> addressSet) {
this.addressSet = addressSet;
}
}


双向地址表PO

package cn.limaoyuan.hibernate.po.both;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity
@Table(name="address")
public class AddressBoth implements Serializable{
private Integer id;
private String city;
private Integer studentId;

private StudentBoth student;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="city")
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}

@Transient
public Integer getStudentId() {
return studentId;
}
public void setStudentId(Integer studentId) {
this.studentId = studentId;
}

/*这里由于使用了双向关联关系,那么就直接把数据库中的student_id字段直接映射成student属性,而不用单独映射成一个studentId属性,然后用@JoinColumn()注解的name属性去指定以本类中的哪个属性去关联对方,默认的是去关联对方的主键值,我们也可以用referencedColumnName属性去指定关联主表中的某个值来确认关系。@JoinColumn(name="student_id")的含义就是用address中的student_id字段去关联student表中的id字段,这里我们在@JoinColumn中省略了referencedColumnName="id",加上了也会得到同样的效果。*/
@ManyToOne
@JoinColumn(name="student_id")
public StudentBoth getStudent() {
return student;
}
public void setStudent(StudentBoth student) {
this.student = student;
}
}


下面来测试一下双向关联的情况

//双向关联保存,外建在对方
public static void insertStudentBoth()
{
IStudentDao studentDao = new StudentDaoImpl();

StudentBoth student = new StudentBoth();
student.setName("zhang san");

AddressBoth address = new AddressBoth();
address.setCity("beijing");
//address.setStudentId(student.getId());
address.setStudent(student); //地址表关联学生

student.getAddressSet().add(address); //学生添加地址

studentDao.insert(student); //保存学生
}


执行后同样会在学生表和地址表各插入一表数据,然而我们只会得到2条insert语句。

2010-02-23 14:57:28,000 DEBUG [main] SQL.log(401) | insert into student (name) values (?)
2010-02-23 14:57:28,015 DEBUG [main] SQL.log(401) | insert into address (city, student_id) values (?, ?)

这显然是我们更想要的结果。

[b][size=small]介绍单向一对一关联关系,外键在从表中[/size][/b]

还是刚才的二张表,我们更改业务逻辑,让一个学生只能有一个地址对应。

哈哈,你上当了,OneToOne的方式,如果关联外建在从表中,那么将不能映射。
OneToOne的方式,要么关键外键在主表中,要么就设成双向关联关系。


[b][size=small]介绍双向一对一关联关系,外键在从表中[/size][/b]

双向学生表PO

package cn.limaoyuan.hibernate.po.single2;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="student")
public class StudentSingle2 {
private Integer id;
private String name;

private AddressSingle2 address;


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

/**
* 双向一对一,外键在对方,一对一可以用OneToOne来映射,由于外键在对方,那么OneToOne方式只能用mappedBy去引用。
**/
@OneToOne(mappedBy="student",cascade=CascadeType.ALL)
public AddressSingle2 getAddress() {
return address;
}
public void setAddress(AddressSingle2 address) {
this.address = address;
}
}


双向地址表PO

package cn.limaoyuan.hibernate.po.single2;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="address")
public class AddressSingle2 implements Serializable{
private Integer id;
private String city;
private StudentSingle2 student;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="city")
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}

@OneToOne
@JoinColumn(name="student_id") //自己的student_id字段去关联studnet表中的id属性,如果不是关联student主键,那么可以用referencedColumnName指定。
public StudentSingle2 getStudent() {
return student;
}
public void setStudent(StudentSingle2 student) {
this.student = student;
}
}


测试双向一对一外键在从表中的情况 :

/**
* 测试插入单向一对多关联关系 ,外建在本方
* */
public static void insertStudentSingle2()
{
IStudentDao studentDao = new StudentDaoImpl();

StudentSingle2 student = new StudentSingle2();
student.setName("zhang san");

AddressSingle2 address = new AddressSingle2();
address.setCity("beijing");
address.setStudent(student); //地址绑定学生

student.setAddress(address); //学生绑定地址

studentDao.insert(student); //插入学生
}


结果在学生和地址表中各插了一条,打印出2条SQL

2010-02-23 15:24:21,546 DEBUG [main] SQL.log(401) | insert into student (name) values (?)
2010-02-23 15:24:21,562 DEBUG [main] SQL.log(401) | insert into address (city, student_id) values (?, ?)


并没有打印出多余的UPDATE语句,可见只要是双向关联后无论是多对一还是一对多,都可以很好的运行级联插入操作,并不会去执行多余的SQL语句。

[b][size=small]介绍单向一对一关联关系,外键在主表中[/size][/b]

首先建表,还是学生表和地址表。
create table student2 (id int primary key auto_increment,name varchar(30),address_id int);

create table address2 (id int primary key auto_increment,city varchar(20));

和之前演示的2张表,唯一的不同就是关联关系交给了主表,也就是学生表,在学生表中多了address_id字段,做为外键去引用address中的id字段,形成关联关系。

单向一对一学生表

package cn.limaoyuan.hibernate.po.single3;
/**
* 一对一单项关联,外键在本方
* */

import javax.persistence.*;

@Entity
@Table(name="student2")
public class StudentSingle3 {
private Integer id;
private String name;

private AddressSingle3 address;


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}



/**
* 单向一对多,外键在本方,一对一可以用OneToOne来映射
* */
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="address_id")
public AddressSingle3 getAddress() {
return address;
}
public void setAddress(AddressSingle3 address) {
this.address = address;
}
}


单向一对一地址表PO:

package cn.limaoyuan.hibernate.po.single3;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="address2")
public class AddressSingle3 implements Serializable{
private Integer id;
private String city;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(name="city")
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}


测试类

/**
* 测试插入单向一对一关联关系 ,外建在本方
* */
public static void insertStudentSingle3()
{
IStudentDao studentDao = new StudentDaoImpl();

StudentSingle3 student = new StudentSingle3();
student.setName("zhang san");

AddressSingle3 address = new AddressSingle3();
address.setCity("beijing");

student.setAddress(address);

studentDao.insert(student);
}


结果很正确,在学生和地址表各插入了一条,并且只打印了2条SQL,这是我们第一次在单项关联的情况下插入2张表只用了2条数据,可见非常好。

2010-02-23 15:53:29,453 DEBUG [main] SQL.log(401) | insert into address2 (city) values (?)
2010-02-23 15:53:29,468 DEBUG [main] SQL.log(401) | insert into student2 (address_id, name) values (?, ?)


[b][size=small]介绍双向一对一关联关系,外键在主表中[/size][/b]
相信结果和双向一对一,外键在从表中的结果是一样的,就不再做测试了,附件是完整的例子。


[b][size=small]介绍利用中间表来关联单向一对一关联关系,外键在关联表中[/size][/b]

[color=red][b]建表:[/b][/color]
学生表:create table student3 (id int primary key auto_increment,name varchar(30));
地址表:create table address3 (id int primary key auto_increment,city varchar(30));
关联表:create table student3_address3 (student_id int,address_id int);


由于上传大小限制,完整的例子在163邮箱的网盘我的文档目录中,文件名字为hibernatetest.rar
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值