Hibernate 详解

基础

1. 什么是框架

   框架是软件的半成品,已经完成了相应部分的内容了.只需要实现自己业务即可.

2. ORM(对象关系映射)

O:面向对象领域的Object(JavaBean对象)
R:关系数据库领域的Relational(表的结构)
M:**映射**Mapping(XML的配置文件)为桥梁链接 对象 和 表
Hibernate使程序员通过操作对象的方式来操作数据库表记录

3. Hibernate概念

Hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量	级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 
Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以	在Servlet/JSP的Web应用中使用。
Hibernate是轻量级JavaEE应用的持久层解决方案,是一个关系数据库ORM框架

4. Hibernate的优点

  1. Hibernate对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码
  2. Hibernate是一个基于jdbc的主流持久化框架,是一个优秀的orm实现,它很大程度的简化了dao层编码工作
  3. Hibernate的性能非常好,因为它是一个轻量级框架。映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系

5. hibernate.cfg.xml配置文件

   *  数据库连接信息:
        hibernate.connection.driver_class           -- 连接数据库驱动程序
        hibernate.connection.url                    -- 连接数据库URL
        hibernate.connection.username               -- 数据库用户名
        hibernate.connection.password               -- 数据库密码
    * 方言  hibernate.dialect                       -- 操作数据库方言
    * hibernate.show_sql                            -- 显示SQL
    * hibernate.format_sql                          -- 格式化SQL
    * hibernate.hbm2ddl.auto                        -- 通过映射转成DDL语句
        * create                -- 每次都会创建一个新的表.---测试的时候
        * create-drop           -- 每次都会创建一个新的表,当执行结束之后,将创									建的这个表删除.---测试的时候
        * update                -- 如果有表,使用原来的表.没有表,创建一个新的表.									同时更新表结构.
        *  validate              -- 如果有表,使用原来的表.同时校验映射文件与表中									字段是否一致如果不一致就会报错.
	 *  加载映射  
	 如果XML方式:
   < mapping resource="cn/atcast/hibernate/domain/User.hbm.xml" />

6. 映射文件配置

 * < class>标签       -- 用来将类与数据库表建立映射关系
     name         -- 类的全路径
     table         -- 表名.(类名与表名一致,那么table属性也可以省略)
     catalog       -- 数据库的名称,基本上都会省略不写
* < id>标签 -- 用来将类中的属性与表中的主键建立映射,id标签就是用来配置主键的。
     name          -- 类中属性名
     column       -- 表中的字段名.(如果类中的属性名与表中的字段名一致,那么                        						column可以省略.)
     length        -- 字段的程度,如果数据库已经创建好了,那么length可以不写。						如果没有创建好,生成表结构时,length最好指定。
* < property>        -- 用来将类中的普通属性与表中的字段建立映射.
    name          -- 类中属性名
    column      -- 表中的字段名.(如果类中的属性名与表中的字段名一致,那么							column可以省略.)
    length        -- 数据长度
    type          -- 数据类型(一般都不需要编写,如果写需要按着规则来编写)
          Hibernate的数据类型    type="string"
          Java的数据类型        type="java.lang.String"
          数据库字段的数据类型    < column name="name" sql-type="varchar"/>
  1. 主键生成策略
    identity    自增长(mysql,db2)
	sequence  自增长(序列), oracle中自增长是以序列方法实现
	native     自增长【会根据底层数据库自增长的方式选择identity或sequence】
			    如果是mysql数据库, 采用的自增长方式是identity
			    如果是oracle数据库, 使用sequence序列的方式实现自增长		
	increment  自增长(会有并发访问的问题,一般在服务器集群环境使用会存在问题。)
	assigned   指定主键生成策略为手动指定主键的值
	uuid      指定uuid随机生成的唯一的值
	foreign    外键的方式 

8. Hibernate核心API

  1. Configuration 配置管理类对象
    config.configure(); 加载主配置文件的方法(hibernate.cfg.xml)
    默认加载src/hibernate.cfg.xml
    config.configure(“cn/config/hibernate.cfg.xml”); 加载指定路径下指定名称的主配置 文件
    config.buildSessionFactory(); 创建session的工厂对象
  2. SessionFactory session的工厂
    sf.openSession(); 创建一个sesison对象
    sf.getCurrentSession(); 创建session或取出session对象
  3. session对象维护了一个连接(Connection), 代表了与数据库连接的会话。
    Hibernate最重要的对象: 只要用hibernate与数据库操作,都用到这个对象
    session.beginTransaction(); 开启一个事务; hibernate要求所有的与数据库的操作 必须有事务的环境,否则报错!
更新:
	session.save(obj);   保存一个对象
	session.update(emp);  更新一个对象
	session.saveOrUpdate(emp);  保存或者更新的方法:
  没有设置主键,执行保存;
  有设置主键,执行更新操作; 
  如果设置主键不存在报错!
	
	主键查询:
	session.get(Employee.class, 1);    主键查询
session.load(Employee.class, 1);   主键查询 (支持懒加载)

9. 工作流程

  1. 通过Configuration对象–读取并解析配置文件
  2. 读取并解析映射信息,创建SessionFactory对象
  3. 打开session
  4. 创建事务Transaction
  5. Query持久化操作,对对象进行CRUD操作
  6. Transaction提交事务
  7. 关闭session和SessionFactory对象
    在这里插入图片描述
细节推理

一个是以.cfg.xml结尾的文件.
文件内部其实就是一个由user,password,url,driver组成的一个连接库的基本信息。

< hibernate-configuration>
< session-factory>
< property name=“connection.username”>
< mapping resource=“Student.hbm.xml”/>

包含的是程序里面的configuration 的实例的信息。从 mapping

里得到对应的表的信息和类的信息.

SessionFactory 所得到的一个 SessionFactory 对象,这个对象可以理解为一个 statement ,我们对数据库的所有操作都是通过它的一系列方法来实现的。

一个是以.hbm.xml结尾的文件。

这个文件是对数据库的表的映射文件,我们用这个文件指出哪个类对

应着哪个表,而且还指出哪个类中的属性对应着表中的哪个字段。文件的内容是这样的:

<xml version="1.0"?PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN"   
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">   
<hibernate-mapping>  
 <class name="src.Student" table="student">  
  <id name="id" column="id">  
   <generator class="increment"/>  
  </id>  
  <property name="name" column="name"/>  
 </class>  
</hibernate-mapping>

注意: 类如果带包的话一定要带包名(推荐一切写的类都要带包).包名和类名要注意.包名小写,类名的第一个大写。

在这里插入图片描述

二、 映射关系

1. 集合映射

public class User {
	private int userId;
	private String userName;
	//分别对set,list,map进行测试
	// 一个用户,对应的多个地址
	private Set<String> address;
	private List<String> addressList = new ArrayList<String>(); 
	private Map<String,String> addressMap = new HashMap<String, String>();

//get,set方法省略

hibernate.cfg.xml 没变化
User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.a_collection">
	
	<class name="User" table="t_user">
		<id name="userId" column="id">
			<generator class="native"></generator>
		</id>	
		<property name="userName"></property>
		<!-- 
			set集合属性的映射
				name 指定要映射的set集合的属性
				table 集合属性要映射到的表
				key  指定集合表(t_address)的外键字段
				element 指定集合表的其他字段
				type 元素类型,一定要指定
		 -->
		 <set name="address" table="t_address">
		 	<key column="uid"></key>
		 	<element column="address" type="string"></element>
		 </set>
		 
		 <!-- 
		 	list集合映射
		 list-index  指定的是排序列的名称 (因为要保证list集合的有序)
		  -->
		  <list name="addressList" table="t_addressList">
		  	  <key column="uid"></key>
		  	  <list-index column="idx"></list-index>
		  	  <element column="address" type="string"></element>
		  </list>
		  
