您的位置:首页 > 其它

Hibernate3.6 入门3:映射_级联_对象状态

2015-10-24 09:30 337 查看

一.对象状态

1.临时状态

a.直接new出来的对象b.不处于session管理c.数据库没有对象记录

2.持久化状态

a.处于session管理b.数据库中有对应的记录c.持久化对象的来源:执行session对象的方法:save/update/get/load/list()等方法,获取到的当前对象.d.若对持久化的对象进行修改,提交事务的时候会映射到数据库中,同步数据库数据.

3.游离状态

a.一般session关闭后,对象就会由持久化变为游离b.不处于sessio
4000
n管理c.数据库中有此记录

4.状态的转换

<span style="font-size:14px;">    private static SessionFactory sf;
static {
sf = new Configuration()
.configure()
.addClass(User.class)
.addClass(Address.class)
.buildSessionFactory();
}
public void po_status(){
Session session = sf.openSession();
session.beginTransaction();

// 创建对象
User user = new User();		// 【【临时】】
user.setName("测试用户");

// 保存
session.save(user);			//*****  insert
// 【【持久化】】
// 修改
user.setName("test1");		// 修改持久化对象,提交时候会反映到数据库!     ****** update
user.setName("test2");
user.setName("test3");		// (智能) 以最后修改的为准!  最后,只是一条update语句!

session.getTransaction().commit();
session.close();

// 【事务提交后【user,为游离状态】】
user.setName("aaaaaaaaaaaaaa");  // 不会反映到数据库
}</span>

二、一级缓存(session缓存)

1.Session缓存    

 Hibernate的一级缓存是由Session提供的,因此它存在于Session的整个生命周期中,当程序调用save()/update()/saveOrupdate()/get()等及查询接口方法list()/iterator()方法时候,如果session中不存在该对象,那么会先将本次的对象存储到一级缓存中便于以后使用,当Session关闭时同时清空一级缓存数据.clear()/evict()

2.Session的缓存作用

  减少访问数据库的次数,进而提高效率.保证缓存中的对象与数据库的记录保持同步,当缓存的对象改变后,session不会立即执行sql,而是将多个sql语句合并为一条sql进行执行,提高效率.  

3.清除缓存

清空session缓存的指定对象:session.evict([对象]);清空session缓存的所有对象:session.clear();当session对象关闭后,缓存就自动被清除了.

4.手动实现一级缓存与数据库同步

session.flush();其实提交事务时,就是调用了flush()方法.

===*5.list()与iterator()查询的区别:

list()查询:只执行一条select查询语句.即,返回值就保存了所有的数据库记录,遍历返回值就可以获取每一条记录.iterator()查询:会执行N+1条sql语句.a.先出现数据表中所有的主键,返回所有主键的集合;b.遍历iterator集合,获取每一个主键,在根据主键生成select查询语句,并执行.c.即,iterator()查询是懒加载,只有用到的时候才后查询.list()与iterator()的缓存:1).list()的查询结果会放入缓存中,但此查询不会获取缓存的数据.即,当完成了第一次list()查询后,再执行一次list()查询,第二次查询不会获取缓存中的数据,而是重新查询.2).iterator()的查询结果会放入缓存中,也会获取缓存中的数据.即当执行第二次iterator()查询时,若缓存中有相同的数据,就会直接获取缓存中的数据,而不会重新查询数据表.

三、级联操作

cascade级联操作:save-update/delete/all/none

在 映射配置文件 中:
<set cascade="none" ></set>
<many-to-one cascade="none"></many-to-one>
默认为none值,关闭级联操作.

若级联操作开启时,当一个数据表与另一个数据库有关联(引用),当修改一个数据表的数据,那么就会影响另一个表中的数据.影响效果跟cascade的值有关.
<span style="font-size:14px;">         //如:若cascade="save-update"
public void save(){
Session session = sf.openSession();
session.beginTransaction();

// 用户
User user = new User();
user.setName("Jack");
user.setAge(18);
// 地址
Address add1 = new Address();
add1.setAddress("广东湛江");
add1.setProvince("广东");
add1.setCity("湛江");
// 关系2
add1.setUser(user);

/*
* 保存
*/
session.save(add1);
//不需要手动保存用户,才能保存地址.可以直接保存地址,而用户会由后台维护.

session.getTransaction().commit();
session.close();
}

