Hibernate 5 的基本用法2

深入Hibernate映射

@GeneratedValue

如果希望 Hibernate 为逻辑主键自动生成主键值,则应该使用 @GeneratedValue 来修饰实体的标识属性:

@JoinColumn

@JoinColumn 注解专门用于定义外键列:

@Column

在默认情况下,被 @Entity 修饰的持久化类的所有属性都会被映射到底层数据表。为了指定某个属
性所映射的数据列的详细信息,如列名、列宇段长度等,可以在实体类中使用 @Column 修饰该属性:

@Entity

 @Entity : 被该注解修饰的POJO 就是一个实体。使用该注解时可指定一个name 属性, name 属性指定该实体类的名称,但大部分时候无须指定该属性,因为系统默认以该类的类名作为实体
类的名称。

@Table

该注解指定持久化类所映射的表:

 

持续补充。。。。

Hibernate的关联映射

Hibernate 的关联映射将可以大大简化持久层数据的访问,关联关系大致有如下两个分类:

单向关系:只需单向访问关联端。例如,只能通过老师访问学生,或者只能通过学生访问老师。
双向关系:关联的两端可以互相访问。例如,老师和学生之间可以互相访问。

单向关联可分为:
单向 1 → 1
单向 1 → N
单向 N → 1
单向 N → N
双向关联又可分为:
双向 1 - 1
双向 1 - N
双向 N - N
双向关系里没有 N - 1 , 因为双向关系 1 - N 和 N - 1 是完全相同的。下面讲解几种常用关联的映射方法。 

单向 N → 1 关联

单向 N → 1 关系,比如多个学生对应同一门课程,只需从学生这一端可以找到对应的地址实体,无须关心某个课程的学生。 即单向的 N → 1 关联只需从N 的一端可以访问1的一端。

为了让两个持久化类支持这种关联映射,程序应该在N 的一端的持久化类中增加一个属性,该属
性引用 1 的一端的关联实体。 

如下为 N → 1 (@ManyToOne)支持的属性

 注意,对于大部分的关联关系, Hibernate 都可以通过 反射 来确定关联实体的类型,因此可
以无须指定 targetEntity 属性,但因此会给系统增加负担,所以个人建议应习惯性指定 targetEntity 属性;一些特殊情况下,例如使用@ OneToMany 、@ManyToMany 修饰的1 - N、N - N 关联,如果用于表示关联实体的Set 集合不带泛型信息,那就必须指定targetEntity 属性。

可使用 @JoinColumn 来修饰代表关联实体的属性, @JoinColumn 用于映射底层的外键列。 

 Student.java

package domain;

import javax.persistence.*;

@Entity
@Table(name = "student_table")
public class Student {

    @Id
    @Column(name = "student_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String studentNo;

    private String name;

    @ManyToOne(targetEntity = Course.class, cascade = CascadeType.ALL)
    @JoinColumn(name = "course_id")
    private Course course;

    public void setId(Integer id) {
        this.id = id;
    }

    public void setStudentNo(String studentNo) {
        this.studentNo = studentNo;
    }

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

    public void setCourse(Course course) {
        this.course = course;
    }

    public Integer getId() {
        return id;
    }

    public String getStudentNo() {
        return studentNo;
    }

    public String getName() {
        return name;
    }

    public Course getCourse() {
        return course;
    }
    
    public Student(){}
}

上面是 Student 类的代码,每个 Student 单向地持有一个 Course ,因此 Student 类里增加了一个Course 类型的属性,并使用 @ManyToOne 修饰代表关联实体的 Course 属性,而且使用@JoinColumn 映射了底层外键列。
除此之外,上面程序还为 @ManyToOne 注解指定了 cascade 属性,该属性用于指定级联操作策略,此处指定的级联策略是 CascadeType. ALL:这表明对 Student 实体的所有持久化操作都会级联到它关联的 Course 实体。

程序无须从 Course 访问 Student , 所以 Course 无须增加 Student 属性。如下面代码所示:

Course.java

package domain;

import javax.persistence.*;

@Entity
@Table(name = "course_table")
public class Course {

    @Id
    @Column(name = "course_no")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer courseNo;

    private String courseName;

    public Integer getCourseNo() {
        return courseNo;
    }

    public void setCourseNo(Integer courseNo) {
        this.courseNo = courseNo;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public String getCourseName() {
        return courseName;
    }

    public Course(){ }

    public Course(String courseName){
        this.courseName = courseName;
    }
}

