Hibernate5--课程笔记5

时间:2023-03-10 06:34:34
Hibernate5--课程笔记5

关联关系映射:

  关联关系,是使用最多的一种关系,非常重要。在内存中反映为实体关系,映射到DB中为主外键关系。实体间的关联,即对外键的维护。关联关系的发生,即对外键数据的改变。

  外键:外面的主键,即,使用其它表的主键值作为自已的某字段的取值。

  (1) 基本概念:

    关联属性:Java代码的实体类定义中,声明的另一个实例类类型或其集合类型的属性,称为关联属性。

    关联关系维护:关联关系的维护,也称为外键维护,即为外键字段赋值。Hibernate默认情况下,关联的双方都具有维护权。即在代码中均可通过调用自己关联属性的set方法来建立关联关系。反映到数据库中,即是为外键字段赋值。

           不过,由于外键是建立在多方表中的,所以对于外键的维护方式,即为外键字段赋值的方式,一方维护与多方维护,其底层执行是不同的。
           若关联关系由一方维护,只能通过update语句来完成。若关联关系由多方维护,通过insert语句来完成。
           虽然双方均具有维护权,但一方同时具有放弃维护权的能力。通过对一方关联属性inverse=“true”设置,即可放弃关联关系维护权,将维护权完全交给多方。

    预处理语句:所谓预处理语句,即当前先不执行,等后面条件成熟,或程序运行完毕再执行的语句。

          当一方具有关联关系的维护权,并且执行save(一方对象)时,会产生一条update预处理语句,用于维护外键值。当多方具有关联关系的维护权,并且执行save(多方对象)时,会产生一条insert预处理语句,用于维护外键值。

    级联关系:当对某一类的对象a进行操作,如增加、删除、修改时,同时会自动对另一类的某对象b进行相同的操作。此时称,对象a、b具有级联关系,对象b为对象a的级联对象。

         级联操作是通过映射文件的cascade属性设置的。该属性的值较多,其介绍如下:

          none:在保存、更新或删除当前对象时,忽略其他关联的对象,即不使用级联。它是默认值。
          save-update:当通过Session的save()、update()、saveOrUpdate()方法来保存或更新当前对象时,将级联到其他DB中的相关联的表。
            delete:当通过Session的delete()方法删除当前对象时,将级联删除所有关联的对象。

          all:包含save-update及delete级联的所有行为。另外,当对当前对象执行lock()操作时,也会对所有关联的持久化对象执行lock()操作。
          delete-orphan:删除所有和当前对象解除关联关系的对象。

          all-delete-orphan:包含all和delete-orphan级联的所有行为。

    关联方向:(1)单向关联:指具有关联关系的实体对象间的加载与访问关系是单向的。即,只有一个实体对象可以加载和访问对方,但对方是看不到另一方的。
         (2)双向关联 :指具有关联关系的实体对象间的加载与访问关系是双向的。即,任何一方均可加载和访问另一方。

    关联数量:实体对象间的关系,从数量上可以划分为:1:1,1:n,n:1,m:n

    总结底层执行:一对多关系中,一方维护关联关系,先插入多方数据,后插入一方数据,最后update多方表中的外键值(???是否正确);多方维护关联关系,先插入一方数据,后插入多方数据,在插入多方数据的同时插入外键值;

           多对多关系中,哪一方维护关联关系,就是哪一方数据先插入,再插入关联方数据,最后插入中间表数据。

  (2) 1:n单向关联(由一方维护关联关系):

    一方实体类代码:

 package com.tongji.beans;

 import java.util.HashSet;
import java.util.Set; public class Country {
private Integer cid;
private String cname;
private Set<Minister> ministers; //关联属性 public Country() {
super();
ministers = new HashSet<Minister>();
} public Country(String cname) {
this();
this.cname = cname;
} public Integer getCid() {
return cid;
} public void setCid(Integer cid) {
this.cid = cid;
} public String getCname() {
return cname;
} public void setCname(String cname) {
this.cname = cname;
} public Set<Minister> getMinisters() {
return ministers;
} public void setMinisters(Set<Minister> ministers) {
this.ministers = ministers;
} @Override
public String toString() {
return "Country [cid=" + cid + ", cname=" + cname + ", ministers=" + ministers + "]";
} }

    一方映射文件代码:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tongji.beans">
<class name="Country">
<id name="cid">
<generator class="native"/>
</id>
<property name="cname"/>
<!-- 对关联属性的映射,inverse=true,表示一方放弃关联关系的维护权,这个不应该这么设置,只是为了演示错误 -->
<set name="ministers" cascade="save-update" inverse="true">
<key column="countryId"/>
<one-to-many class="Minister"/>
</set>
</class>
</hibernate-mapping>

     其中,column是外键的名称

     测试代码:

 @Test
public void test03() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
Minister minsiter1 = new Minister("aaa");
Minister minsiter2 = new Minister("bbb");
Minister minsiter3 = new Minister("ccc"); Country country = new Country("USA");
//关联的建立在这里完成
country.getMinisters().add(minsiter1);
country.getMinisters().add(minsiter2);
country.getMinisters().add(minsiter3); session.save(country);
//4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
}

  (2) 1:n双向关联:

    一方实体类代码和一方映射文件代码同上;

    多方实体类代码:

 package com.tongji.beans;

 public class Minister {
private Integer mid;
private String mname;
private Country country; //关联属性 public Minister() {
super();
} public Minister(String mname) {
super();
this.mname = mname;
} public Integer getMid() {
return mid;
} public void setMid(Integer mid) {
this.mid = mid;
} public String getMname() {
return mname;
} public void setMname(String mname) {
this.mname = mname;
} public Country getCountry() {
return country;
} public void setCountry(Country country) {
this.country = country;
} @Override
public String toString() {
return "Minister [mid=" + mid + ", mname=" + mname + "]";
} }

    多方映射文件代码:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tongji.beans">
<class name="Minister">
<id name="mid">
<generator class="native"/>
</id>
<property name="mname"/>
<!-- 关联属性名 -->
<many-to-one name="country" cascade="save-update"
class="Country" column="countryId"/>
</class>
</hibernate-mapping>

    测试代码:

 package com.tongji.test;

 import org.hibernate.Session;
import org.junit.Test; import com.tongji.beans.Country;
import com.tongji.beans.Minister;
import com.tongji.utils.HbnUtils; public class MyTest {
@Test
public void test00() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
Minister minister = new Minister("aaa"); Country country = new Country("USA");
//Country方在维护关联关系
country.getMinisters().add(minister); session.save(country);
//4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
} @Test
public void test01() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
Minister minister = new Minister("aaa"); Country country = new Country("USA");
//Minister方在维护关联关系
minister.setCountry(country); //谁在维护关联关系,就save()谁
session.save(minister);
//4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
}
}

  (3) 自关联:

    实体类代码:

 package com.tongji.beans;

 import java.util.HashSet;
import java.util.Set; //新闻栏目
public class NewsLabel {
private Integer id;
private String name; //栏目名称
private String content; //栏目内容
private NewsLabel parentNewsLabel; //父栏目
private Set<NewsLabel> childNewsLabels; //子栏目 public NewsLabel() {
childNewsLabels = new HashSet<NewsLabel>();
} public NewsLabel(String name, String content) {
this();
this.name = name;
this.content = content;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
} public NewsLabel getParentNewsLabel() {
return parentNewsLabel;
} public void setParentNewsLabel(NewsLabel parentNewsLabel) {
this.parentNewsLabel = parentNewsLabel;
} public Set<NewsLabel> getChildNewsLabels() {
return childNewsLabels;
} public void setChildNewsLabels(Set<NewsLabel> childNewsLabels) {
this.childNewsLabels = childNewsLabels;
} @Override
public String toString() {
return "NewsLabel [id=" + id + ", name=" + name + ", content=" + content + "]";
} }

    映射文件代码:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tongji.beans">
<class name="NewsLabel">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="content"/>
<!-- 站在一方角度 -->
<set name="childNewsLabels" cascade="save-update">
<key column="pid"/>
<one-to-many class="NewsLabel"/>
</set>
<!-- 站在多方角度 -->
<many-to-one name="parentNewsLabel" cascade="save-update"
class="NewsLabel" column="pid"/>
</class>
</hibernate-mapping>

    测试代码:

 package com.tongji.test;

 import org.hibernate.Session;
import org.junit.Test; import com.tongji.beans.NewsLabel;
import com.tongji.utils.HbnUtils; public class MyTest {
//让父方栏目维护关联关系,即一方维护
@Test
public void test00() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
NewsLabel footballNews = new NewsLabel("足球新闻","国足国足国足国足");
NewsLabel basketballNews = new NewsLabel("篮球新闻","登哥登哥登哥登哥"); NewsLabel sportsNews = new NewsLabel("体育新闻","奥运奥运奥运奥运"); sportsNews.getChildNewsLabels().add(footballNews);
sportsNews.getChildNewsLabels().add(basketballNews); session.save(sportsNews); //4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
} //让子方栏目维护关联关系,即多方维护
@Test
public void test01() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
NewsLabel basketballNews = new NewsLabel("篮球新闻","登哥登哥登哥登哥"); NewsLabel sportsNews = new NewsLabel("体育新闻","奥运奥运奥运奥运"); basketballNews.setParentNewsLabel(sportsNews); session.save(basketballNews); //4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
}
}

  (4) n:1单向关联(由多方来维护关联关系):

    一方实体类代码和一方映射文件代码为原来不考虑关联时候的样子,多方实体类代码和多方映射文件代码同(3)1:n双向关联,测试代码同(3)1:n双向关联的多方维护部分。

  (5)n:m单向关联:

    维护关联关系的一方的实体类代码:

 package com.tongji.beans;

 import java.util.HashSet;
