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

14 篇文章 0 订阅

一.对象状态

1.临时状态

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

2.持久化状态

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

3.游离状态

a.一般session关闭后,对象就会由持久化变为游离
b.不处于session管理
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;
}
public 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>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值