 执行下面的方法:

public void OneWayManyToOne(){
    Session session = HibernateUtil.currentSession();
    Transaction transaction = session.beginTransaction();

    Student student = new Student();
    Course course = new Course("Java开发框架");
    student.setName("lyq");
    student.setStudentNo("1901030086");
    //设置student和course的关联关系
    student.setCourse(course);
    //持续化student对象
    session.persist(student);
    Course new_course = new Course("项目实战");
    //修改持久化状态的student对象
    student.setCourse(new_course);

    transaction.commit();
    HibernateUtil.closeSession();
}

结果如下:多个学生和一个课程的对应关系

上面的方法创建了三个持久化实体, 即一个 Student 对象、两个 Course 对象,该方法只保存了一次 Student 对象,从来不曾保存过 Course 对象。

程序在 Course course = new Course("Java开发框架"); 处创建了一个瞬态的 Course 对象,当程序执行到 session.persist(student); 处时,系统准备保存 Student 对象,系统将要向student_table 数据表中插入一条记录,由于在 Student 类里的@ManyToOne 指定了cascade=CascadeType.ALL 属性,这意味着系统将先自动级联插入主表记录,也就是先持久化 Course 对象,再持久化 Student 对象。也就是说, Hibernate 在 session.persist(student); 处先执行一条insert into Course ..语句,再执行一条insert into Student ..语句。如果 Student 类中缺少了cascade=CascadeType . ALL 属性,则程序运行到 session.persist(student); 时将抛出TransientPropertyValueException 异常。

程序在 Course new_course = new Course("项目实战"); 处再次创建了一个瞬态的 Course 对象,但当程序执行到 transaction.commit(); 处时,程序将瞬态的 Course 对象关联到持久化状态下的Student 对象。类似地, 系统也会自动持久化 Course 对象,再建立 Course 对象和 Student 对象之间的关联。也就是说, Hibernate 在 transaction.commit(); 处先执行一条insert into
Course ...语句插入记录,再执行update Student ...语句修改该 student_table表的记录的外键值。

双向 1 - N 关联

对于1-N 关联, Hibernate 推荐使用双向关联,而且不要让1的一端控制关联关系,而使用N 的一
端控制关联关系。

N 的一端需要增加 @ManyToOne 注解来修饰代表关联实体的属性,在使用@ManyToOne 注解的同时,使用@JoinColumn 来映射外键列。而 1 的一端则需要使用@OneToMany 注解来修饰代表关联实体的属性,在使用@OneToMany 注解时指定mappedBy 属性,则表明当前实体不能控制关联关系,放弃控制关联关系之后, Hibernate 就不允许使用@JoinColumn 或@JoinTable 修饰代表关联实体的属性了。

注意:对于指定了mappedBy 属性的@OneToMany 、@ManyToMany 、@OneToOne 注解,都
不能与@JoinColumn 或@JoinTable 同时修饰代表关联实体的属性。

使用@OneToMany 注解可以指定如的属性如下图所示:

 Student.java

package domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "student_table")
public class Student {

    @Id
    @Column(name = "student_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String studentNo;

    private String name;

    @OneToMany(targetEntity = Course.class, mappedBy = "student")
    private Set<Course> courses = new HashSet<>();

    public void setCourses(Set<Course> courses) {
        this.courses = courses;
    }

    public Set<Course> getCourses() {
        return courses;
    }

    public void setId(Integer id) { this.id = id; }

    public void setStudentNo(String studentNo) {
        this.studentNo = studentNo;
    }

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

    public Integer getId() {
        return id;
    }

    public String getStudentNo() {
        return studentNo;
    }

    public String getName() {
        return name;
    }

    public Student(){}
}

Course.java

package domain;

import javax.persistence.*;

@Entity
@Table(name = "course_table")
public class Course {

    @Id
    @Column(name = "course_no")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer courseNo;

    private String courseName;

    @ManyToOne(targetEntity = Student.class)
    @JoinColumn(name = "student_id", referencedColumnName = "student_id", nullable = true)
    private Student student;

    public void setStudent(Student student) {
        this.student = student;
    }

    public Student getStudent() {
        return student;
    }

    public Integer getCourseNo() {
        return courseNo;
    }

    public void setCourseNo(Integer courseNo) {
        this.courseNo = courseNo;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public String getCourseName() {
        return courseName;
    }

    public Course(){ }

    public Course(String courseName){
        this.courseName = courseName;
    }
}

