hibernate表关系
1.一对一
用户表可以查分成两个表,一个userInfo、一个userLogin表
实现方式:
(1)使用外键:外键+唯一性约束+非空约束
(2)公用主键:公用主键,从表的主键同时也是外键,来源于主表的主键。
2. 一对多
2.1. 概念
使用外键,实现一对多关系,外键可以为null
主从关系:一:主表,多:从表 ,从表的外键必须是主表的主键或者null。
代码在:day29
2.2.实现方法:Student Clazz
step1:在一个一方创建对象引用集合 Set<>
private Set<Student> students = new HashSet<>(0);
step2:在多的一方创建对象引用
private Clazz clazz;
step3:在多方 实体中建立多对一关系-<many-to-one>【由多的一方维护关系】。
step4:在一方配置<set>标签【可选】 配置一对多
public class Student implements Serializable { private Integer id; private String stuName; private Integer age; private Clazz clazz; //set,get省略 }
public class Clazz implements Serializable { private Integer id; private String className; //实例化,可以防止空指针异常 private Set<Student> students = new HashSet<>(0); //长度设为0 }
<hibernate-mapping package="cn.getword.domain"> <class name="Student" table="tb_student"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="stuName" column="username" /> <property name="age" column="age" /> <!-- name:用于指定引用对象在类中属性名称 class:一方的类的路径 column:外键名称 --> <many-to-one name="clazz" class="Clazz" column="clazz_id" /> </class> </hibernate-mapping>
<hibernate-mapping package="cn.getword.domain"> <class name="Clazz" table="tb_clazz"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="className" column="class_name" /> <!-- name:引用集合的名称 table:多方表明 --> <set name="students" table="tb_student"> <!-- key:多方表的外键名称 --> <key column="clazz_id"></key> <one-to-many class="Student"></one-to-many> </set> </class> </hibernate-mapping>
2.3. 一对多的CRUD操作
(1)添加
/** * 查询一个班级 * 创建一个学生 * 建立两者关系: * 执行保存操作 * 这里由于Clazz没有级联操作,所以即使Clazz【持久态】发生了改变【添加了Student】,也不会进行更新操作 * 解决方法: * (一) 添加Student【维护多对一的关系,并且可以级联操作】 * (二) 让Clazz【主表】也参与维护关系(set一对多),并且给他添加级联操作【cascade=true】,但是student没有外键? * 此时需要建立双向关系【student1.setClazz(clazz);】,在更新Clazz的时候发现有关联的Student对象,因此执行 * 添加Student操作,由于我们让Student参加维护关系的工作,同时又给student设置了Clazz,因此在添加Student的时候 * 会自动添加外键约束【等价于方法一】 * 在实际开发中:一般有多的一方维护关系,并设置级联操作。一的一方将关系维护交给对方,并设置级联操作。配置如下 * <many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" /> * <set name="students" table="tb_student" cascade="save-update,delete" inverse="true"> * <key column="clazz_id"></key> * <one-to-many class="Student"></one-to-many> * </set> */ @Test public void save(){ Student student1 = new Student(); student1.setStuName("admin2"); student1.setAge(21); Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Clazz clazz = session.get(Clazz.class, 1); clazz.getStudents().add(student1); // 给Clazz添加一个学生,在级联操作时,会执行insert student student1.setClazz(clazz); session.update(clazz); transaction.commit(); } /** * 创建一个班级 * 创建一个学生 * 建立两者关系:建立双向关系,原理同上,建立双向关系能保证外键的生成 * 执行保存操作 * 先保存主表,在保存从表 * 此时发现一个问题:多出一个update语句 * 跟一级缓存有关、由于双方建立双向关系。 * 解决办法:让一方放弃维护关系的权利。在多方配置文件中【set】,添加属性inverse=true */ @Test public void save2(){ Student student1 = new Student(); student1.setStuName("张三"); student1.setAge(21); Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Clazz clazz = new Clazz(); clazz.setClassName("软件8班"); //建立关系 clazz.getStudents().add(student1); student1.setClazz(clazz);//如果Clazz参与了维护关系,则这行代码就不需要了,在添加student1后悔自动update它的外键【这里需要外键可以为空】 //保存 session.save(clazz); // session.save(student1); 根据级联操作,student会自动添加,并且添加student时,会自动添加外键【student维护了多对一 的关系】 transaction.commit(); } /** * 级联保存 * <many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" /> * save-update:对保存和更新操作进行级联操作, * 如果不设置级联操作,则会报错【持久状态对象不能包含瞬时状态变量】 * 级联操作配置在哪方,哪方就可以进行级联操作,这里配置在从属【Student】一方, * 因此只有Student可以进行级联添加,添加Clazz没有就不会有级联效果 * */ @Test public void save3(){ Student student1 = new Student(); student1.setStuName("张三"); student1.setAge(21); Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Clazz clazz = new Clazz(); clazz.setClassName("软件6班"); //建立关系 student1.setClazz(clazz); //保存 session.save(student1); transaction.commit(); }
(2)更新
/** * 级联更新:只要Clazz的属性发生了变化,就会自动更新 * 查询一个Clazz * 创建一个学生 * 建立二者关系 * 更新Clazz * * 和save1类似 */ @Test public void update(){ Student student1 = new Student(); student1.setStuName("admin3"); student1.setAge(21); Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Clazz clazz = session.get(Clazz.class, 1); clazz.getStudents().add(student1); // 给Clazz添加一个学生,在级联操作时,会执行insert student student1.setClazz(clazz); session.update(clazz); transaction.commit(); }
(3)删除
/** * 删除从表:直接删除 * 删除主表: * 有引用: * 级联级联【在主表中配置级联删除,从表不需要配置】 * 如果没有将关系维护权交给从表【set inverse=true】,那么在删除主表之前会先把从表的外键设为null,外键为非空,则抛出异常 * 因此在级联删除时推荐将关系维护交给从表【Student】来维护 * 没有设置级联删除:报错 * 没有引用:直接删除 */ @Test public void delete(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //建立关系 transaction.commit(); }
(4)查询
/** * 对象导航查询 * */ @Test public void select(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Clazz clazz = session.get(Clazz.class, 1); System.out.println(clazz); //建立关系 transaction.commit(); }
获取Clazz对象是,对于Student对象,默认采用延迟加载的方式加载。
获取Student对象,默认对于Clazz对象也是立即加载。【这种情况可以使用立即加载,因为数据量小】
更改默认配置:
<many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" lazy="false" /> //立即加载 <set name="students" table="tb_student" cascade="save-update,delete" inverse="true" lazy="false"> <!-- key:多方表的外键名称 --> <key column="clazz_id"></key> <one-to-many class="Student"></one-to-many> </set>
- many-to-one: Student中配置
false:立即加载Clazz,默认就是立即加载,可以不用修改
no-proxy:不管
proxy:加载时机取决于Clazz对象的load加载时机。
public void select2(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Student student = session.get(Student.class, 1); Clazz clazz = student.getClazz(); System.out.println(clazz); //建立关系 transaction.commit(); }
- set:查询Clazz,默认懒加载Students,也可以不用修改
public void select(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Clazz clazz = session.get(Clazz.class, 1); Set<Student> students = clazz.getStudents(); System.out.println(students); //建立关系 transaction.commit(); }
3. 多对多
使用中间表实现多对多关系。中间表只有两个字段,即两个表的主键,中间表的主键就是两个外键的联合主键。
3.1 实现
多对多可以看成各自相对于中间表一对多的关系。中间表引用他们的主键--构造中间表
多方都是配置set:
注意:必须有一方放弃维护关系,否则在插入中间表信息时会产生冲突
实体类:
public class Teacher implements Serializable { private Integer id; private String name; private Set<Student> students = new HashSet<Student>(0); set/get... }
public class Student implements Serializable { private Integer id; private String stuName; private Integer age; private Clazz clazz; private Set<Teacher> teachers = new HashSet<Teacher>(0); }
配置文件:
<hibernate-mapping package="cn.getword.domain"> <class name="Teacher" table="tb_teacher"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="name" column="name" /> <!-- name: table:中间表名【如果把teacher和中间表看做一对多,那么就是指的是多方表名】 --> <set name="students" table="tb_teacher_student"> <!--key column: 指定中间表的外键名称 --> <key column="tid"></key> <!--配置多对多的关系 class:指定对方实体类的类名 column:对方实体类主键对应的外键 --> <many-to-many class="Student" column="sid"></many-to-many> </set> </class> </hibernate-mapping>
<hibernate-mapping package="cn.getword.domain"> <class name="Student" table="tb_student"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="stuName" column="username" /> <property name="age" column="age" /> <!-- name:用于指定引用对象在类中属性名称 class:一方的类的路径 column:外键名称 --> <many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" /> <set name="teachers" table="tb_teacher_student" inverse="true"> <key column="sid"></key> <many-to-many class="Teacher" column="tid"></many-to-many> </set> </class> </hibernate-mapping>
3.2 CRUD操作
(1)添加
/** * 创建两个老师 * 创建三个学生 * 给一号老师分配1,2学生 * 给二号老师分配2,3学生 * 建立双向关系 * 保存 */ @Test public void save(){ Teacher t1 = new Teacher(); t1.setName("张三"); Teacher t2 = new Teacher(); t2.setName("李四"); Student s1 = new Student(); s1.setStuName("赵云"); s1.setAge(12); Student s2 = new Student(); s2.setStuName("周瑜"); s2.setAge(21); Student s3 = new Student(); s3.setStuName("诸葛"); s3.setAge(23); //建立关系 t1.getStudents().add(s1); t1.getStudents().add(s2); t2.getStudents().add(s2); t2.getStudents().add(s3); // s1.getTeachers().add(t1); // s2.getTeachers().add(t1); // s2.getTeachers().add(t2); // s3.getTeachers().add(t2); //保存 Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); session.save(t1); session.save(t2); transaction.commit(); }
(2)删除
先删除中间表相关记录,在删除实体类的数据
注意:千万慎重使用。一不小心删光所有数据
end