1.cascade:实现级联cud
1.1.什么是cud
在Hibernate中,CUD是指对数据库进行增加(Create)、更新(Update)和删除(Delete)操作的简称。
-
创建(Create):通过Hibernate,可以使用持久化对象将数据插入到数据库中。将一个新的持久化对象保存到数据库时,Hibernate会自动生成相应的INSERT语句并执行,将数据插入到数据库表中。
-
更新(Update):在Hibernate中,可以通过修改持久化对象的属性值来更新数据库中的数据。当对已存在于数据库中的持久化对象进行更改时,Hibernate会自动检测对象的变化,并生成相应的UPDATE语句来更新数据库中的相关数据。
-
删除(Delete):使用Hibernate,可以从数据库中删除特定的数据行。当删除一个持久化对象时,Hibernate会生成相应的DELETE语句并执行,从数据库中删除对应的数据行。
这些CUD操作使得使用Hibernate进行数据库操作变得更加方便和简洁。开发人员可以通过使用Hibernate提供的API来执行这些操作,而无需手动编写复杂的SQL语句。同时,Hibernate还提供了事务管理和数据一致性等功能,使得数据库操作更加可靠和高效。
1.2 什么是级联(Cascade)
Cascade(级联)是一种配置选项,用于指定当对一个对象进行操作时,是否要同时对与之相关联的其他对象进行相同的操作。
Cascade可以应用于关联关系(Association)中的父子对象,例如一对多(OneToMany)、多对一(ManyToOne)、一对一(OneToOne)等。通过配置Cascade属性,可以实现对关联对象的级联操作。
常见的Cascade选项包括:
-
CascadeType.ALL:表示对关联对象执行所有操作,包括创建、更新和删除。当对主对象进行操作时,会级联执行相同的操作到关联对象。
-
CascadeType.PERSIST(或CascadeType.SAVE_UPDATE):表示对关联对象执行持久化操作(插入/保存和更新)。当对主对象进行持久化操作时,会级联执行相同的操作到关联对象。
-
CascadeType.MERGE:表示对关联对象执行合并操作。当对主对象进行合并操作时,会级联执行相同的操作到关联对象。
-
CascadeType.REMOVE:表示对关联对象执行删除操作。当对主对象进行删除操作时,会级联执行相同的操作到关联对象。
-
CascadeType.REFRESH:表示对关联对象执行刷新操作。当对主对象进行刷新操作时,会级联执行相同的操作到关联对象。
通过使用Cascade属性,我们可以方便地管理关联对象之间的数据一致性,减少手动操作的复杂性。但需要注意,在使用Cascade选项时,需谨慎考虑潜在的数据完整性和性能方面的影响。
1.3 代码测试
默认情况下,在一对多表之间的关系时,如:父亲和孩子;在关联从表时,不能直接关联
- 创建数据库
CREATE TABLE `tab_father` (
`fid` INT(11) NOT NULL AUTO_INCREMENT,
`fname` VARCHAR(100) DEFAULT NULL,
`fjob` VARCHAR(100) DEFAULT NULL,
PRIMARY KEY (`fid`)
) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8
CREATE TABLE `tab_son` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`sname` varchar(100) DEFAULT NULL,
`sage` int(11) DEFAULT NULL,
`sfid` int(11) DEFAULT NULL,
PRIMARY KEY (`sid`),
KEY `fk_s_f` (`sfid`),
CONSTRAINT `fk_s_f` FOREIGN KEY (`sfid`) REFERENCES `tab_father` (`fid`)
) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8
- mapper
<class name="test04.Father" table="tab_father">
<!-- 指定主键列对应的属性 -->
<id name="fid" column="fid" type="int">
<generator class="identity"/>
</id>
<!--非主键对应的属性-->
<property name="fname" column="fname" />
<property name="fjob" column="fjob" />
<!--给属性sonSet赋值-->
<!--cascade="save-update" 实现级联保存-->
<set name="sonSet" lazy="false">
<key column="sfid"></key><!--key column="sfid"指定从表中的外键列名-->
<one-to-many class="test04.Son" />
</set>
</class>
- 启动类
//ctrl+p 提示参数列表
Father f1=new Father(20,"老王1","工程师");
Set<Son> sonSet=new HashSet<>();
//注意:关联的son不能设置id 否则不会插入son 直接修改son
sonSet.add(new Son(null,"王二小",19));
sonSet.add(new Son(null,"王之涣",29));
sonSet.add(new Son(null,"王石",39));
f1.setSonSet(sonSet);
System.out.println("添加::"+session.save(f1));
- 打印的日志信息
Hibernate: insert into tab_father (fname, fjob) values (?, ?)
添加::16
Hibernate: update tab_son set sfid=? where sid=?
Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: test04.Son
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:242)
1.4 实现级联添加
默认情况:
//默认情况下 删除主表 把从表中关联的行的外键值更改为null
Father father;
father=new Father().setFid(15);//NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
father=(Father)session.get(Father.class,15);
session.delete(father);
-
日志
Hibernate: select father0_.fid as fid1_0_, father0_.fname as fname1_0_, father0_.fjob as fjob1_0_ from tab_father father0_ where father0_.fid=?
Hibernate: select sonset0_.sfid as sfid1_, sonset0_.sid as sid1_, sonset0_.sid as sid2_0_, sonset0_.sname as sname2_0_, sonset0_.sage as sage2_0_, sonset0_.sfid as sfid2_0_ from tab_son sonset0_ where sonset0_.sfid=?
Hibernate: update tab_son set sfid=null where sfid=?
Hibernate: delete from tab_father where fid=?
1.5 设置级联删除
- mapper
<!--cascade="delete" 实现级联删除-->
<set name="sonSet" cascade="delete" lazy="false">
<key column="sfid"></key><!--key column="sfid"指定从表中的外键列名-->
<one-to-many class="com.zhiyou100.test04.Son" />
</set>
- 测试代码
-
Father father; father=new Father().setFid(11);//使用此方式删除 是把从表中关联的行的外键值更改为null //father=(Father)session.get(Father.class,9);//此方式会加从表关联的外键也进行删除 session.delete(father);
-
日志
Hibernate: select father0_.fid as fid1_0_, father0_.fname as fname1_0_, father0_.fjob as fjob1_0_ from tab_father father0_ where father0_.fid=?
Hibernate: select sonset0_.sfid as sfid1_, sonset0_.sid as sid1_, sonset0_.sid as sid2_0_, sonset0_.sname as sname2_0_, sonset0_.sage as sage2_0_, sonset0_.sfid as sfid2_0_ from tab_son sonset0_ where sonset0_.sfid=?
Hibernate: update tab_son set sfid=null where sfid=?
Hibernate: delete from tab_son where sid=?
Hibernate: delete from tab_son where sid=?
Hibernate: delete from tab_son where sid=?
Hibernate: delete from tab_father where fid=?
-
注意:级联删除:根据需求/业务逻辑 选择设置
1.6 同时实现级联保存和删除
将属性改为cascade="all"
cascade="all" 等价于 cascade="delete" + cascade="save-update"
2.检索方式
-
检索方式1:ognl:对象导航 Object Graph Navigation Language
-
// 通过a对象获取其关联的b对象的信息 Father f1=(Father)session.get(Father.class,1); for (Son s:f1.getSonSet()) { System.out.println(s); System.out.println("孩子:"+s); } Son s1=(Son)session.get(Son.class,111); f1=s1.getFather(); System.out.println("父亲:"+f1);
-
检索方式2:oid ::object id::通过对象的id获取对象
-
//检索方式2:oid ::object id::通过对象的id获取对象 //通过session的两个方法:get/load f1=(Father)session.get(Father.class,1); f1=(Father)session.load(Father.class,2);
-
检索方式3:hql: hibernate query language:::通过query接口访问数据库
-
//不是表名 是类名 不是列名 是属性名::::通过面向对象的思想操作数据库 // 通过session的createQuery方法一个query接口 //3.1 获取所有 Query query=session.createQuery("from test04.Son"); System.out.println("获取所有:::"+query.list()); //3.2 获取一个 query=session.createQuery("from test04.Son where sid=220"); System.out.println("获取一个:::"+query.uniqueResult()); //3.3 删除一个 query=session.createQuery("delete from test04.Son where sid=220"); System.out.println("删除一个:"+query.executeUpdate()); //3.4 修改 query=session.createQuery("update test04.Son set sage=sage+1"); System.out.println("修改多个:"+query.executeUpdate()); //3.5 query不支持添加insert
-
检索方式4:qbc:::query by Criteria::::通过session的createCriteria方法获取Criteria接口
-
//Criteria接口 是完全面向对象::::只能查询 不支持增删改 Criteria criteria = session.createCriteria(Son.class); System.out.println("获取所有:::"+criteria.list()); //4.2 获取一个 Criterion c1= Restrictions.eq("sid",112);//获取sid=112的记录 criteria.add(c1);//设置查询条件 System.out.println("获取一个:"+criteria.uniqueResult()); //4.3 复杂条件:sname like '%张%' or sage < 20 criteria = session.createCriteria(Son.class); Criterion c21= Restrictions.like("sname","%张%"); Criterion c22= Restrictions.lt("sage",20); Criterion c2=Restrictions.or(c21,c22); criteria.add(c2);//设置查询条件 System.out.println("获取满足复杂条件的记录:"+criteria.list()); //4.4 实现分页 criteria = session.createCriteria(Son.class); criteria.setFirstResult(2);//设置起始索引::从0开始 criteria.setMaxResults(3);//设置总记录数 System.out.println("分页实现:"+criteria.list()); // croteria支持方法链 System.out.println("分页实现:"+session.createCriteria(Son.class).setMaxResults(3).setFirstResult(1).list());
-
检索方式5:本地sql:::通过SqlQuery接口 由原始的sql语句 操作数据库
-
SQLQuery sqlQuery = session.createSQLQuery("select * from tab_son"); System.out.println("获取所有:"+sqlQuery.addEntity(Son.class).list()); sqlQuery = session.createSQLQuery("select * from tab_son where sid=211"); System.out.println("获取一个:"+sqlQuery.addEntity(Son.class).uniqueResult()); sqlQuery = session.createSQLQuery("insert into tab_son(sid,sname,sage) values(999,'呵呵呵',18)"); System.out.println("添加一个:"+sqlQuery.addEntity(Son.class).executeUpdate()); sqlQuery = session.createSQLQuery("update tab_son set sage=sage+1"); System.out.println("修改多个:"+sqlQuery.addEntity(Son.class).executeUpdate()); sqlQuery = session.createSQLQuery("delete from tab_son where sid=113"); System.out.println("删除多个:"+sqlQuery.addEntity(Son.class).executeUpdate());