 运行如下方法:

public  void TwoWayOneToMany(){
    Session session = HibernateUtil.currentSession();
    Transaction transaction = session.beginTransaction();

    Student student = new Student();
    student.setName("lyq");
    student.setStudentNo("1901030086");
    session.save(student);

    Course course1 = new Course("java开发框架");
    course1.setStudent(student);
    session.persist(course1);

    Course course2 = new Course("项目实战");
    course2.setStudent(student);
    session.persist(course2);

    transaction.commit();
    HibernateUtil.closeSession();
}

程序需要注意如下几点:

1.最好先持久化 Student 对象(或该 Student 对象本身己处于持久化状态) 。因为程序希望持久化
Course 对象时, Hibernate 可为 Course 的外键属性分配值一一也就是说,向 course_table 数据表插入记录时, 该记录的外键列己指定了值这表明它参照的主表记录己存在,也就是Person
对象必须已被持久化。

2.先设置 Student 和 Course 的关联关系,再保存持久化 Course 对象。如果顺序反过来,程序持久化 Course 对象时, 该 Course 对象还没有关联实体,所以Hibernate 不可能为对应记录的外
键列指定值;等到设置关联关系时, Hibernate 只能再次使用 update 语句来修改关联关系。

3.不要通过 Student 对象来设置关联关系,因为己经在 Student 映射注解@OneToMany 中指定了
mappedBy 属性, 该属性表明 Student 对象不能控制关联关系。

运行结果如下:一个学生和对个课程的对应关系

 

双向 N - N 关联

双向 N - N 关联需要两端都使用Set 集合属性,两端都增加对集合属性的访问。双向 N - N 关联没
有太多选择,只能采用连接表来建立两个实体之间的关联关系。

双向 N - N 关联需要在两端分别使用@ManyToMany 修饰Set 集合属性,并在两端都使用            @JoinTable显式映射连接表。在两端映射连接表时,两端指定的连接表的表名应该相同,而且两端使用@JoinTable时指定的外键列的列名也是相互对应的。 

如果程序希望某一端放弃控制关联关系,则可在这一端的@ManyToMany 注解中指定mappedBy 属性,这一端就无须、也不能使用@JoinTable 映射连接表了。 

使用 @ManyToMany 时可指定的属性如下所示:

@JoinTable 专门用于映射底层 连接表 的信息,使用@JoinTable 时可指定的属性如下所示:

 Student.java

package domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "student_table")
public class Student {

    @Id
    @Column(name = "student_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String studentNo;

    private String name;

    @ManyToMany(targetEntity = Course.class)
    @JoinTable(name = "student_course",
            joinColumns = @JoinColumn(name = "student_id", referencedColumnName = "student_id"),
            inverseJoinColumns = @JoinColumn(name = "course_no", referencedColumnName = "course_no"))
    private Set<Course> courses = new HashSet<>();

    public void setCourses(Set<Course> courses) {
        this.courses = courses;
    }

    public Set<Course> getCourses() {
        return courses;
    }

    public void setId(Integer id) { this.id = id; }

    public void setStudentNo(String studentNo) {
        this.studentNo = studentNo;
    }

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

    public Integer getId() {
        return id;
    }

    public String getStudentNo() {
        return studentNo;
    }

    public String getName() {
        return name;
    }

    public Student(){}
}

代码映射了底层连接表的表名为 student_course ,并指定该连接表中包含两列: student_id 和course_no , 其中 student_id 参照 student_table 表的 student_id 主键列,course_no 参照
course_table 表的 course_no 主键列。由于此处管理的是 N - N 关联, 因此不能为任何@JoinColumn 注解增加 unique = true。

Course.java

package domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "course_table")
public class Course {

    @Id
    @Column(name = "course_no")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer courseNo;

    private String courseName;

    @ManyToMany(targetEntity = Student.class)
    @JoinTable(name = "student_course",
            joinColumns = @JoinColumn(name = "course_no", referencedColumnName = "course_no"),
            inverseJoinColumns = @JoinColumn(name = "student_id", referencedColumnName = "student_id"))
    private Set<Student> students= new HashSet<>();

    public void setStudents(Set<Student> students) {
        this.students = students;
    }

    public Set<Student> getStudents() {
        return students;
    }

    public Integer getCourseNo() {
        return courseNo;
    }

    public void setCourseNo(Integer courseNo) {
        this.courseNo = courseNo;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public String getCourseName() {
        return courseName;
    }

    public Course(){ }

    public Course(String courseName){
        this.courseName = courseName;
    }
}

执行如下方法:

public void TwoWayManyToMany() {
    Session session = HibernateUtil.currentSession();
    Transaction transaction = session.beginTransaction();

    Student student = new Student();
    student.setName("lyq");
    student.setStudentNo("1901030086");
    session.save(student);

    Course course1 = new Course("C语言");
    course1.getStudents().add(student);
    session.persist(course1);

    Course course2 = new Course("数据库");
    course2.getStudents().add(student);
    session.persist(course2);

    Student student2 = new Student();
    student2.setName("msr");
    student2.setStudentNo("00000001");
    student2.getCourses().add(course2);
    session.save(student2);

    transaction.commit();
    HibernateUtil.closeSession();

}

运行结果如下:

 

 

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值