在这里主要说下两张表关联的删除操作(存在一对多、多对一的关系),我在这里只测试了在inverse(true/false),级联cascade(all [save,update,delete,saveorupdate]/ none)的影响下,数据查询方式分别为new方式和get两种状况的影响下的删除操作。
以下我的代码虽然很啰嗦,但是为了能够更明显的看出区别与变化只好出此下策了,如果这还看不明白。我也没办法了。这一篇其实还是值得一看的,尤其是这个:
注意:hibernate框架下的通过get方式查询和通过new方式查询是有很大区别的,并且它两的执行机制也是不一样的。
一、项目的所有包及架包
二、数据库信息
表字段信息:
表二字段信息:
三、实体类.hbm.xml配置
boy表:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.yibin.empty">
<class name="Boy" table="boy">
<id name="bid">
<generator class="identity"></generator>
</id>
<property name="age"></property>
<property name="bname" type="string" length="50"></property>
<set name="girlsfriend" table="girl" inverse="true" cascade="all">
<key column="bid"></key>
<one-to-many class="Girl"/>
</set>
<!-- inverse="true" -->
</class>
</hibernate-mapping>
girl表:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.yibin.empty">
<class name="Girl" table="girl">
<id name="gid">
<generator class="identity"></generator>
</id>
<property name="gname" type="string" length="50"></property>
<many-to-one name="boy" column="bid" class="Boy"></many-to-one>
</class>
</hibernate-mapping>
四、hibernate配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- 开始配置我们的工厂,因为在某些方面工厂比较强大 -->
<session-factory>
<!-- 其次配置我们的数据源 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///inverse_cascade</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- 然后在配置我们hibernate独有的一个配置dialect(方言)作用是:为每一种数据库提供适配器,方便转换 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 根据需求是否需要在控制台输出我们执行的SQL语句 -->
<property name="show_sql">true</property>
<!-- 根据实体类自动创建表 -->
<property name="hbm2ddl.auto">update</property>
<!-- 接着配置我们的需要操作表的实体类的映射文件配置 -->
<mapping resource="com/yibin/empty/Boy.hbm.xml"/>
<mapping resource="com/yibin/empty/Girl.hbm.xml"/>
</session-factory>
</hibernate-configuration>
五、测试类
package test.yibin.inverse;
import org.hibernate.Session;
import org.junit.Test;
import com.yibin.empty.Boy;
import com.yibin.empty.Girl;
import com.yibin.session.HibernateSessionFactory;
/**
* 测试两表关联,inverse与cascade对删除操作的影响
* @author 廖宜彬
*时间:2018-6-22 20:35:29
*/
public class Testdelete {
@Test
/**
* 首先测试我们两表关联的删除测试
* 具备条件是:控制反转为默认状态,且级联为none(默认状态)
* 并且查询方式是通过new对象获取的
*/
public void deleteinverse() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=new Boy();
boy.setBid(7); //这是通过我们new对象获取到我们该id的一个对象,虽然这个方法也获取到了内容,但是与
//我们的get方式是有区别的。用new我们是创建了一个对象去接收,该对象是属于游离状态的与从表的关联字段的关系是已经切断了的
//
session.delete(boy);
/** 这个操作是执行了的,执行的SQL语句是
* Hibernate: update girl set bid=null where bid=?
* Hibernate: delete from boy where bid=?
*
*数据库的显示结果是:
*主表(boy表)该记录是删除了的,而从表(girl表)的关联字段bid更新为null了
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
}
@Test
/**
* 再次测试我们两表关联的删除测试
* 具备条件是:控制反转为true状态(失去主控权,将主控权交还从表),且级联为none(默认状态)
* 并且查询方式仍旧是通过new对象获取的
*/
public void deleteinverse2() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=new Boy();
boy.setBid(8);
session.delete(boy);
/** 这个操作是执行了的,执行的SQL语句是,
* Hibernate: delete from boy where bid=?
*
*执行时报错了:
*Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`inverse_cascade`.`girl`, CONSTRAINT `FK306A9CC2241EE4` FOREIGN KEY (`bid`) REFERENCES `boy` (`bid`))
*报错原因很明显,因为两表之间有外键约束所以执行删除操作时失败了
*
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
}
@Test
/**
* 再次测试我们两表关联的删除测试
* 具备条件是:控制反转为默认状态(具备主控权),且级联为all(即拥有save,update,saveorupdate,delete)
* 并且查询方式仍旧是通过new对象获取的
*/
public void deleteinverse3() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=new Boy();
boy.setBid(8);
session.delete(boy);
/** 这个操作是成功的,执行的SQL语句是
* Hibernate: update girl set bid=null where bid=?
* Hibernate: delete from boy where bid=?
*
* 数据库内结果为:
* 主表(boy表)该记录是删除了的,而从表(girl表)的关联字段bid更新为null了。由此可见,不论我们是否开启级联都对我们的删除操作五影响
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
}
@Test
/**
* 再次测试我们两表关联的删除测试
* 具备条件是:控制反转为true状态,且级联为all(即拥有save,update,saveorupdate,delete)
* 并且查询方式仍旧是通过new对象获取的
*/
public void deleteinverse4() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=new Boy();
boy.setBid(9);
session.delete(boy);
/** 执行的SQL语句是
* Hibernate: delete from boy where bid=?
* 执行失败,报错为:
* Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`inverse_cascade`.`girl`, CONSTRAINT `FK306A9CC2241EE4` FOREIGN KEY (`bid`) REFERENCES `boy` (`bid`))
*
* 报错原因很明显,因为两表之间有外键约束所以执行删除操作时失败了。并且主表失去了主控权
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
/** 两两之间比较进行个小结
* 首先,我们的查询都是通过new的方式获取的
* 1.不论我们是否开启级联,当我们的控制反转状态是默认状态时,操作是成功的。结果是:
* 我们的主表数据删除成功,从表关联字段数据更新为null。由执行的SQL语句便可以理解
* 2.不论我们是否开启级联,当我们的控制反转状态是true时(即主表失去主控权),操作是失败的:
* 原因是,首先我们的主表失去了主控权。但是我们两表之间关联存在着外键约束,所以执行删除操作时无法执行
*
*/
}
/**************************以下是我查询方式为get的测试********************************************/
@Test
/**
* 具备条件是:控制反转为默认状态,且级联为none(默认状态)
* 并且查询方式是通过get对象获取的
*/
public void deleteinverseget() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=(Boy)session.get(Boy.class, 9);
//这是我们通过get的方式获取数据的,该方式获取到的对象是持久状态的,因为get是直接从数据库得到的,所以它们直接的关联联系是存在的
session.delete(boy);
/** 这个操作是执行了的,执行的SQL语句是
*Hibernate: select boy0_.bid as bid0_0_, boy0_.age as age0_0_, boy0_.bname as bname0_0_ from boy boy0_ where boy0_.bid=?
*Hibernate: update girl set bid=null where bid=?
*Hibernate: delete from boy where bid=?
*
*数据库的显示结果是:
*主表(boy表)该记录是删除了的,而从表(girl表)的关联字段bid更新为null了
*
*由结果可知我们查询方式get和new的区别,看执行的sql语句便可以理解为什么他们的状态是不一样的,
*虽然结果是一样的,但执行的机制却是不同的
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
}
@Test
/**
* 具备条件是:控制反转为默认状态,且级联为all
* 并且查询方式是通过get对象获取的
*/
public void deleteinverseget2() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=(Boy)session.get(Boy.class, 10);
session.delete(boy);
/** 这个操作是执行了的,执行的SQL语句是
*Hibernate: select boy0_.bid as bid0_0_, boy0_.age as age0_0_, boy0_.bname as bname0_0_ from boy boy0_ where boy0_.bid=?
*Hibernate: select girlsfrien0_.bid as bid0_1_, girlsfrien0_.gid as gid1_, girlsfrien0_.gid as gid1_0_, girlsfrien0_.gname as gname1_0_, girlsfrien0_.bid as bid1_0_ from girl girlsfrien0_ where girlsfrien0_.bid=?
*Hibernate: update girl set bid=null where bid=?
*Hibernate: delete from girl where gid=?
*Hibernate: delete from girl where gid=?
*Hibernate: delete from girl where gid=?
*Hibernate: delete from boy where bid=?
*
*数据库的显示结果是:
*主表(boy表)该记录删除了的,从表(girl表)的关联字段也删除了
*
*该操作与我们通过new的方式对比下,你会发现使用get的方式是全删除了,而new的则没有
*原因呢,是因为它两的执行机制不一样,new呢是直接先将从表的关联字段更新为null(因为两表之间已经没有什么维护关系了)
*而我们的get呢,则是先查询。首先查询我们的主表其次是从表,若存在该数据,则将从表的关联字段更新为null然后在删除与主表有该关系的记录,最后在删除我们的主表
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
}
@Test
/**
* 具备条件是:控制反转为true状态,且级联为none
* 并且查询方式是通过get对象获取的
*/
public void deleteinverseget3() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=(Boy)session.get(Boy.class, 11);
session.delete(boy);
/** 这个操作是执行了的,执行的SQL语句是
*Hibernate: select boy0_.bid as bid0_0_, boy0_.age as age0_0_, boy0_.bname as bname0_0_ from boy boy0_ where boy0_.bid=?
*Hibernate: delete from boy where bid=?
*
*
*该操作报错了(自然该操作是失败的,数据库数据并未改变):
*Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`inverse_cascade`.`girl`, CONSTRAINT `FK306A9CC2241EE4` FOREIGN KEY (`bid`) REFERENCES `boy` (`bid`))
*
*该操作与我们通过new的方式对比下,你会发现使用get的方式是它的执行机制多了个查询报错原因是一样的
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
}
@Test
/**
* 具备条件是:控制反转为true状态,且级联为all
* 并且查询方式是通过get对象获取的
*/
public void deleteinverseget4() {
Session session=HibernateSessionFactory.creSession();
session.beginTransaction();
Boy boy=(Boy)session.get(Boy.class, 11);
session.delete(boy);
/** 这个操作是执行了的,执行的SQL语句是
*Hibernate: select boy0_.bid as bid0_0_, boy0_.age as age0_0_, boy0_.bname as bname0_0_ from boy boy0_ where boy0_.bid=?
*Hibernate: select girlsfrien0_.bid as bid0_1_, girlsfrien0_.gid as gid1_, girlsfrien0_.gid as gid1_0_, girlsfrien0_.gname as gname1_0_, girlsfrien0_.bid as bid1_0_ from girl girlsfrien0_ where girlsfrien0_.bid=?
*Hibernate: delete from girl where gid=?
*Hibernate: delete from girl where gid=?
*Hibernate: delete from girl where gid=?
*Hibernate: delete from boy where bid=?
*
*数据库结果为:
*主表与从表的关联数据全部删除
*
*该操作与我们通过new的方式对比下,你会发现使用get的方式操作是成功的,并未报错而new的方式则报错了。
*为什么在相同条件下,使用new的方式则报错而使用get的方式则成功呢?原因很简单,还是那句老话。使用new因为两表之间的关系没了
*所以删除时有外键约束,而使用get,因为是直接从数据库获取的所以他们两表之间的关系是还有的,自然执行删除是可以的
*/
session.getTransaction().commit();
HibernateSessionFactory.closesession(session);
}
}