import java.util.Set; public class Student {
private Integer sid;
private String sname;
private Set<Course> courses; public Student() {
courses = new HashSet<Course>();
} public Student(String sname) {
this();
this.sname = sname;
} public Integer getSid() {
return sid;
} public void setSid(Integer sid) {
this.sid = sid;
} public String getSname() {
return sname;
} public void setSname(String sname) {
this.sname = sname;
} public Set<Course> getCourses() {
return courses;
} public void setCourses(Set<Course> courses) {
this.courses = courses;
} @Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + "]";
} }

    维护关联关系的一方的映射文件代码:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tongji.beans">
<class name="Student">
<id name="sid">
<generator class="native"/>
</id>
<property name="sname"/>
<!-- 关联属性的映射 -->
<set name="courses" cascade="save-update" table="middle"> <!-- table指定多对多关系的中间表 -->
<key column="studentId"/> <!-- 指定当前类在中间表中对应的外键 -->
<many-to-many class="Course" column="courseId"/> <!-- 指定关联类在中间表中对应的外键 -->
</set>
</class>
</hibernate-mapping>

    测试代码:

 package com.tongji.test;

 import org.hibernate.Session;
import org.junit.Test; import com.tongji.beans.Course;
import com.tongji.beans.Student;
import com.tongji.utils.HbnUtils; public class MyTest {
//第一种解决方案
@Test
public void test00() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
Course course1 = new Course("Struts2");
Course course2 = new Course("Hibernate5");
Course course3 = new Course("Sring4"); Student student1 = new Student("张三");
Student student2 = new Student("李四"); student1.getCourses().add(course1);
student1.getCourses().add(course2); student2.getCourses().add(course1);
student2.getCourses().add(course3); session.save(student1);
session.save(student2);
//4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
} }

   (6)n:m双向关联:

      上述另一方实体类代码:

 package com.tongji.beans;

 import java.util.HashSet;
import java.util.Set; public class Course {
private Integer cid;
private String cname;
private Set<Student> students; public Course() {
students = new HashSet<Student>();
} public Course(String cname) {
this();
this.cname = cname;
} public Integer getCid() {
return cid;
} public void setCid(Integer cid) {
this.cid = cid;
} public String getCname() {
return cname;
} public void setCname(String cname) {
this.cname = cname;
} public Set<Student> getStudents() {
return students;
} public void setStudents(Set<Student> students) {
this.students = students;
} @Override
public String toString() {
return "Course [cid=" + cid + ", cname=" + cname + "]";
} }

    上述另一方映射文件代码:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tongji.beans">
<class name="Course">
<id name="cid">
<generator class="native"/>
</id>
<property name="cname"/>
<!-- 关联属性映射-->
<set name="students" cascade="save-update" table="middle">
<key column="courseId"/>
<many-to-many class="Student" column="studentId"/>
</set>
</class>
</hibernate-mapping>

    测试代码:

 package com.tongji.test;

 import org.hibernate.Session;
import org.junit.Test; import com.tongji.beans.Course;
import com.tongji.beans.Student;
import com.tongji.utils.HbnUtils; public class MyTest {
@Test
public void test00() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
Course course1 = new Course("Struts2");
Course course2 = new Course("Hibernate5");
Course course3 = new Course("Sring4"); Student student1 = new Student("张三");
Student student2 = new Student("李四"); student1.getCourses().add(course1);
student1.getCourses().add(course2); student2.getCourses().add(course1);
student2.getCourses().add(course3); session.save(student1);
session.save(student2);
//4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
} @Test
public void test01() {
//1. 获取Session
Session session = HbnUtils.getSession();
try {
//2. 开启事务
session.beginTransaction();
//3. 操作
Course course1 = new Course("Struts2");
Course course2 = new Course("Hibernate5");
Course course3 = new Course("Sring4"); Student student1 = new Student("张三");
Student student2 = new Student("李四"); course1.getStudents().add(student1);
course1.getStudents().add(student2); course2.getStudents().add(student2); course3.getStudents().add(student1); session.save(course1);
session.save(course2);
session.save(course3);
//4. 事务提交
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
//5. 事务回滚
session.getTransaction().rollback();
}
} }