		  <!-- 
		  	map集合的映射
		  		key  指定外键字段
		  		map-key 指定map的key 
		  		element  指定map的value
		   -->
		  <map name="addressMap" table="t_addressMap">
		  	<key column="uid"></key>
		  	<map-key column="shortName" type="string" ></map-key>
		  	<element column="address" type="string" ></element>
		  </map>  
	</class>
</hibernate-mapping>

测试

App.java

package cn.atcast.a_collection;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;

public class App {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(User.class)   // 测试时候使用
			.buildSessionFactory();
	}

	// 保存set
	@Test
	public void testSaveSet() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		//保存
		Set<String> addressSet = new HashSet<String>();
		addressSet.add("广州");
		addressSet.add("深圳");
		// 用户对象
		User user = new User();
		user.setUserName("Jack");
		user.setAddress(addressSet);
		// 保存
		session.save(user);
		session.getTransaction().commit();
		session.close();
	}
	
	// 保存list
	@Test
	public void testSaveList() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		User user = new User();
		user.setUserName("Tom");
 		// 用户对象  --  list
 		user.getAddressList().add("广州");
 		user.getAddressList().add("深圳");
 		// 保存
 		session.save(user);
		session.getTransaction().commit();
		session.close();
	}
	// 保存map
	@Test
	public void testSaveMap() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		User user = new User();
		user.setUserName("Tom");
		// 用户对象  --  Map
		user.getAddressMap().put("A0001", "广州");
		user.getAddressMap().put("A0002", "深圳");
		// 保存
		session.save(user);
		session.getTransaction().commit();
		session.close();
	}
	
	// 获取
	@Test
	public void testGet() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		
		// 获取
		User user = (User) session.get(User.class, 3); // 及时加载
		System.out.println(user.getUserId());
		System.out.println(user.getUserName());
		
// 当查询用户,同时可以获取用户关联的list集合的数据 (因为有正确映射)
	// 当使用到集合数据的使用,才向数据库发送执行的sql语句  (懒加载)
		System.out.println(user.getAddressList());
		
		session.getTransaction().commit();
		session.close();
	}
}

在这里插入图片描述

2. 关联映射

1) 一对多和多对一(部门与员工)

一个部门有多个员工; (一对多)
多个员工,属于一个部门 (多对一)

在一对多与多对一的关联关系中,保存数据最好通过多的一方来维护关系,这样 可以减少update语句的生成,从而提高hibernate的执行效率!

Dept.java部门类

package cn.atcast.b_one2Many;
import java.util.HashSet;
import java.util.Set;
public class Dept {
	private int deptId;
	private String deptName;
	// 【一对多】 部门对应的多个员工
	private Set< Employee> emps = new HashSet< Employee>();
.....省略get set
}

Employee.java 员工

package cn.atcast.b_one2Many;
public class Employee {
	private int empId;
	private String empName;
	private double salary;
	// 【多对一】员工与部门
	private Dept dept;
,,,,,,,省略 
}

重点—配置

Dept.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.b_one2Many">
	<class name="Dept" table="t_dept">
		<id name="deptId">
			<generator class="native"></generator>
		</id>	
		<property name="deptName" length="20"></property>
		<!-- 
			一对多关联映射配置  (通过部门管理到员工)
			Dept 映射关键点:
			1.  指定 映射的集合属性: "emps"
			2.  集合属性对应的集合表: "t_employee"
			3.  集合表的外键字段   "t_employee. dept_id"
			4.  集合元素的类型
			
			inverse=false  set集合映射的默认值; 表示有控制权
		 -->
		 <!-- 只能在一的一方设置。如果设置控制反转,即inverse=true, 员工方不会维护关联关系,
		 通过部门方维护关联关系。但在App1_save的save()方法中,先保存员工,后保存部门,此时员工没有部门的编号,员工表中部门值为null,
		 并且维护方为部门,员工方就不会再执行update语句,因为员工方是不需要维护的。而将inverser=false,则部门方不维护,将维护交给员工方,
		 所以员工方会执行update语句 -->
		 <set name="emps" cascade="save-update,delete" table="t_employee" inverse="true">   
		 	 <key column="dept_id"></key>
		 	 <one-to-many class="Employee"/>
		 </set>
	</class>
</hibernate-mapping>
Employee.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.b_one2Many">
	<class name="Employee" table="t_employee">
		<id name="empId">
			<generator class="native"></generator>
		</id>	
		<property name="empName" length="20"></property>
		<property name="salary" type="double"></property>
		<!-- 
			多对一映射配置
			Employee 映射关键点:
			1.  映射的部门属性  :  dept
			2.  映射的部门属性,对应的外键字段: dept_id
			3.  部门的类型
		 -->
		<many-to-one name="dept" column="dept_id"  class="Dept" ></many-to-one>
	</class>
</hibernate-mapping>

测试
App1_save.java

package cn.atcast.b_one2Many;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.atcast.a_collection.User;
public class App1_save {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)   // 测试时候使用
			.buildSessionFactory();
	}
	// 保存, 部门方 【一的一方法操作】
	@Test
	public void save() {
		Session session = sf.openSession();
		session.beginTransaction();
		// 部门对象
		Dept dept = new Dept();
		dept.setDeptName("应用开发部");
		// 员工对象
		Employee emp_zs = new Employee();
		emp_zs.setEmpName("张三");
		Employee emp_ls = new Employee();
		emp_ls.setEmpName("李四");
		// 关系
		dept.getEmps().add(emp_zs);
		dept.getEmps().add(emp_ls);

		// 保存
		session.save(emp_zs);
		session.save(emp_ls);
		session.save(dept); // 保存部门,部门下所有的员工  
		
		session.getTransaction().commit();
		session.close();
		/*  在Dept.hbm.xml中设置inverse="true"
		 *  结果
		 *  Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
			Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
			Hibernate: insert into t_dept (deptName) values (?)
			Hibernate: update t_employee set deptId=? where empId=?    维护员工引用的部门的id
			Hibernate: update t_employee set deptId=? where empId=?
		 */
	}
	// 【推荐】 保存, 部门方 【多的一方法操作】
	@Test
	public void save2() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		// 部门对象
		Dept dept = new Dept();
		dept.setDeptName("综合部");
		// 员工对象
		Employee emp_zs = new Employee();
		emp_zs.setEmpName("张三");
		Employee emp_ls = new Employee();
		emp_ls.setEmpName("李四");
		// 关系
		emp_zs.setDept(dept);
		emp_ls.setDept(dept);
		// 保存
		session.save(dept); // 先保存一的方法
		session.save(emp_zs);
session.save(emp_ls);// 再保存多的一方,关系回自动维护(映射配置完)
		
		session.getTransaction().commit();
		session.close();
		/*
		 *  结果
		 *  Hibernate: insert into t_dept (deptName) values (?)
			Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
			Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
			少生成2条update  sql
			也可以设置inverse="true"
		 */
	}
}

App2_get.java

**package cn.atcast.b_one2Many;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.atcast.a_collection.User;
public class App2_get {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)   // 测试时候使用
			.buildSessionFactory();
	}
	@Test
	public void get() {
		Session session = sf.openSession();
		session.beginTransaction();
		// 通过部门方,获取另外一方
		Dept dept = (Dept) session.get(Dept.class, 1);
		System.out.println(dept.getDeptName());
		System.out.println(dept.getEmps());// 懒加载
		// 通过员工方,获取另外一方
		Employee emp = (Employee) session.get(Employee.class, 1);
		System.out.println(emp.getEmpName());
		System.out.println(emp.getDept().getDeptName());
		
		session.getTransaction().commit();
		session.close();
	}
}
	inverse属性,是在维护关联关系的时候起作用的。
	表示控制权是否转移。(在一的一方起作用)
	inverse = false  不反转;   当前方有控制权
	true  控制反转; 当前方没有控制权