<span style="white-space:pre">	</span>//若cascade="delete"
public void delete() {
Session session = sf.openSession();
session.beginTransaction();

//删除   ----  成功
//自动维护t_employee表的外键引用,将其置空
//一对多
Object obj = session.get(Dept.class, 2);
session.delete(obj);
/*
*Hibernate: select dept0_.id as id0_0_, dept0_.deptName as deptName0_0_ from t_dept dept0_ where dept0_.id=?
Hibernate: update t_employee set dept_id=null where dept_id=?
Hibernate: delete from t_dept where id=?

查询要删除的对象Dept  id=2
将外键表中的引用置空为null,即Employee中所有的dept_id=2的数据的dept_id都为null
删除对象
*/

/*
//成功
//删除用户后,该用户引用的部门id对应的部门记录也被删除,而用户表的另一个引用了部门表的id被置空
Object obj = session.get(Employee.class, 2);
session.delete(obj);

* Hibernate: select employee0_.id as id1_0_, employee0_.empName as empName1_0_, employee0_.age as age1_0_, employee0_.gender as gender1_0_, employee0_.dept_id as dept5_1_0_ from t_employee employee0_ where employee0_.id=?
Hibernate: select dept0_.id as id0_0_, dept0_.deptName as deptName0_0_ from t_dept dept0_ where dept0_.id=?
Hibernate: update t_employee set dept_id=null where dept_id=?
Hibernate: delete from t_employee where id=?
Hibernate: delete from t_dept where id=?
*
* 先查询要删除的对象id=2的Employee
* 根据查询的对象Employee(id=2)的外键(dept_id)查询对应的主键表(Dept)的记录
* 将要删除的对象(Employee)的外键值(dept_id)的所有记录(Employee中)的外键置空为null
* 删除对象Employee  id=2
* 删除对象对应的主键表记录 Dept id=dept_id
*/

/*
//更新 -----成功
//不需要保存部门,保存e1就行,d自动维护
//员工
Employee e1 = new Employee();
e1.setEmpName("Man");
//部门
Dept d = new Dept();
d.setDeptName("财务部");

e1.setDept(d);
//d.getEmployees().add(e1);//失败

session.save(e1);
//session.save(d);
*/

session.getTransaction().commit();
session.close();
}</span>

四、映射

1).一对一映射

1.基于对象映射

外键表的外键不是主键.a.JavaBean,属性略
<span style="font-size:14px;">public class IdCard {

private int id;
private String cardNo;
private String place;
// 身份证,关联的用户;  一对一;  有外键方
private Person person;
}
public class Person {

private int id;
private String name;
private int age;
// 身份证; 一对一;  没有外键方
private IdCard idcard;
}b.映射配置</span>

b.映射配置
<span style="font-size:14px;"><!-- IdCard对象的映射配置 -->
<class name="IdCard" table="t_IdCard">
<id name="id">
<generator class="native"></generator>
</id>
<property name="cardNo" length="50"></property>
<property name="place" length="20"></property>

<!-- 一对一,有外键方 (person映射到数据库,对应的是外键字段!) -->
<!-- unique="true" 添加唯一约束! -->
<many-to-one name="person" class="Person" column="person_id" unique="true" cascade="save-update"></many-to-one>
</class>

<!-- Person对象的映射配置 -->
<class name="Person" table="t_person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" length="50"></property>
<property name="age"></property>

<!-- 一对一, 无外键方 -->
<one-to-one name="idcard" class="IdCard"></one-to-one>
</class></span>

2.基于外键策略:主键作为外键

外键表的外键是表的主键.

a.JavaBean

