*一)【双向】一对多【客户 vs 订单】优化
(1)传统:保存订单,级联保存客户,设置双向关联--------------7条SQL
问题的前提:是双向关系
问题的产生:在默认情况下,单方和多方,都负责产生SQL语句,这样的话,可能产生不必要的多余SQL语句。
理论上哪方负责都可以,
但实际中用多方负责产生SQL较佳,即多方是主控方。
inverse反转/向
(2)优化:保存订单,级联保存客户,设置单向关联--------------4条SQL
(3)什么情况下用cascade和inverse属性
cascade:
当你需要操作Customer时,同时又要操作对应的所有Order,这样可以使用cascade属性
常用取值:save-update,delete,all
inverse:
前提:必须是双向关联,单向关联不存在inverse的情况
保存多方,要级联保存单方的情况
常用取值:true
Customer.hbm.xml
<?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映射文件 --> <hibernate-mapping package="one2many_double"> <class name="Customer" table="CUSTOMERS"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="name" column="NAME"/> <property name="age" column="AGE"/> <property name="des" column="DES"/> <!-- inverse=true如果出现在Customer类中,表示 Order类是主控方,负责产生SQL语句 inverse=false如果出现在Customer类中,表示 Customer类是主控方,负责产生SQL语句 如果Customer和Order都无inverse的话,表示 Customer和Order都是主控方,负责产生SQL语句 如果这样的话:问题有二: 一)会产生多余的SQL(成功) 二)会产主键重重(失败) --> <set name="orders" table="ORDERS" cascade="all" inverse="true"> <key column="CUSTOMERS_ID"/> <one-to-many class="Order"/> </set> </class> </hibernate-mapping>
Order.hbm.xml
<?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映射文件 --> <hibernate-mapping package="one2many_double"> <class name="Order" table="ORDERS"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="orderno" column="ORDERNO"/> <property name="price" column="PRICE"/> <property name="time" column="TIME"/> <many-to-one name="customer" column="customers_id" cascade="all" /> </class> </hibernate-mapping>
二)【双向】一对一【人 vs 身份证】
private Integer id;
private String name;
private Double salary;
private Card card;
Person.hbm.xml文件
<?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映射文件 --> <hibernate-mapping package="onetoonedouble"> <class name="Person" table="PERSON"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="name" column="NAME"/> <property name="salary" column="SALARY"/> <!-- name:表示Person类的关联属性 property-ref:表示Person类的对方Card类的关联属性 --> <one-to-one name="card" property-ref="person" cascade="all" /> </class> </hibernate-mapping>
private int id;
private String number;
private String location;
Card.hbm.xml文件
<hibernate-mapping package="onetoonedouble"> <class name="Card" table="CARD"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="number" column="NUMBER"/> <property name="location" column="LOCATION"/> <!-- name:表示Card类的关联属性 column:表示cards表对应的外健 not-null:表示该列不能为NULL unique:表示该列的值必须惟一 --> <many-to-one name="person" column="PERSON_ID" not-null="true" unique="true" /> </class> </hibernate-mapping>
(1)保存人,级联保存身份证
(2)查询人,对象导航查询身份证
(3)更新人,级联更新身份证
(4)删除人,级联删除身份证
*三)【双向】多对多【学生 vs 老师】
use hibernate
drop table if exists middles;
drop table if exists students;
drop table if exists teachers;
create table if not exists students(
id int primary key auto_increment,
name varchar(20)
);
create table if not exists teachers(
id int primary key auto_increment,
name varchar(20)
);
create table if not exists middles(
students_id int,
teachers_id int,
primary key(students_id,teachers_id),
constraint students_id_FK foreign key(students_id) references students(id),
constraint teachers_id_FK foreign key(teachers_id) references teachers(id)
);
select * from students;
select * from middles;
select * from teachers;
int id;
String name;
Set<Teacher> teacherset = new HashSet<Teacher>();
Student.hbm.xml文件
<?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映射文件 --> <hibernate-mapping package="many2many_double"> <class name="Student" table="STUDENTS"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="name" column="NAME"/> <!-- name:表示Student类的关联属性 table:表示middles表名 key-column:表示middles表中引用students表的外健 class:表示Student类对应的类型,即Teacher类型 many-to-many-column:表示middles表中引用teachers表的外健 --> <set name="teacherset" table="MIDDLES" cascade="all" inverse="true"> <key column="STUDENTS_ID"/> <many-to-many class="Teacher" column="TEACHERS_ID"/> </set> </class> </hibernate-mapping>
private int id;
private String name;
Set<Student> studentset = new HashSet<Student>();
Teacher.hbm.xml文件
<?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映射文件 --> <hibernate-mapping package="many2many_double"> <class name="Teacher" table="TEACHERS"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="name" column="NAME"/> <set name="studentset" table="MIDDLES" cascade="all"> <key column="TEACHERS_ID"/> <many-to-many class="Student" column="STUDENTS_ID"/> </set> </class> </hibernate-mapping>
(1)保存老师,级联保存学生【学生是主控方或老师是主控方】
(2)删除1号老师,不级联删除学生
@Test
public void test2() {
Session session = sessionFactory.openSession();
Transaction t = session.beginTransaction();
try {
Teacher t1 = (Teacher) session.load(Teacher.class, 1);
for (Student s : t1.getStudentset()) {
s.getTeacherset().remove(t1);
}
t1.setStudentset(null);
session.delete(t1);
t.commit();
} catch (Exception e) {
e.printStackTrace();
t.rollback();
} finally {
session.close();
}
}
四)映射组件整体与部分的关系【客户 vs 公司地址和家庭地址】
(1)hibernate将持久化对象分为二种类型
>>含有OID的对象:实体型,一定对应一条完整记录
>>不含OID的对象:值类型,不能对应一条完整记录,只能对应一条记录的某些部分
<hibernate-mapping package="cn.itcast.web.hibernate.component">
<class name="User" table="USERS">
<id name="id" column="ID">
<generator class="increment"/>
</id>
<property name="name" column="NAME"/>
<!--
name:表示User中JavaBean的属性
class:表示该JavaBean属性的类型
-->
<component name="homeAddress" class="Address">
<property name="province" column="HOME_PROVINCE"/>
<property name="city" column="HOME_CITY"/>
<property name="area" column="HOME_AREA"/>
</component>
<component name="comAddress" class="Address">
<property name="province" column="COM_PROVINCE"/>
<property name="city" column="COM_CITY"/>
<property name="area" column="COM_AREA"/>
</component>
</class>
</hibernate-mapping>
(2)保存客户
(3)查询客户
(4)更新客户
(5)删除客户
*五)深入理解session一级缓存与批处理
(1)对象生命周期图堆栈图
Session是Hibernate中操作数据库的重要对象,完成CRUD操作
Session就是由集合组成
只要Session有引用存在,它所引用的对象,就不会被GC回收
(2)hibernate自动清理session一级缓存的时间
清理flush:session一级缓存根据持久化对象属性值的改变情况,自动生成SQL的过程,叫清理。
注意:此时并没有与数据库交互
(3)对比以下方法:
>>session.flush()
清理session缓存,但session缓存中的持久化对象没有删除
*>>session.evict()
将【一个】持久状态对象转成游离状态对象
>>session.clear()
一次性将位于session缓存中的所有持久状态对象,清出session缓存区
将【多个】持久状态对象转成游离状态对象
*>>transaction.commit()
它包括二个子步骤:
A)session.flush()
B)事务提交,就与数据库交互
*>>session.close()
它包括二个子步骤:
A)session.clear()
B)回收资源
注意:如果要全完销毁Session,一定要加上如下代码:session = null
(4)批量向MySQL数据库插入10万条数据,并记录所耗时间
public void test1() {
Session session = sessionFactory.openSession();
Transaction t = session.beginTransaction();
try {
long begin = System.currentTimeMillis();
for (int i = 1; i <= 100001; i++) {
User user = new User("用户" + i);
session.save(user);
if (i % 1000 == 0) {
t.commit();
session.clear();
t = session.beginTransaction();
}
}// end of for loop
t.commit();
long end = System.currentTimeMillis();
System.out.println((end - begin) / 1000 + "��");
} catch (Exception e) {
e.printStackTrace();
t.rollback();
} finally {
session.close();
}
}
参见<<PPT第21,22页>>
六)安装Oracle数据库11g版本