A对B,这是关联关系,另一个要讨论的是级联关系。
级联,就是对一个对象进行操作的时候,会把他相关联的对象也一并进行相应的操作
hibernate的级联我感觉和通常数据库的参照动作为cascade不太一样,hibernate不管是外键那方还是参照那方都可以设置cascade。不过我推荐参照那方用比较好(比较合乎实际意义)
而且数据库的on delete cascade 是指将依赖表中和参照表要删除主键值对应记录一起删了,on update cascade是指将依赖表中和参照表中要修改主键值对应的所有外键值一起修改。而hiberna的级联意思则是设置级联的那方插入/删除/修改时,关联的对象也一起插入/删除/修改
关联关系映射并不会帮你连被控方一起操作了,除非你设置cascade(cascade默认为none,代表不级联)。不然两方的数据库操作都需要写出来
级联可以在关联关系映射元素中设置,设置的一方成为主控方。 比如双向多对一,两方都可以设置cascade。
一、单向多对一关联
1.多方设置cascade
package com.zhj.VO;
public class ProductSMto {
public int productId;
public String productName;
public FactorySMto factory;
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public FactorySMto getFactory() {
return factory;
}
public void setFactory(FactorySMto factory) {
this.factory = factory;
}
}
package com.zhj.VO;
public class FactorySMto {
public int factoryId;
public String factoryName;
public int getFactoryId() {
return factoryId;
}
public void setFactoryId(int factoryId) {
this.factoryId = factoryId;
}
public String getFactoryName() {
return factoryName;
}
public void setFactoryName(String factoryName) {
this.factoryName = factoryName;
}
}
<?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>
<class name="com.zhj.VO.ProductSMto" table="productsmto">
<id name="productId" column="productid" type="int">
<generator class="native"/>
</id>
<property name="productName" column="productname" type="string"></property>
<many-to-one name="factory" class="com.zhj.VO.FactorySMto" cascade="all">
<column name="factoryId"/>
</many-to-one>
</class>
</hibernate-mapping>
<?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>
<class name="com.zhj.VO.FactorySMto" table="factorysmto">
<id name="factoryId" column="factoryId" type="int">
<generator class="native"/>
</id>
<property name="factoryName" column="factoryName" type="string"></property>
</class>
</hibernate-mapping>
package com.zhj.TestH;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.zhj.VO.FactorySMto;
import com.zhj.VO.ProductSMto;
import com.zhj.utils.hibernateUtils;
public class TestSMto {
public static void main(String[] args) {
Session session=null;
Transaction transaction=null;
try {
session=hibernateUtils.getSessionFactory().openSession();
transaction=session.beginTransaction();
/*
FactorySMto factory=new FactorySMto();
factory.setFactoryName("fa1");
ProductSMto product=new ProductSMto();
product.setProductName("p1");
product.setFactory(factory);
session.save(product); //结果是两个都插入了
transaction.commit();*/
/*transaction=session.beginTransaction();
ProductSMto product=session.get(ProductSMto.class,new Integer(1));
product.setProductName("p1top2");
product.getFactory().setFactoryName("f1tof2");
session.update(product); //两个都更新了
transaction.commit();*/
transaction=session.beginTransaction();
ProductSMto product=session.get(ProductSMto.class,new Integer(1));
session.delete(product); //两个都删了
transaction.commit();
session.close();
}catch(Exception ex) {
session.getTransaction().rollback();
}finally {
session.close();
}
}
}
sql执行结果:
Hibernate: alter table productsmto add constraint FK42ko0gqkkep3lljiuhateyrn1 foreign key (factoryId) references factorysmto (factoryId)
Hibernate: insert into factorysmto (factoryName) values (?)
Hibernate: insert into productsmto (productname, factoryId) values (?, ?)
如果不设置cascade,又不先插入factory,会报错:
Hibernate: alter table productsmto add constraint FK42ko0gqkkep3lljiuhateyrn1 foreign key (factoryId) references factorysmto (factoryId)
Hibernate: insert into productsmto (productname, factoryId) values (?, ?)
九月 09, 2019 9:08:54 下午 org.hibernate.internal.ExceptionMapperStandardImpl mapManagedFlushFailure
ERROR: HHH000346: Error during managed flush [org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.zhj.VO.FactorySMto]
如果hibernate设置自动生成数据库表,product映射表中会生成字段factoryid(看看product类里面是FactorySMto对象)
二、单向一对多关联
1.一方设置cascade
package com.zhj.VO;
public class ProductSMto {
public int productId;
public String productName;
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
}
package com.zhj.VO;
import java.util.Set;
public class FactorySMto {
public int factoryId;
public String factoryName;
public Set<ProductSMto> products;
public int getFactoryId() {
return factoryId;
}
public void setFactoryId(int factoryId) {
this.factoryId = factoryId;
}
public String getFactoryName() {
return factoryName;
}
public void setFactoryName(String factoryName) {
this.factoryName = factoryName;
}
public Set<ProductSMto> getProducts() {
return products;
}
public void setProducts(Set<ProductSMto> products) {
this.products = products;
}
}
<?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>
<class name="com.zhj.VO.ProductSMto" table="productsmto">
<id name="productId" column="productid" type="int">
<generator class="native"/>
</id>
<property name="productName" column="productname" type="string"></property>
</class>
</hibernate-mapping>
<?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>
<class name="com.zhj.VO.FactorySMto" table="factorysmto">
<id name="factoryId" column="factoryid" type="int">
<generator class="native"/>
</id>
<property name="factoryName" column="factoryname" type="string"></property>
<set name="products" cascade="all">
<key column="factoryid"/>
<one-to-many class="com.zhj.VO.ProductSMto"/>
</set>
</class>
</hibernate-mapping>
package com.zhj.TestH;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.zhj.VO.FactorySMto;
import com.zhj.VO.ProductSMto;
import com.zhj.utils.hibernateUtils;
public class TestSMto {
public static void main(String[] args) {
Session session=null;
Transaction transaction=null;
try {
session=hibernateUtils.getSessionFactory().openSession();
transaction=session.beginTransaction();
ProductSMto product=new ProductSMto();
product.setProductName("p1");
FactorySMto factory=new FactorySMto();
factory.setFactoryName("fa1");
Set<ProductSMto> set=new HashSet<ProductSMto>();
set.add(product);
factory.setProducts(set);
session.save(factory);
transaction.commit();//两个都保存了
session.close();
}catch(Exception ex) {
session.getTransaction().rollback();
}finally {
session.close();
}
}
}
执行结果:
Hibernate: alter table productsmto add constraint FK7fk048c4rhhssuwajyh2jyp5n foreign key (factoryid) references factorysmto (factoryid)
Hibernate: insert into factorysmto (factoryname) values (?)
Hibernate: insert into productsmto (productname) values (?)
Hibernate: update productsmto set factoryid=? where productid=?
单向一对多,会给product映射表生成外键(参照factory表的主键)(即使product持久化类甚至都没有关联factory类),而factory映射表不会生成和product表相关的属性。(即一定是多方的表中有外键引用一方的表的主键,不管多方的类有没有一方的对象)
这里提前说一下,所有的一多关系(单向一对多、单向多对一、双向多对一),数据库中的表字段是没有变化的,都是多方的外键指向一方的主键,但是在实体类中的属性会有变化,比如单向多对一中,在User中增加了一个Classes classes的变量,目的就是为了存放班级对象实例。在单向一对多中,在一方也就是Classes类中加了一个set集合用来装自己的学生。这两个属性跟表字段没什么关系,这个不要搞混淆
三、双向多对一或者叫双向一对多
1.多方设置cascade
package com.zhj.VO;
public class ProductSMto {
public int productId;
public String productName;
public FactorySMto factory;
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public FactorySMto getFactory() {
return factory;
}
public void setFactory(FactorySMto factory) {
this.factory = factory;
}
}
package com.zhj.VO;
import java.util.Set;
public class FactorySMto {
public int factoryId;
public String factoryName;
public Set<ProductSMto> products;
public int getFactoryId() {
return factoryId;
}
public void setFactoryId(int factoryId) {
this.factoryId = factoryId;
}
public String getFactoryName() {
return factoryName;
}
public void setFactoryName(String factoryName) {
this.factoryName = factoryName;
}
public Set<ProductSMto> getProducts() {
return products;
}
public void setProducts(Set<ProductSMto> products) {
this.products = products;
}
}
<?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>
<class name="com.zhj.VO.ProductSMto" table="productsmto">
<id name="productId" column="productid" type="int">
<generator class="native"/>
</id>
<property name="productName" column="productname" type="string"></property>
<many-to-one name="factory" class="com.zhj.VO.FactorySMto" cascade="all">
<column name="factoryid"/>
</many-to-one>
</class>
</hibernate-mapping>
<?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>
<class name="com.zhj.VO.FactorySMto" table="factorysmto">
<id name="factoryId" column="factoryid" type="int">
<generator class="native"/>
</id>
<property name="factoryName" column="factoryname" type="string"></property>
<set name="products">
<key column="factoryid"/>
<one-to-many class="com.zhj.VO.ProductSMto"/>
</set>
</class>
</hibernate-mapping>
package com.zhj.TestH;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.zhj.VO.FactorySMto;
import com.zhj.VO.ProductSMto;
import com.zhj.utils.hibernateUtils;
public class TestSMto {
public static void main(String[] args) {
Session session=null;
Transaction transaction=null;
try {
session=hibernateUtils.getSessionFactory().openSession();
transaction=session.beginTransaction();
ProductSMto product=new ProductSMto();
product.setProductName("p1");
FactorySMto factory=new FactorySMto();
factory.setFactoryName("fa1");
Set<ProductSMto> set=new HashSet<ProductSMto>();
set.add(product);
factory.setProducts(set);
product.setFactory(factory);
session.save(product);
transaction.commit();//两个都保存了
session.close();
}catch(Exception ex) {
session.getTransaction().rollback();
}finally {
session.close();
}
}
}
product映射表会生成factoryid,factory映射表不会生成和Product类相关的字段
2.一方设置cascade
后面我感觉都和上面类似的
四、单向一对一主键关联
1.只能给关联方的一方设置cascade(只有关联方的xml写了<one-to-one>)
五、双向一对一主键关联
1.给参照方的一方设置cascade
2.给关联方的一方设置cascade
六、单向一对一外键关联
1.只能给关联方的一方设置cascade(只有关联方的xml写了<one-to-one>)
七、双向一对一外键关联
1.给参照方的一方设置cascade
2.给关联方的一方设置cascade
八、多对多关联
默认两方都会维护这多对多的关系,都会往中间表插数据。注意:对于双向的多对多关联,必须把其中一端的 invers设置为true,否则两端都维护关联关系会造成主键冲突。
package com.zhj.VO;
import java.util.Set;
public class Teacher {
private int id;
private String name;
private Set<Classes> classesSet;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Classes> getClassesSet() {
return classesSet;
}
public void setClassesSet(Set<Classes> classesSet) {
this.classesSet = classesSet;
}
}
package com.zhj.VO;
import java.util.Set;
public class Classes {
private int id;
private String name;
private Set<Teacher> teachers;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
}
<?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>
<class name="com.zhj.VO.Teacher" table="t_teacher">
<id name="id" column="id" >
<generator class="native"/>
</id>
<property name="name" ></property>
<set name="classesSet" table="t_teacher_classes" inverse="true">
<key column="teacher_id"/>
<many-to-many class="com.zhj.VO.Classes" column="classes_id"/>
</set>
</class>
</hibernate-mapping>
<?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>
<class name="com.zhj.VO.Classes" table="t_classes">
<id name="id" column="id" >
<generator class="native"/>
</id>
<property name="name" ></property>
<set name="teachers" table="t_teacher_classes" >
<key column="classes_id"/>
<many-to-many class="com.zhj.VO.Teacher" column="teacher_id"/>
</set>
</class>
</hibernate-mapping>
package com.zhj.TestH;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.zhj.VO.Classes;
import com.zhj.VO.Teacher;
import com.zhj.utils.hibernateUtils;
public class M2MTCTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Session session=null;
Transaction transaction=null;
try {
session=hibernateUtils.getSessionFactory().openSession();
transaction=session.beginTransaction();
Teacher t1=new Teacher();
t1.setName("huanglaoshi");
Teacher t2=new Teacher();
t2.setName("lilaoshi");
Classes c1=new Classes();
c1.setName("java1801");
Classes c2=new Classes();
c2.setName("java1802");
Set<Teacher> teachers=new HashSet<>();
teachers.add(t1);
teachers.add(t2);
Set<Classes> classesSet=new HashSet<>();
classesSet.add(c1);
classesSet.add(c2);
t1.setClassesSet(classesSet);
t2.setClassesSet(classesSet);
c1.setTeachers(teachers);
c2.setTeachers(teachers);
session.save(t1);
session.save(t2);
session.save(c1);
session.save(c2);
transaction.commit();
session.close();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
Hibernate: create table t_classes (id integer not null auto_increment, name varchar(255), primary key (id)) engine=MyISAM
Hibernate: create table t_teacher (id integer not null auto_increment, name varchar(255), primary key (id)) engine=MyISAM
Hibernate: create table t_teacher_classes (teacher_id integer not null, classes_id integer not null, primary key (classes_id, teacher_id)) engine=MyISAM
Hibernate: alter table t_teacher_classes add constraint FK5hc6pfigrjqb8foflbv2f8amm foreign key (classes_id) references t_classes (id)
Hibernate: alter table t_teacher_classes add constraint FKnc97pr4of1477exiayutmw0k7 foreign key (teacher_id) references t_teacher (id)
Hibernate: insert into t_teacher (name) values (?)
Hibernate: insert into t_teacher (name) values (?)
Hibernate: insert into t_classes (name) values (?)
Hibernate: insert into t_classes (name) values (?)
Hibernate: insert into t_teacher_classes (classes_id, teacher_id) values (?, ?)
Hibernate: insert into t_teacher_classes (classes_id, teacher_id) values (?, ?)
Hibernate: insert into t_teacher_classes (classes_id, teacher_id) values (?, ?)
Hibernate: insert into t_teacher_classes (classes_id, teacher_id) values (?, ?)