<span style="font-size:14px;">public class IdCard {

private int person_id;
private String cardNo;
private String place;
// 身份证,关联的用户; 一对一; 有外键方
private Person person;
}

public class Person {

private int id;
private String name;
private int age;
// 身份证; 一对一; 没有外键方
private IdCard idcard;
}</span>b.配置映射
<span style="font-size:14px;"><class name="IdCard" table="t_IdCard">
<!-- 主键生成策略: 外键策略! -->
<id name="person_id" column="person_id">
<generator class="foreign">
<span style="white-space:pre"> </span><param name="property">person</param> <!-- 引用的是one-to-one中的name属性! -->
</generator>
</id>
<property name="cardNo" length="50"></property>
<property name="place" length="20"></property>

<!-- 一对一 【主键生成策略是: 外键策略】 -->
<!-- constrained="true" 表示在主键上添加外键约束! -->
<one-to-one name="person" class="Person" constrained="true"></one-to-one>
</class>
------------------------------------------------
<class name="Person" table="t_person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" length="50"></property>
<property name="age"></property>

<!-- 一对一, 无外键方 -->
<one-to-one name="idcard" class="IdCard"></one-to-one>
</class></span>

2).组合映射

<span style="font-size:14px;">由多个(类)对象映射成一张表.

1.JavaBean
public class Car {
private int id;
private String name;
private double price;
// 车轮
private Wheel wheel;
}

public class Wheel {
private int size;
private int count;
private double price;
}</span>
<span style="font-size:14px;">2.映射配置
<class name="Car" table="t_cat">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" length="50"></property>
<property name="price"></property>

<!-- 组合映射:由两个对象组成一张表 -->
<component name="wheel">
<property name="count"></property>
<property name="size"></property>
<property name="price" column="wheel_price"></property>
</component>
</class></span>


3).继承映射

<span style="font-size:14px;">//*******JavaBean**********
//父类
public class Animal {
private int id;
private String name;
}
//子类
public class Cat extends Animal{
// 抓老鼠
private String catching;
}
pub
c59f
lic class Dog extends Animal{
// 玩耍
private String play;
}</span>

1.普通继承映射

<span style="font-size:14px;"><class name="Cat" table="t_cat">
<!-- 父类 -->
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>

<!-- 子类 -->
<property name="catching"></property>
</class></span>

注意:当Animal的子类有很多的时候,就会有很多子类对应的表,那么如何查询出来所有的Animal子类对象的记录?HQL“fromAnimal”,此时报错为没有指定的hbm文件,因此需要在HQL语句查询的时候指定全类名.HQL  “from cn.hib.extend.Animal”即可.

2.方式1:整个继承结构一张表(不推荐)

<span style="font-size:14px;"><class name="Animal">
<!-- 父类 -->
<id name="id">
<generator class="native"></generator>
</id>
<!-- 鉴别器字段,主要用于区分是哪个子类 [冗余字段] -->
<discriminator column="type_" type="string"></discriminator>
<!-- 父类字段 -->
<property name="name"></property>

<!-- 子类:猫 -->
<!-- discriminator-value 指定鉴别器字段(type_)的值;  如果没有指定默认是子类的全名! -->
<subclass name="Cat" discriminator-value="cat_">
<property name="catching"></property>
</subclass>

<!-- 子类:狗-->
<subclass name="Dog" discriminator-value="dog_">
<property name="play"></property>
</subclass>
</class></span>
表结构:
ID     NAME      catching    play        type_(区分不同的子类) 【鉴别器】
 1      黑猫       抓老鼠     NULL         Cat
 2       狗           NULL       抓猫         Dog

结论:
1. 整个继承,只需要一个映射文件.
父类、子类的信息写在一起.
减少xml文件的个数.
2. 生成的表,不符合数据库设计原则.
有很多冗余的字段.
如:鉴别器字段  猫数据的play字段   狗数据的catching字段   都为冗余字段.

3.方式2:每个类对应一张表(不是特别好)