App3_inverse.java

package cn.atcast.b_one2Many;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.atcast.a_collection.User;

public class App3_inverse {
	
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)   // 测试时候使用
			.buildSessionFactory();
	}
	// 1. 保存数据 
	@Test
	public void save() {
		Session session = sf.openSession();
		session.beginTransaction();
		// 部门对象
		Dept dept = new Dept();
		dept.setDeptName("应用开发部");
		// 员工对象
		Employee emp_zs = new Employee();
		emp_zs.setEmpName("张三");
		Employee emp_ls = new Employee();
		emp_ls.setEmpName("李四");
		
		//关系
//如果此时设置inverse=true, 员工方不会维护关联关系,通过部门方维护。
		dept.getEmps().add(emp_zs);
		dept.getEmps().add(emp_ls);  
		
		// 保存
		session.save(emp_zs);
		session.save(emp_ls);
		session.save(dept); // 保存部门,部门下所有的员工  
		
		session.getTransaction().commit();
		session.close();
	}
	
	//2. 是否设置inverse,对获取数据的影响?   无.
	@Test
	public void get() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		Dept dept = (Dept) session.get(Dept.class, 1);
		System.out.println(dept.getDeptName());
		System.out.println(dept.getEmps());
		
		session.getTransaction().commit();
		session.close();
	}
	
	//3. 是否设置inverse属性,在删除数据中对关联关系的影响?
	// inverse=false, 有控制权, 可以删除。先清空外键引用,再删除数据。
	// inverse=true,  没有控制权: 如果删除的记录有被外键引用,会报错,违反主外键引用约束!
	//如果删除的记录没有被引用,可以直接删除。
	@Test
	public void deleteData() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		// 查询部门
		//inverse=true,  没有控制权: 如果删除的记录有被外键引用,会报错,违反主外键引用约束!  但可以设置cascade="delete"
		Dept dept = (Dept) session.get(Dept.class, 5);
		session.delete(dept);
		
		session.getTransaction().commit();
		session.close();
	}
}
	cascade  表示级联操作  (可以设置到一的一方或多的一方)
	none           不级联操作, 默认值
	save-update     级联保存或更新
	delete		   级联删除
	save-update,delete    级联保存、更新、删除
	all                 同上。级联保存、更新、删除

App4_cascade.java

package cn.atcast.b_one2Many;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.atcast.a_collection.User;

public class App4_cascade {
	
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)   // 测试时候使用
			.buildSessionFactory();
	}
	// 级联保存
	@Test
	public void save() {
		Session session = sf.openSession();
		session.beginTransaction();
		// 部门对象
		Dept dept = new Dept();
		dept.setDeptName("财务部");
		// 员工对象
		Employee emp_zs = new Employee();
		emp_zs.setEmpName("张三");
		Employee emp_ls = new Employee();
		emp_ls.setEmpName("李四");
		// 关系
		dept.getEmps().add(emp_zs);
		dept.getEmps().add(emp_ls);  
		
		// 保存
//		session.save(emp_zs);
//		session.save(emp_ls);
		session.save(dept); // 需要设置级联保存; 保存部门,部门下所有的员工  
		session.getTransaction().commit();
		session.close();
	}

	// 级联删除
	@Test
	public void delete() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		Dept dept = (Dept) session.get(Dept.class,7);
		session.delete(dept); // 级联删除
		
		session.getTransaction().commit();
		session.close();
	}
}

在这里插入图片描述

多对多

项目与开发人员(多对多)
一个项目,有多个开发人员!
一个开发人员,参与多个项目!

Project.java 项目

public class Project {
	private int prj_id;
	private String prj_name;
	// 项目下的多个员工
	private Set<Developer> developers = new HashSet<Developer>();
	.....省略

Developer.java

/**

  • 开发人员
    */
public class Developer {
	private int d_id;
	private String d_name;
	// 开发人员,参与的多个项目
	private Set<Project> projects = new HashSet<Project>();

重点 配置文件

**Project.hbm.xml

<?xml version="1.0"?> Developer.hbm.xml <?xml version="1.0"?>
<class name="Developer" table="t_developer">
	<id name="d_id">
		<generator class="native"></generator>
	</id>	
	<property name="d_name" length="20"></property>
	<!-- 
		多对多映射配置: 员工方
			name  指定映射的集合属性
			table 集合属性对应的中间表
key  指定中间表的外键字段(引用当前表t_developer主键的外键字段)
			many-to-many
				column 指定外键字段对应的项目字段
				class  集合元素的类型
	 -->
	<set name="projects" table="t_relation">
		<key column="did"></key>
		<many-to-many column="prjId" class="Project"></many-to-many>
	</set>
</class>

测试

App1_save.java
package cn.atcast.c_many2many;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.atcast.a_collection.User;

public class App1_save {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Project.class)   
			.addClass(Developer.class)   // 测试时候使用
			.buildSessionFactory();
	}
	// 1. 多对多,保存  【只能通过一方维护另外一方,不能重复维护!】
	@Test
	public void save() {
		Session session = sf.openSession();
		session.beginTransaction();
		/*
		 * 模拟数据: 
			电商系统(曹吉,王春)
			OA系统(王春,老张)
		 */
		// 创建项目对象
		Project prj_ds = new Project();
		prj_ds.setPrj_name("电商系统");
		Project prj_oa = new Project();
		prj_oa.setPrj_name("OA系统");
		
		// 创建员工对象
		Developer dev_cj = new Developer();
		dev_cj.setD_name("曹吉");
		Developer dev_wc = new Developer();
		dev_wc.setD_name("王春");
		Developer dev_lz = new Developer();
		dev_lz.setD_name("老张");
		
		// 关系 【项目方】通过项目维护员工
		prj_ds.getDevelopers().add(dev_cj);
		prj_ds.getDevelopers().add(dev_wc); // 电商系统(曹吉,王春)
		prj_oa.getDevelopers().add(dev_wc);
		prj_oa.getDevelopers().add(dev_lz); // OA系统(王春,老张)
		
		//通过员工维护项目,不能重复维护!因为通过项目维护员工时会在中间表中插入一条记录,而再通过员工去维护项目,相当于在中间表中又插入一条记录。
		//dev_cj.getProjects().add(prj_ds);
		//方法一:直接保存员工和项目
		/*
		// 保存
		session.save(dev_cj);
		session.save(dev_wc);
		session.save(dev_lz);
	
		session.save(prj_ds);
		session.save(prj_oa);   // 必须要设置级联保存 
		*/
		
		//方法二:使用级联操作
		session.save(prj_ds);
		session.save(prj_oa);  
		
		session.getTransaction().commit();
		session.close();
	}
}
App2_inverse.java
package cn.atcast.c_many2many;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.atcast.a_collection.User;

public class App2_inverse {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Project.class)   
			.addClass(Developer.class)   // 测试时候使用
			.buildSessionFactory();
	}
	// 多对多
	//1. 设置inverse属性,对保存数据影响?
	// 有影响。
	// inverse=false ,有控制权,可以维护关联关系; 保存数据的时候会把对象关系插入中间表;
	// inverse=true,  没有控制权, 不会往中间表插入数据。
	@Test
	public void save() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		/*
		 * 模拟数据: 
			电商系统(曹吉,王春)
			OA系统(王春,老张)
		 */
		// 创建项目对象
		Project prj_ds = new Project();
		prj_ds.setPrj_name("电商系统");
		Project prj_oa = new Project();
		prj_oa.setPrj_name("OA系统");
		
		// 创建员工对象
		Developer dev_cj = new Developer();
		dev_cj.setD_name("曹吉");
		Developer dev_wc = new Developer();
		dev_wc.setD_name("王春");
		Developer dev_lz = new Developer();
		dev_lz.setD_name("老张");
		// 关系 【项目方】
		prj_ds.getDevelopers().add(dev_cj);
		prj_ds.getDevelopers().add(dev_wc); // 电商系统(曹吉,王春)
		prj_oa.getDevelopers().add(dev_wc);
		prj_oa.getDevelopers().add(dev_lz); // OA系统(王春,老张)
		
		// 保存
//		session.save(dev_cj);
//		session.save(dev_wc);
//		session.save(dev_lz);
		
		session.save(prj_ds);
		session.save(prj_oa);   // 必须要设置级联保存 
		
		session.getTransaction().commit();
		session.close();
	}
	
	//2 .设置inverse属性, 对获取数据影响?  无
	@Test
	public void get() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		Project prj = (Project) session.get(Project.class, 1);
		System.out.println(prj.getPrj_name());
		System.out.println(prj.getDevelopers());
		
		session.getTransaction().commit();
		session.close();
	}
	//3. 设置inverse属性,对删除数据的影响?
	// inverse=false, 有控制权。 先删除中间表数据,再删除自身。
	// inverse=true, 没有控制权。 如果删除的数据有被引用,会报错! 否则,才可以删除
	@Test
	public void deleteData() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		Project prj = (Project) session.get(Project.class, 1);
		session.delete(prj);
		
		session.getTransaction().commit();
		session.close();
	}
}

在这里插入图片描述

3) 一对一

需求: 用户与身份证信息
一条用户记录对应一条身份证信息(一对一的关系)
基于外键的映射

// 用户
public class User {
	private int userId;
	private String userName;
	// 用户与身份证信息, 一对一关系
	private IdCard idCard;
// 身份证
public class IdCard {
	// 身份证号(主键)
	private String cardNum;// 对象唯一表示(Object Identified, OID)
	private String place; //  身份证地址
	// 身份证与用户,一对一的关系
	private User user;

User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.d_one2oneForeign">
	<class name="User" table="t_user">
		<id name="userId">
			<generator class="native"></generator>
		</id>	
		<property name="userName" length="20"></property>
		<!-- 
			一对一映射: 没有外键方
		 -->
		 <one-to-one name="idCard" class="IdCard"></one-to-one>
	</class>
</hibernate-mapping>
IdCard.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.atcast.d_one2oneForeign">
	<class name="IdCard" table="t_IdCard">
		<id name="cardNum">
			<generator class="assigned"></generator>
		</id>	
		<property name="place" length="20"></property>
		<!-- 
			一对一映射,有外键方
			unique="true"   给外键字段添加唯一约束
		 -->
		 <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>	
	</class>
</hibernate-mapping>

App.java

package cn.atcast.d_one2oneForeign;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
public class App {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(IdCard.class)   
			.addClass(User.class)   // 测试时候使用
			.buildSessionFactory();
	}
	@Test
	public void getSave() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		// 用户
		User user = new User();
		user.setUserName("Jack");
		// 身份证
		IdCard idCard = new IdCard();
		idCard.setCardNum("441202XXX");
		idCard.setPlace("广州XXX");
		// 关系
		idCard.setUser(user);
		
		// ----保存----
		session.save(idCard);

		session.getTransaction().commit();
		session.close();
	}
}
基于主键的映射

User.java

package cn.atcast.d_one2onePrimary;
// 用户
public class User {
	private int userId;
	private String userName;
	// 用户与身份证信息, 一对一关系
	private IdCard idCard;
	
	public IdCard getIdCard() {
		return idCard;
	}
	public void setIdCard(IdCard idCard) {
		this.idCard = idCard;
	}
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
}

IdCard.java

package cn.atcast.d_one2onePrimary;
// 身份证
public class IdCard {
	private int user_id;
	// 身份证号
	private String cardNum;
	private String place; //  身份证地址
	// 身份证与用户,一对一的关系
	private User user;
	
	public int getUser_id() {
		return user_id;
	}
	public void setUser_id(int userId) {
		user_id = userId;
	}
	public String getCardNum() {
		return cardNum;
	}
	public void setCardNum(String cardNum) {
		this.cardNum = cardNum;
	}
	public String getPlace() {
		return place;
	}
	public void setPlace(String place) {
		this.place = place;
	}
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
}

User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.d_one2onePrimary">
	<class name="User" table="t_user">
		<id name="userId">
			<generator class="native"></generator>
		</id>	
		<property name="userName" length="20"></property>
		<!-- 
			一对一映射: 没有外键方
		 -->
		 <one-to-one name="idCard" class="IdCard"></one-to-one>
	</class>
</hibernate-mapping>
IdCard.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.d_one2onePrimary">
	<class name="IdCard" table="t_IdCard">
		<id name="user_id">
			<!-- 
				id 节点指定的是主键映射, 即user_id是主键
		主键生成方式: foreign  即把别的表的主键作为当前表的主键;
						property (关键字不能修改)指定引用的对象     对象的全名 cn..User、  对象映射 cn.User.hbm.xml、   table(id)
			 -->
			<generator class="foreign">
				<param name="property">user</param>
			</generator>
		</id>	
		<property name="cardNum" length="20"></property>
		<property name="place" length="20"></property>
		<!-- 
			一对一映射,有外键方
			(基于主键的映射)
			 constrained="true"  指定在主键上添加外键约束
		 -->
		<one-to-one name="user" class="User" constrained="true"  cascade="save-update"></one-to-one>	
	</class>
</hibernate-mapping>

App.java

package cn.atcast.d_one2onePrimary;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;
public class App {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(IdCard.class)   
			.addClass(User.class)   // 测试时候使用
			.buildSessionFactory();
	}

	@Test
	public void getSave() {
		
		Session session = sf.openSession();
		session.beginTransaction();
		
		// 用户
		User user = new User();
		user.setUserName("Jack");
		// 身份证
		IdCard idCard = new IdCard();
		idCard.setCardNum("441202XXX");
		idCard.setPlace("广州XXX");
		// 关系
		idCard.setUser(user);
		
		// ----保存----
		session.save(idCard);
		
		session.getTransaction().commit();
		session.close();
	}
}

在这里插入图片描述

在这里插入图片描述

1) 对象状态

Hibernate中对象的状态: 临时/瞬时状态、持久化状态、游离状态。

临时状态

特点:
直接new出来的对象;
不处于session的管理;
数据库中没有对象的记录;

持久化状态

当调用session的save/saveOrUpdate/get/load/list等方法的时候,对象就是持久化状态。
处于持久化状态的对象,当对对象属性进行更改的时候,会反映到数据库中!
特点:
处于session的管理;
数据库中有对应的记录;

游离状态

特点
不处于session的管理;
数据库中有对应的记录
Session关闭后,对象的状态;

package cn.atcast.a_status;

import java.util.HashSet;
import java.util.Set;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;

public class App1_status {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(User.class)   // 测试时候使用
			.buildSessionFactory();
	}

	//1. 对象状态的转换
	@Test
	public void testSaveSet() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		
		//创建对象	 【临时状态】
		User user = new User();
		user.setUserName("Jack22222");
		//保存	【持久化状态】
		session.save(user);		
		user.setUserName("Jack333333");  // 会反映到数据库
		
		// 查询
		//当调用session的save/saveOrUpdate/get/load/list/iterator方法的时候,都会把对象放入session的缓存中
//		User user1 = (User) session.get(User.class, 1);
//		user1.setUserName("Tomcat");// hibernate会自动与数据库匹配(一级缓存),如果一样就不更新数据库
		
		session.getTransaction().commit();
		session.close();	
		
		user.setUserName("Jack444444444");
		// 打印							【游离状态】
		System.out.println(user.getUserId());
		System.out.println(user.getUserName());
	}
}
App2_cache.java
package cn.atcast.a_status;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;

public class App2_cache {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(User.class)   // 测试时候使用
			.buildSessionFactory();
	}

	@Test
	public void testCache() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		User user = null;
		
		// 查询 (先要执行testSaveSet()方法,在数据库中增加一条数据)
	//先执行一次,将数据放在user对象后,放在一级缓存中。会有查询的语句
		user = (User) session.get(User.class, 1);
		//先检查缓存中是否有数据,如果有不查询数据库,直接从缓存中获取。再次进行查询,不会发出sql语句
		user = (User) session.get(User.class, 1);
		
		session.getTransaction().commit();
		session.close();
	}
	
	@Test
	public void flush() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		
		User user = null;
		user = (User) session.get(User.class, 1);
		 
		//会执行一条更新语句
//		user.setUserName("Jack");
//		user.setUserName("Jack_new");
		 
		
		//会执行两条更新语句
		user.setUserName("Jack");
		// 缓存数据与数据库同步
		session.flush();
		user.setUserName("Jack_new");
		
		session.getTransaction().commit();  // session.flush();
		session.close();
	}
	
	@Test
	public void clear() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		
		User user = null;
		// 查询
		user = (User) session.get(User.class, 1);
		// 清空缓存内容 
 		//session.clear(); // 清空所有
		 session.evict(user);// 清除指定
		//会再次发出select语句查询数据库
		user = (User) session.get(User.class, 1);
	
		session.getTransaction().commit();  // session.flush();
		session.close();
	}
	
	//不同的session是否会共享缓存数据?
	@Test
	public void sessionTest() throws Exception {
		Session session1 = sf.openSession();
		session1.beginTransaction();
		Session session2 = sf.openSession();
		session2.beginTransaction();
		
		// user放入session1的缓存区
		User user = (User) session1.get(User.class, 1);
		// user放入session2的缓存区,并没有从缓存session1中获取user对象,而是再次发出update语句
		User user2 = (User) session2.get(User.class, 1);
		
		session1.getTransaction().commit();  // session1.flush();
		session1.close();
		session2.getTransaction().commit();  // session2.flush();
		session2.close();
	}
}
App3_list_iterator.java
package cn.atcast.a_status;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;

public class App3_list_iterator {
	
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(User.class)   // 测试时候使用
			.buildSessionFactory();
	}
	/**
	 * list与iterator区别
	 * 1. list 方法
	 * 2. iterator 方法
	 * 3. 缓存
	 * @throws Exception
	 */
	//1.  list 方法  
	/*	一次把所有的记录都查询出来,
		会放入缓存,但不会从缓存中获取数据
	    Hibernate: select user0_.id as id0_, user0_.userName as userName0_ from t_user user0_
		User [userId=1, userName=New Name]
		User [userId=2, userName=tom]
		User [userId=3, userName=jerry]
	 */
	@Test
	public void list() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		// HQL查询
		Query q = session.createQuery("from User ");
		// list()方法
		List<User> list = q.list();
		
		for (int i=0; i<list.size(); i++){
			System.out.println(list.get(i));
		}
		
		session.getTransaction().commit();  
		session.close();
	}
	
	//2. iterator 方法
	/*
	  	N+1查询; N表示所有的记录总数
		即会先发送一条语句查询所有记录的主键(1),
		再根据每一个主键再去数据库查询(N)!
		会放入缓存,也会从缓存中取数据!
	    Hibernate: select user0_.id as col_0_0_ from t_user user0_
		Hibernate: select user0_.id as id0_0_, user0_.userName as userName0_0_ from t_user user0_ where user0_.id=?
		User [userId=1, userName=New Name]
		Hibernate: select user0_.id as id0_0_, user0_.userName as userName0_0_ from t_user user0_ where user0_.id=?
		User [userId=2, userName=tom]
		Hibernate: select user0_.id as id0_0_, user0_.userName as userName0_0_ from t_user user0_ where user0_.id=?
		User [userId=3, userName=jerry]
	 */
	@Test
	public void iterator() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		// HQL查询
		Query q = session.createQuery("from User ");
		// iterator()方法
		Iterator<User> it = q.iterate();
		while(it.hasNext()){
			// 得到当前迭代的每一个对象
			User user = it.next();
			System.out.println(user);
		}
		
		session.getTransaction().commit();  
		session.close();
	}
	
	
	//3. 缓存
	@Test
	public void cache() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		/*
		 * Hibernate: select user0_.id as id0_, user0_.userName as userName0_ from t_user user0_
			User [userId=1, userName=New Name]
			User [userId=2, userName=tom]
			=========list===========
			Hibernate: select user0_.id as id0_, user0_.userName as userName0_ from t_user user0_
			User [userId=1, userName=New Name]
			User [userId=2, userName=tom]
		 */
		/**************执行2次list****************
		Query q = session.createQuery("from User");
		List<User> list = q.list();      // 【会放入?】
		for (int i=0; i<list.size(); i++){
			System.out.println(list.get(i));
		}
		System.out.println("=========list===========");
		list = q.list();				// 【会放入?】
		for (int i=0; i<list.size(); i++){
			System.out.println(list.get(i));
		}
		*/
		 
		/**************执行2次iteator*****************/
		/*
		 * 	Hibernate: select user0_.id as col_0_0_ from t_user user0_
			Hibernate: select user0_.id as id0_0_, user0_.userName as userName0_0_ from t_user user0_ where user0_.id=?
			User [userId=1, userName=New Name]
			Hibernate: select user0_.id as id0_0_, user0_.userName as userName0_0_ from t_user user0_ where user0_.id=?
			User [userId=2, userName=tom]
			Hibernate: select user0_.id as id0_0_, user0_.userName as userName0_0_ from t_user user0_ where user0_.id=?
			User [userId=3, userName=jerry]
			==========iterate===========
			Hibernate: select user0_.id as col_0_0_ from t_user user0_
			User [userId=1, userName=New Name]
			User [userId=2, userName=tom]
			User [userId=3, userName=jerry]
		 */
		Query q = session.createQuery("from User ");
		Iterator<User> it = q.iterate();		// 【放入缓存】
		while(it.hasNext()){
			User user = it.next();
			System.out.println(user);
		}
		System.out.println("==========iterate===========");
		it = q.iterate();						// 【也会从缓存中取】
		while(it.hasNext()){
			User user = it.next();
			System.out.println(user);
		}
		 
		session.getTransaction().commit();  
		session.close();
	}
	
	// 测试list方法会放入缓存,但不会从缓存中获取数据
	//先用list查询,再用Iterator查询,如果Iterator直接从缓存中取数据,说明list方法将数据库中读取的数据放入了缓存。
	/*
	 * 	Hibernate: select user0_.id as id0_, user0_.userName as userName0_ from t_user user0_
		User [userId=1, userName=New Name]
		User [userId=2, userName=tom]
		User [userId=3, userName=jerry]
		Hibernate: select user0_.id as col_0_0_ from t_user user0_
		User [userId=1, userName=New Name]  //直接从缓存中取数据,不再执行select语句
		User [userId=2, userName=tom]
		User [userId=3, userName=jerry]
	 */
	@Test
	public void list_iterator() throws Exception {
		Session session = sf.openSession();
		session.beginTransaction();
		
		// 得到Query接口的引用
		Query q = session.createQuery("from User ");
		
		// 先list  【会放入缓存,但不会从缓存中获取数据】
		List<User> list = q.list(); 
		for (int i=0; i<list.size(); i++){
			System.out.println(list.get(i));
		}
		
		// 再iteraotr  (会从缓存中取)
		Iterator<User> it = q.iterate();
		while(it.hasNext()){
			User user = it.next();
			System.out.println(user);
		}
		
		session.getTransaction().commit();  
		session.close();
	}
}

2) lazy懒加载

get与load方法区别
get: 及时加载,只要调用get方法立刻向数据库查询
load:默认使用懒加载,当用到数据的时候才向数据库查询。当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象。

懒加载:(lazy)

概念:当用到数据的时候才向数据库查询,这就是hibernate的懒加载特性。
目的:提供程序执行效率!

lazy 值

true   使用懒加载
false   关闭懒加载
extra   (在集合数据懒加载时候提升效率)

在真正使用数据的时候才向数据库发送查询的sql;
如果调用集合的size()/isEmpty()方法,只是统计,不真正查询数据!

3) 查询

1) HQL查询

Dept.java
package cn.atcast.b_query;

import java.util.HashSet;
import java.util.Set;
public class Dept {
	private int deptId;
	private String deptName;
	// 【一对多】 部门对应的多个员工
	private Set<Employee> emps = new HashSet<Employee>();
	
	public Dept(int deptId, String deptName) {
		super();
		this.deptId = deptId;
		this.deptName = deptName;
	}
	public Dept() {
		super();
	}
	public int getDeptId() {
		return deptId;
	}
	public void setDeptId(int deptId) {
		this.deptId = deptId;
	}
	public String getDeptName() {
		return deptName;
	}
	public void setDeptName(String deptName) {
		this.deptName = deptName;
	}
	public Set<Employee> getEmps() {
		return emps;
	}
	public void setEmps(Set<Employee> emps) {
		this.emps = emps;
	}
}
Employee.java
package cn.atcast.b_query;
public class Employee {
	private int empId;
	private String empName;
	private double salary;
	// 【多对一】员工与部门
	private Dept dept;
	
	public int getEmpId() {
		return empId;
	}
	public void setEmpId(int empId) {
		this.empId = empId;
	}
	public String getEmpName() {
		return empName;
	}
	public void setEmpName(String empName) {
		this.empName = empName;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	public Dept getDept() {
		return dept;
	}
	public void setDept(Dept dept) {
		this.dept = dept;
	}
}
Dept.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.b_query" auto-import="true">
	<class name="Dept" table="t_dept" >
		<id name="deptId">
			<generator class="native"></generator>
		</id>	
		<property name="deptName" length="20"></property>
		 <set name="emps">
		 	 <key column="dept_id"></key>
		 	 <one-to-many class="Employee"/>
		 </set>
	</class>
	<!-- HQL查询优化 -->
	<!-- 存放sql语句 	-->
	<query name="getAllDept">
		<![CDATA[
			from Dept d where deptId < ?
		]]>
	</query>
</hibernate-mapping>
Employee.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.atcast.b_query">
	<class name="Employee" table="t_employee">
		<id name="empId">
			<generator class="native"></generator>
		</id>	
		<property name="empName" length="20"></property>
		<property name="salary" type="double"></property>
		
		<many-to-one name="dept" column="dept_id" class="Dept"></many-to-one> 
	</class>
</hibernate-mapping>
testData.java
package cn.atcast.b_query;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;

public class testData {
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)   // 测试时候使用
			.buildSessionFactory();
	}
	// 【推荐】 保存, 部门方 【多的一方法操作】
	@Test
	public void save() {
		
		Session session = sf.openSession();
		session.beginTransaction();
		
		// 部门对象
		Dept dept = new Dept();
		dept.setDeptName("综合部");
		// 员工对象
		Employee emp_zs = new Employee();
		emp_zs.setEmpName("张三");
		Employee emp_ls = new Employee();
		emp_ls.setEmpName("李四");
		
		// 关系
		emp_zs.setDept(dept);
		emp_ls.setDept(dept);
		
		
		// 保存
		session.save(dept); // 先保存一的方法
		session.save(emp_zs);
		session.save(emp_ls);// 再保存多的一方,关系回自动维护(映射配置完)
		
		session.getTransaction().commit();
		session.close();
	}
}
App_hql.java
package cn.atcast.b_query;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.junit.Test;

public class App_hql {
	
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)   // 测试时候使用
			.buildSessionFactory();
	}

	/* 
	 * 1)	Get/load主键查询
		2)	对象导航查询
		3)	HQL查询,  Hibernate Query language  hibernate 提供的面向对象的查询语言。
		4)	Criteria 查询,   完全面向对象的查询(Query By Criteria  ,QBC)
		5)	SQLQuery, 本地SQL查询
	 */


	@Test
	public void all() {
		Session session = sf.openSession();
		session.beginTransaction();
		//1) 主键查询 (此时不能在hibernate.cfg.xml中开启二级缓存的配置,因为开启后会先在二级缓存中找,而不会去数据库中取)
		//Dept dept =  (Dept) session.get(Dept.class, 1);
		//Dept dept =  (Dept) session.load(Dept.class, 1);
		
		//2) 对象导航查询
//		Dept dept =  (Dept) session.get(Dept.class, 1);
//		System.out.println(dept.getDeptName());
//		System.out.println(dept.getEmps());
		
		// 3)	HQL查询
		// 注意:使用hql查询的时候 auto-import="true" 要设置true,默认为true
		//  如果是false,写hql的时候,要指定类的全名
//		Query q = session.createQuery("from Dept");
//		System.out.println(q.list());
		
		// a. 查询全部列
		//Query q = session.createQuery("from Dept");  //OK
		//Query q = session.createQuery("select * from Dept");  //NOK, 错误,不支持*
//		Query q = session.createQuery("select d from Dept d");  // OK d是Dept的别名
//		System.out.println(q.list());

		// b. 查询指定的列  【返回对象数据Object[] 】
		//加入断点测试,可以选中q.list()后用watch,在Expressions面板中查看。
		Query q = session.createQuery("select d.deptId,d.deptName from Dept d");  
		System.out.println(q.list());
		
		// c. 查询指定的列, 自动封装为对象  【必须要提供带参数构造器】
//		Query q = session.createQuery("select new Dept(d.deptId,d.deptName) from Dept d");  
//		System.out.println(q.list());
		
		// d. 条件查询: 一个条件/多个条件and or/between and/模糊查询
		// 条件查询: 占位符
		//Query q = session.createQuery("from Dept d where deptName=?");
		//两种方法
		//q.setString(0, "综合部");
		//q.setParameter(0, "综合部");
		//System.out.println(q.list());
		
		// 条件查询: 命名参数
//		Query q = session.createQuery("from Dept d where deptId=:myId or deptName=:name");
//		q.setParameter("myId", 1);
//		q.setParameter("name", "综合部");
//		System.out.println(q.list());
		
		// 范围
//		Query q = session.createQuery("from Dept d where deptId between ? and ?");
//		q.setParameter(0, 1);
//		q.setParameter(1, 3);
//		System.out.println(q.list());
		
		// 模糊
//		Query q = session.createQuery("from Dept d where deptName like ?");
//		q.setString(0, "%部%");
//		System.out.println(q.list());
		

		// e. 聚合函数统计
		//select count(*) from t_Dept 统计总记录 会统计null
		//select count(1) from t_Dept 统计总记录,效率更高。会统计null
		//select count(deptName) from t_Dept 忽略null值,聚合函数统计都会忽略null值
// 		Query q = session.createQuery("select count(*) from Dept");
//		Long num = (Long) q.uniqueResult();
//		System.out.println(num);
		
		// f. 分组查询
		//-- 统计t_employee表中,每个部门的人数
		//数据库写法:SELECT dept_id,COUNT(*) FROM t_employee GROUP BY dept_id;
		// HQL写法
		//Query q = session.createQuery("select e.dept, count(*) from Employee e group by e.dept");
//		Query q = session.createQuery("select e.dept, count(*) from Employee e group by e.dept having count(*)>1");
//		//加入断点测试,可以选中q.list()后用watch,在Expressions面板中查看。
//		System.out.println(q.list());
		
//		session.getTransaction().commit();
//		session.close();
	}
	
	// g. 连接查询
	@Test
	public void join() {
		
		Session session = sf.openSession();
		session.beginTransaction();
		/*
		//1) 内连接   【映射已经配置好了关系,关联的时候,直接写对象的属性即可】
		/* 需求:显示员工名称,薪水,部门
		 * sql方法一:
		 *  select e.empName,e.salary,d.deptName from t_dept d, t_employee e 
			where d.deptId=e.dept_id;
			sql方法二:
			select e.empName,e.salary,d.deptName from t_dept d inner join t_employee e 
			on d.deptId=e.dept_id;
		 */
		/*
			Query q = session.createQuery("from Dept d inner join d.emps");
			q.list();
		//加入断点,watch-->数组中第一个元素是Dept,第二个元素是Employee
		List<Object[]> list=q.list();
		for(int i=0;i<list.size();i++){
			Object[] obj=list.get(0);
			Dept dep=(Dept)obj[0];
			System.out.println(dep.getDeptName());
		}
		 */
		//2) 左外连接(解除主外键约束条件,在部门表加一个没有员工的新部门,在员工表加一个没有部门的新员工进行测试)
		/* 需求:显示部门,以及部门下的员工,如果部门下没有员工用null表示 (始终显示左表t_dept信息,)
		 * select e.empName,e.salary,d.deptName from t_dept d left join t_employee e 
		   on d.deptId=e.dept_id;
		   select e.empName,e.salary,d.deptName from t_employee e left join t_dept d 
		   on d.deptId=e.dept_id;
		 */
		// 查询出来的每一项为 object[]数组,左表存在object[0]中,右表存在object[1]中
		Query q = session.createQuery("from Dept d left join d.emps"); //左表为dept部门表,将部门表中的信息全保留。
		//Query q = session.createQuery("from Employee e left join e.dept"); //左表为employee员工表,将员工表中的信息全保留。
		q.list();
		//3) 右外连接
		/* 始终显示right join后面表的数据
		 * select e.empName,e.salary,d.deptName from t_employee e  right join t_dept d 
			on d.deptId=e.dept_id;
		 */
//		Query q = session.createQuery("from Employee e right join e.dept");
//		q.list();
		
		session.getTransaction().commit();
		session.close();
	}
	/*
	 内连接和迫切内连接的区别:
		    * 内连接:发送就是内连接的语句,封装的时候将每条记录封装到一个Object[]数组中,最后得到一个List<Object[]>.
		    * 迫切内连接:发送的也是内连接的语句,在join后添加一个fetch关键字,Hibernate会将每条数据封装到对象中,最后List<Customer>. 需要去掉重复值.
	*/
	// g. 连接查询 - 迫切连接
	/* Employee.hbm.xml
	 * <many-to-one name="dept" column="dept_id" class="Dept" fetch="select/join"></many-to-one>
	 * fetch="select",另外发送一条select语句抓取当前对象关联实体或集合
	   fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合
		此时lazy会失效
	 */
	@Test
	public void fetch() {
		Session session = sf.openSession();
		session.beginTransaction();
		//1)fetch抓取策略
//		Employee employee = (Employee)session.load(Employee.class, 1);
//		System.out.println("Employee.empName=" + employee.getEmpName());
		//另外发送一条select语句抓取当前对象关联实体或集合
		//System.out.println("Dept.name=" + employee.getDept().getDeptName());
	
		
		//2)  //迫切外连接,则是表示在做连接的同时,对于关联的表的对象也一并取出,进行初始化。 
		//迫切左外连接(使用fetch, 会把右表employee的数据,填充到左表dept对象中)
		Query q2 = session.createQuery("from Dept d left join fetch d.emps");//左表为dept部门表,将部门表中的信息全保留。
		q2.list();
		//3) 迫切内连接
//		Query q1 = session.createQuery("from Dept d inner join fetch d.emps");
//		q1.list();
		
		session.getTransaction().commit();
		session.close();
	}
	
	// HQL查询优化
	@Test
	public void hql_other() {
		Session session = sf.openSession();
		session.beginTransaction();
		// HQL写死
//		Query q = session.createQuery("from Dept d where deptId < 10 ");
		
		// HQL 放到映射文件中(放在那个映射文件都可以,但现在查询的是部门,就放在Dept.hbm.xml中)
		Query q = session.getNamedQuery("getAllDept");
		q.setParameter(0, 5);
		System.out.println(q.list());
		
		session.getTransaction().commit();
		session.close();
	}
}

在这里插入图片描述
在这里插入图片描述

2) Criteria 查询

App_criteria.java

package cn.atcast.b_query;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.hibernate.criterion.Restrictions;
import org.junit.Test;

public class App_criteria {
	private static SessionFactory sf;
	static {
sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)   // 测试时候使用
			.buildSessionFactory();
	}
	@Test
	public void criteria() {	
		Session session = sf.openSession();
		session.beginTransaction();
	
		Criteria criteria = session.createCriteria(Employee.class);
		// 构建条件
		criteria.add(Restrictions.eq("empId", 1));
		//criteria.add(Restrictions.idEq(1));  // 主键查询
		
		System.out.println(criteria.list());
		session.getTransaction().commit();
		session.close();
	}
	// 5)	SQLQuery, 本地SQL查询
	// 不能跨数据库平台: 如果改了数据库,sql语句有肯能要改。
	@Test
	public void sql() {
		Session session = sf.openSession();
		session.beginTransaction();
		
		SQLQuery q = session.createSQLQuery("SELECT * FROM t_Dept limit 3;")
			.addEntity(Dept.class);  // 也可以自动封装
		  Dept dept= (Dept)q.list().get(0);
		 System.out.println( dept.getDeptName());
		
		session.getTransaction().commit();
		session.close();
	}
}

离线查询

/*
一般我们进行web开发都会碰到多条件查询。例如根据条件搜索。条件的多少 逻辑关系 是or 还是and等等。我们要根据这些条件来拼写查询语句。
但是有了离线查询这些都不是问题,我们可以使用DetachedCriteria来构造查询条件,然后将这个DetachedCriteria作为方法调用参数传递给业务层对象。
而业务层对象获得DetachedCriteria之后,可以在session范围内直接构造Criteria,进行查询。
就此,查询语句的构造完全被搬离到web层实现,而业务层则只负责完成持久化和查询的封装即可。
换句话说,业务层代码是不变化的。我们不必为了查询条件的变化而去频繁改动查询语句了。。
*/

@Test
/**
 * 离线条件查询:DetachedCriteria(SSH整合经常使用.).
 * * 可以脱离session设置参数.
 */
public void detached(){
	// 获得一个离线条件查询的对象
	DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Employee.class);
	detachedCriteria.add(Restrictions.eq("empName","张三"));
	
	Session session = sf.openSession();
	Transaction tx = session.beginTransaction();
	
	List<Employee> list = detachedCriteria.getExecutableCriteria(session).list();// 离线条件查询对象与session绑定.
	for (Employee employee : list) {
		System.out.println(employee.getEmpName());
	}
	tx.commit();
	session.close();
}

}

3) 分页查询

public class App_page {

private static SessionFactory sf;
static {
	sf = new Configuration()
		.configure()
		.addClass(Dept.class)   
		.addClass(Employee.class)   // 测试时候使用
		.buildSessionFactory();
}
// 分页查询
@Test
public void all() {
	Session session = sf.openSession();
	session.beginTransaction();
	
	 Query q = session.createQuery("from Employee");
	 
	 ScrollableResults scroll = q.scroll();  // 得到滚动的结果集,开始时指向第0行
	 scroll.last();							//  滚动到最后一行
	 int totalCount = scroll.getRowNumber() + 1;// 得到滚到的记录数,即总记录数
	 
	 // 设置分页参数
	 q.setFirstResult(0);
	 q.setMaxResults(2);
	 
	 // 查询
	 System.out.println(q.list());
	 System.out.println("总记录数:" + totalCount);
	
	session.getTransaction().commit();
	session.close();
}

}

4) 缓存

缓存的作用

为什么要用缓存?
目的:减少对数据库的访问次数!从而提升hibernate的执行效率!
Hibernate中缓存分类:
一级缓存
二级缓存
 概念
1)Hibenate中一级缓存,也叫做session的缓存,它可以在session范围内减少数据库的访问次数! 只在session范围有效! Session关闭,一级缓存失效!
2)当调用session的save/saveOrUpdate/get/load/list/iterator方法的时候,都会把对象放入session的缓存中。
3)Session的缓存由hibernate维护, 用户不能操作缓存内容; 如果想操作缓存内容,必须通过hibernate提供的evit/clear方法操作。

特点:
只在(当前)session范围有效,作用时间短,效果不是特别明显!
在短时间内多次操作数据库,效果比较明显!

缓存相关几个方法的作用

session.flush(); 让一级缓存与数据库同步
session.evict(arg0); 清空一级缓存中指定的对象
session.clear(); 清空一级缓存中缓存的所有对象

在什么情况用上面方法?
批量操作使用使用:
Session.flush(); // 先与数据库同步
Session.clear(); // 再清空一级缓存内容
 不同的session是否会共享缓存数据?
Session是一级缓存,不会共享数据
 list与iterator查询的区别?
list()
一次把所有的记录都查询出来,
会放入缓存,但不会从缓存中获取数据
Iterator
N+1查询; N表示所有的记录总数
即会先发送一条语句查询所有记录的主键(1),
再根据每一个主键再去数据库查询(N)!
会放入缓存,也会从缓存中取数据!
(做查询的时候iterate()会先到数据库中把id都取出来,然后真正要遍历某个对象的时候先到缓存中找,如果找不到,以id为条件再发一条sql到数据库,这样如果缓存中没有数据,则再查询数据库)

2) 二级缓存配置

二级缓存:
Hibernate提供了基于应用程序级别(对整个应用程序都有效)的缓存, 可以跨多个session,即不同的session都可以访问缓存数据。 这个缓存也叫二级缓存。
Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!如果用户想用二级缓存,只需要在hibernate.cfg.xml中配置即可; 不想用,直接移除,不影响代码。
如果用户觉得hibernate提供的框架框架不好用,自己可以换其他的缓存框架或自己实现缓存框架都可以。

使用二级缓存
查看hibernate.properties配置文件,二级缓存如何配置?
##########################

Second-level Cache

##########################
#hibernate.cache.use_second_level_cache false【二级缓存默认不开启,需要手动开启】
#hibernate.cache.use_query_cache true 【开启查询缓存】

choose a cache implementation 【二级缓存框架的实现】

#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默认实现
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
二级缓存,使用步骤
1)开启二级缓存
2)指定缓存框架
3)指定那些类加入二级缓存
4)测试二级缓存!

缓存策略

放入二级缓存的对象,只读;
非严格的读写
读写; 放入二级缓存的对象可以读、写;
(基于事务的策略,hibernate3.6不支持)

hibernate.cfg.xml

	<!--****************** 【二级缓存配置】****************** --> 
		<!-- a.  开启二级缓存 -->
		<property name="hibernate.cache.use_second_level_cache">true</property>
		<!-- b. 指定使用哪一个缓存框架(默认提供的) -->
		<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
		<!-- 开启查询缓存 -->
		<property name="hibernate.cache.use_query_cache">true</property>
		<!-- c. 指定哪一些类,需要加入二级缓存 -->
		<class-cache usage="read-write" class="cn.atcast.b_second_cache.Dept"/>
		<class-cache usage="read-only" class="cn.atcast.b_second_cache.Employee"/>
		<!-- 集合缓存[集合缓存的元素对象,也加加入二级缓存] -->
		<collection-cache usage="read-write" collection="cn.atcast.b_second_cache.Dept.emps"/>
App.java
package cn.atcast.c_second_cache;

import java.util.Iterator;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.junit.Test;


public class App {
	
	private static SessionFactory sf;
	static {
		sf = new Configuration()
			.configure()
			.addClass(Dept.class)   
			.addClass(Employee.class)  
			.buildSessionFactory();
	}
	@Test
	public void testCache() {
		Session session1 = sf.openSession();
		session1.beginTransaction();
		//将dept放入一级缓存(默认),同时也放入到二级缓存
		Dept dept=(Dept)session1.get(Dept.class, 1);
		//dept.getEmps().size();
		Set emps=dept.getEmps();
		Iterator its=emps.iterator();
		while(its.hasNext()){
			 System.out.println(its.next());
		}
		System.out.println("--------------------------------");
		Session session2 = sf.openSession();
		session2.beginTransaction();
		Dept dept2=(Dept)session2.get(Dept.class, 1);
		Set emps2=dept2.getEmps();
		Iterator its2=emps2.iterator();
		while(its2.hasNext()){
			 System.out.println(its2.next());
		}
		//dept2.setDeptName("销售部");
		session2.getTransaction().commit();
		
	 
	}
	
	@Test
	public void listCache() {
		 
		Session session1=sf.openSession();
		session1.beginTransaction();
		Query q=session1.createQuery("from Employee").setCacheable(true);
		System.out.println(q.list());
		session1.getTransaction().commit();
		session1.close();
		
		System.out.println("---------------------------");
		Session session2=sf.openSession();
		session2.beginTransaction();
		Query q2=session2.createQuery("from Employee").setCacheable(true);
		System.out.println(q2.list());
		session2.getTransaction().commit();
		session2.close();
		 
	}
}

3) ThreadLocal

ThreadLocal类提供了线程局部 (thread-local) 变量。是一个线程级别的局部变量, 并非“本地线程”。
thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。
应用场景:用ThreadLocal 来管理 Hibernate Session
我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,那么Session是否是线程安全的呢?很遗憾,答案是否定的。Session中包含了数据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱
// 使用ThreadLocal集合保存当前业务线程中的SESSION
private static ThreadLocal session = new ThreadLocal();

4) Open session in view

在hibernate中使用load方法时,并未把数据真正获取时就关闭了session,当我们真正想获取数据时会迫使load加载数据,而此时session已关闭,所以就会出现异常。 比较典型的是在MVC模式中,我们在M层调用持久层获取数据时(持久层用的是load方法加载数据),当这一调用结束时,session随之关闭,而我们希望在V层使用这些数据,这时才会迫使load加载数据,我们就希望这时的session是open着得,这就是所谓的Open Session In view 。OpenSessionInViewFilter [全名:org.springframework.orm.hibernate3.support.OpenSessionInViewFilter]是Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个Session,直到这个请求结束,具体是通过一个Filter来实现的。
由于Hibernate引入了Lazy Load特性,使得脱离Hibernate的Session周期的对象如果再想通过getter方法取到其关联对象的值,Hibernate会抛出一个LazyLoad的Exception。所以为了解决这个问题,Spring引入了这个Filter,使得Hibernate的Session的生命周期变长。
有两种方式可以配置实现OpenSessionInView,分别是OpenSessionInViewInterceptor和OpenSessionInViewFilter,功能完全相同,只不过一个在web.xml配置,另一个在application.xml配置而已。倾向配置在application.xml里,因为web.xml里配置的东西的太多的话容易发生冲突,虽然可以调整,但是毕竟多了个麻烦。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值