<span style="font-size:14px;"><class name="Animal">
<!-- 父类 -->
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>

<!-- 子类:猫 -->
<joined-subclass name="Cat" table="t_cat">
<key column="animal_id"></key>
<span>	</span><property name="catching"></property>
<span>	</span></joined-subclass>

<!-- 子类:狗-->
<joined-subclass name="Dog" table="t_dog">
<key column="animal_id"></key>
<property name="play"></property>
</joined-subclass>
</class></span>
表结构:
t_animal  父类表(id被子类的映射表引用为主键)
id    name

t_cat     猫表
animal_id    catching      【animal_id    是外键,应用父类表的主键】

t_dog     狗狗
animal_id    play

结论:

a. 整个继承结构,1个映射文件.
b. 父类对应表,每个子类对应表.
c. 表太多,表关系比较复杂.
   表关系复杂,数据库效率低下.
例如: 插入猫
 先把父类信息插入父类对应表.
 再把猫的信息,插入当前子类.(外键引用.)

4.方式3:父类对应表, 每个子类一张表(推荐)

<span style="font-size:14px;"><!-- abstract="true" 表示当前映射的对象,不对应表  -->
<class name="Animal" abstract="true">
<!-- 父类 -->
<id name="id">
<generator class="assigned"></generator>
</id>
<property name="name"></property>

<!-- 子类:猫 -->
<union-subclass name="Cat" table="t_cat">
<property name="catching"></property>
</union-subclass>

<!-- 子类:狗-->
<union-subclass name="Dog" table="t_dog">
<property name="play"></property>
</union-subclass>
</class></span>
表结构:(父类Animal不会生成表)
t_cat     猫id      name     catching      

t_dog     狗id      name     play
结论:
a. 最合理的方式.b. 简化映射配置* 映射文件,只需要一个* 父类属性,只需要写一次c. 生成的表,最合理.注意:主键不能为自增长.

五、关于查询

<span style="font-size:14px;">//1. hql入门
// auto-import="true"  默认值,hql查询的时候可以不用写包名!
// auto-import="false" hql查询的时候必须写类全名
Query q = session.createQuery("from  User");

//2. 查询全部
//q = session.createQuery("from  User");
//q = session.createQuery("select u from  User u");
//q = session.createQuery("select u.* from  User u");  // 不支持 *, 报错!

//3. 查询指定的列,   不会自动封装为对象!
// 如果查询的是一列,返回的list集合是: List<Object>
// 如果查询的是多列, 返回的list集合是: List<Object[]>
//q = session.createQuery("select id,name,age from  User");
//List<Object[]> list =  q.list();
//System.out.println(list);

//3. 查询指定的列, 自动封装
//q = session.createQuery("select new User(id,name,age) from  User");
//System.out.println(q.list());

//4. 条件查询!
q = session.createQuery("from User where name='副班1' or name='副班2'");
q = session.createQuery("from User where name='副班1' and name='副班2'");
q = session.createQuery("from User where id between 1 and 3");
q = session.createQuery("from User where id in(1,2)");
q = session.createQuery("from User where name like '%班%'");

//5. ?  占位符查询
q = session.createQuery("from User where name=? or name=?");
// 设置占位符值
q.setParameter(0, "副班1");		// 注意:下表从0开始!   (JDBC的pstmt从1开始!)
q.setParameter(1, "副班2");

//5. 命名查询
q = session.createQuery("from User where name=:name1_ or name= :name2_");
q.setParameter("name1_", "副班1");
q.setParameter("name2_", "副班2");

//6. 从xml中获取hql,执行!
//q = session.createQuery("from User where id>=1 and id<=3 ");
q = session.getNamedQuery("my_hql_byId");
System.out.println(q.list());
/*
<!-- 保存hql语句 -->
<!-- CDATA, 用于批量转移字符串! -->
<query name="my_hql_byId">
<![CDATA[
from User where id>=3 and id<=4
]]>
</query>
*/</span>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: