Java 框架 01 — Hibernate 03(表关联及其配置文件设置、查询总结、HQL查询、Criteria查询)

0 回顾

0.1 hibernate主键生成策略

  1. 自然主键:
    1. assigned
  2. 代理主键:
    1. identity
    2. increment
    3. native
    4. hilo
    5. sequence
    6. uuid

0.2 hibernate基本查询

0.2.1 HQL

  • HQL:
    • session.createQuery(HQL);
    • Query - list()/uniqueResult()
      • setParameter(index/name, args);
      • setString/Int()

0.2.2 Criteria

  • Criteria:
    • session.createCriteria(class);
    • add(Restrictions.idEq(id)/eq("name", arg))
    • list/uniqueResult

0.2.3 SQL

  • SQL:
    • session.createSQLQuery(sql);,API同Query
    • addEntity(class) -> 指定封装类型

0.3 hibernate事务

  • 事务级别

    1. 读未提交、
    2. 读已提交、
    3. 可重复读、
    4. 串行化
  • 开启事务:

    • servicedao:执行sql,用到同一个session

    • getCurrentSession()

    • property:current_session_context_class=thread

      <!-- 配置session和当前线程绑定
      			"current_session_context_class" thread
      -->
      <property name="current_session_context_class">thread</property>
      

一、表关联的关系

  1. 1-n:在n的一方添加1的一方的主键作为外键
  2. n-n:需要第三张表存储各自外键

1.1 一对多(1-n)

1.1.1 添加关联关系

//
	@Test
	public void test01() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		// 1> 准备Customer对象
		Customer c1 = new Customer();
		c1.setCust_name("百度");
		Customer c2 = new Customer();
		c2.setCust_name("网易");
		
		// 2> 准备Linkman对象
		Linkman l1 = new Linkman();
		l1.setLkm_name("老王");
		Linkman l2 = new Linkman();
		l2.setLkm_name("老张");
		Linkman l3 = new Linkman();
		l3.setLkm_name("老李");
		Linkman l4 = new Linkman();
		l4.setLkm_name("老刘");
		
		// 3>添加关联关系
		c1.getLinkmen().add(l1);
		c1.getLinkmen().add(l2);
		
		l1.setCustomer(c1);
		l2.setCustomer(c1);
		
		c2.getLinkmen().add(l3);
		c2.getLinkmen().add(l4);
		
		l3.setCustomer(c2);
		l4.setCustomer(c2);
		
		// 4> 将对象持久化
		session.save(c1);
		session.save(c2);
		// cascade="save-update" 可以省略如下代码
//		session.save(l1);
//		session.save(l2);
//		session.save(l3);
//		session.save(l4);
		
		// --------------------
		tx.commit();
		session.close();
	}
1.1.1.1 被引用表(1表)配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
<hibernate-mapping package="beans">
	<class name="Customer" table="cst_customer" >
		<id name="cust_id" >
			<generator class="native" ></generator>
		</id>
		<property name="cust_name" ></property>
		<property name="cust_source"></property>
		<property name="cust_industry"></property>
		<property name="cust_level"></property>
		<property name="cust_phone"></property>
		<property name="cust_mobile"></property>
		
		<!-- 配置一对多关系 -->
		<!-- set:表示一对多
			  |- name:多的一方属性名
			  |- inverse:是否放弃外键维护,
			  			  默认为"false",不放弃
			  			  true,放弃维护,只有 1的一方 才能放弃
			  |- cascade:级联操作选项
			  		save-update:保存Customer的时候,自带的Linkman一起保存
			  		delete:级联删除
			  		all: save-update + delete
			 key - column:表示自己被别人引用的外键名
			 one-to-many - class:多的一方的引用类型
		 -->
		<set name="linkmen" inverse="true" cascade="save-update">
			<key column="lkm_cust_id"></key>
			<one-to-many class="Linkman"/>
		</set>
		
	</class>
</hibernate-mapping>
  • 重点
		<set name="linkmen" inverse="true" cascade="save-update">
			<key column="lkm_cust_id"></key>
			<one-to-many class="Linkman"/>
		</set>
  • 属性辨析:
    • set
      1. name多的一方属性名
      2. inverse:是否放弃外键维护
        • 默认为"false",不放弃
        • true,放弃维护,只有 1的一方 才能放弃
      3. cascade:级联操作选项
        • save-update:保存Customer的时候,自带的Linkman一起保存
        • delete:级联删除
        • all: save-update + delete
    • key
      1. column:表示自己被别人引用的外键名
    • one-to-many
      1. class多的一方的引用类型
1.1.1.2 被应用表(n表)配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
<hibernate-mapping package="beans">
	<class name="Linkman" table="cst_linkman" >
		<id name="lkm_id" >
			<generator class="native" ></generator>
		</id>
		<property name="lkm_name" ></property>
		<property name="lkm_gender"></property>
		<property name="lkm_phone"></property>
		<property name="lkm_mobile"></property>
		<property name="lkm_email"></property>
		<property name="lkm_qq"></property>
		<property name="lkm_position"></property>
		<property name="lkm_memo"></property>
		
		<!-- 配置多对一关系 -->
		<!-- many-to-one:
				|- name:引用一的一方的属性名
				|— column:外键字段
				|- class:外键对应类型是什么
		 -->
		<many-to-one name="customer" column="lkm_cust_id" class="Customer" ></many-to-one>
		
	</class>
</hibernate-mapping>
  • 重点
<!-- 配置多对一关系 -->
<!-- many-to-one:
	   |- name:引用一的一方的属性名
	   |— column:外键字段
	   |- class:外键对应类型是什么
-->
<many-to-one name="customer" column="lkm_cust_id" class="Customer" ></many-to-one>
  • 属性辨析
    • many-to-one
      1. name:引用1的一方的属性名
      2. column:外键字段
      3. class:外键对应类型

1.1.2 删除操作

	// 删除客户1
	@Test
	public void test02() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		// 1> 查找id为1的客户对象
		Customer customer = session.get(Customer.class, 2l);
		
		System.out.println(customer.getLinkmen());
		
		// 2> 删除customer对象
		session.delete(customer);
		// 所关联的联系人一起删掉:cascade=delete
		// --------------------
		tx.commit();
		session.close();
	}
	
//----------------------------------
	// 删除客户2,保留他原先的联系人
	@Test
	public void test03() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		// 1> 查找id为1的客户对象
		Customer customer = session.get(Customer.class, 2l);
		
		System.out.println(customer.getLinkmen());
		// 2> 取消关联关系 - 外键
		for (Linkman lk : customer.getLinkmen()) {
			lk.setCustomer(null);//手动取消
		}
		
		// 3> 删除customer对象
		session.delete(customer);
		
		// --------------------
		tx.commit();
		session.close();
	}

1.2 多对多(n-n)

1.2.1 添加用户、角色/权限

	@Test
	// 添加用户、角色/权限
	public void test01() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		// 1> 准备User对象
		User u1 = new User();
		u1.setUser_code("zhangsan");
		u1.setUser_name("张三");
		
		User u2 = new User();
		u2.setUser_code("lucy");
		u2.setUser_name("露西");
		
		// 2> 准备Role对象
		Role r1 = new Role();
		r1.setRole_name("经理");
		
		Role r2 = new Role();
		r2.setRole_name("保安");
		
		// 3>添加关联关系
		u1.getRoles().add(r1);
		u1.getRoles().add(r2);
		u2.getRoles().add(r1);
		u2.getRoles().add(r2);
		
		r1.getUsers().add(u1);
		r1.getUsers().add(u2);
		r2.getUsers().add(u1);
		r2.getUsers().add(u2);
		
		// 4> 将对象持久化
		session.save(u1);
		session.save(u2);
//		session.save(r1);
//		session.save(r2);
		
		// --------------------
		tx.commit();
		session.close();
	}

1.2.2 添加新的权限

	@Test
	// 给lucy添加新的权限
	public void test02() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		// 1> 获得lucy对象
		User lucy = session.get(User.class, 2l);
		
		// 2> 新建新的权限
		Role role = new Role();
		role.setRole_name("财务");
		
		// 3> 设置关联关系
		lucy.getRoles().add(role);
		role.getUsers().add(lucy);
		
		// 4> 保存role
		session.save(role);
		
		// --------------------
		tx.commit();
		session.close();
	}

1.2.3 删除权限

	@Test
	// 给张三删除 除了保安以外的权限
	public void test03() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		// 1> 获得张三对象
		User user = session.get(User.class, 1l);
		
		// 2> 获得经理对象
		Role role = session.get(Role.class, 1l);
		
		// 3> 从张三中删除经理
		user.getRoles().remove(role);
		role.getUsers().remove(user);
		
		// HashSet 删除原理 hashCode equals(Role 和 User必须重写equals和hashCode放法)
		
		// --------------------
		tx.commit();
		session.close();
	}	

1.2.4 中间表的配置文件

  • User
		<!-- 多对多关系 -->
		<!-- set: 多对多
			   |- name: 包含的属性名
			   |- table: 第三张表
			 key:
			   |- column: 别人引用“我”的外键名  - 第三张表中
			 many-to-many:
			   |- class: 包含的属性对应的完整类名
			   |- column: “我”引用别人的外键
		 -->
		<set name="roles" table="sys_user_role" cascade="save-update">
			<key column="user_id"></key>
			<many-to-many class="Role" column="role_id"></many-to-many>
		</set>
  • Role表
		<!-- 多对多哪方放弃外键维护,都可以
			 业务角度来说,是后添加的来维护外键
		 -->
		<set name="users" table="sys_user_role" inverse="true">
			<key column="role_id"></key>
			<many-to-many class="User" column="user_id"></many-to-many>
		</set>
  • 总结:set属性中多了table属性,中间表的表名
    • 外键维护:
      • 一对多元素添加的时候,外键添加了两次,说明外键被双方维护
      • 减少不必要的外键维护,一定需要有一方放弃外键维护
      • inverse="true",减少不必要的sql语句
    • 级联操作:减少java代码书写
      • cascade
        • save-update
        • delete
        • all

二、查询总结

2.1 oid查询 - get/load

  • 根据主键查询

2.2 对象属性导航查询 - customer.getLinkmen()

  • 通过查询结果的属性进行导航查询

2.3 HQL

2.3.1 查询全部

  • HQL语句:from com.ruki.crm.Customer
@Test
	public void test01() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
//		String hql = "from Customer"; // 简化写法:工程中只有唯一的Custoemr类
		// from Customer - hql:*不能用
		String hql = "from beans.Customer"; // 完整写法
//		String hql = "from java.lang.Object"; // 查询所有对象
		// select cust_id, cust_name from cst_customer
		// 查询出来的结果,每条记录都封装成一个Object数组
		String hql1 = "select cust_id, cust_name from Customer";
		// 要求:有对应的构造器
		String hql2 = "select new Customer(cust_id, cust_name) from Customer";
		
		Query query = session.createQuery(hql2);
		List list = query.list();
		System.out.println(list);
		// --------------------
		tx.commit();
		session.close();
	}
  • 重点步骤
    1. 写HQL语句:
      1. 查询所有的完整写法:String hql = "from beans.Customer";
      2. 查询含特定字段的Customer对象:String hql2 = "select new Customer(cust_id, cust_name) from Customer";
        • 前提是必须要有有参构造器
    2. 传入HQL语句,生成Query对象。Query query = session.createQuery(hql2);
    3. Query对象,返回查询结果。List list = query.list();

2.3.2 条件查询

  • 在HQL语句中添加where条件即可

2.3.3 分页查询

@Test
	// 分页查询
	public void test02() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		String hql = "from Linkman";
		
		Query query = session.createQuery(hql);
		// 设置分页显示 最开始条数、每页显示条数
		// select * from cst_customer limit 0, 2;
		query.setFirstResult(0); // startRow 从0开始
		
		query.setMaxResults(2); // pageSize
		
		List list = query.list();
		System.out.println(list);
		
		// --------------------
		tx.commit();
		session.close();
	}

2.3.4 聚合函数

@Test
	// 聚合函数
	public void test03() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		// select count(*) from cst_customer
		// select count(cust_id) from Customer
		String hql = "select count(cust_id) from Customer";
		Query query = session.createQuery(hql);
		Number count = (Number) query.uniqueResult();
		
		System.out.println(count);
		// --------------------
		tx.commit();
		session.close();
	}
  • 重点步骤

    • HQL语句的编写,在原生sql语句中进行改进

      -- 原生sql 
      select count(*) from cst_customer
      -- HQL
      select count(cust_id) from Customer
      

2.3.5 表链接(关联查询)

  • 关联查询的sql语句

    -- 内连接 
     -- 隐式内连接:等值链接 
     select * from cst_customer c,cst_linkman l 
    	 where c.cust_id = l.lkm_cust_id 
     -- 显示内连接: 
     select * from cst_customer c 
     	[inner] join cst_linkman l
     	on c.cust_id = l.lkm_cust_id 
     -- 左外 
     select * from cst_customer c 
     	[outer] left join cst_linkman l 
    	 on c.cust_id = l.lkm_cust_id 
     -- 右外 
     select * from cst_customer c 
     	[outer] right join cst_linkman l 
     	on c.cust_id = l.lkm_cust_id 
    
2.3.5.1 迫切内连接
from Customer c join fetch c.linkmen
  • fetch将返回结果包装成对象
2.3.5.2 迫切左外连接
from Customer c left join fetch c.linkmen
2.3.5.3 迫切右外连接
from Customer c right join fetch c.linkmen
	@Test
	public void test05() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
//		String hql = "from Customer c join fetch c.linkmen";
//		String hql = "from Customer c left join fetch c.linkmen";
		String hql = "from Customer c right join fetch c.linkmen";
		Query query = session.createQuery(hql);
		List list = query.list();

		System.out.println(list);
		// --------------------
		tx.commit();
		session.close();
	}

2.3.6 排序

@Test
	// 排序
	public void test04() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		String hql = "from Customer order by cust_id desc";
		Query query = session.createQuery(hql);
		List list = query.list();
		
		System.out.println(list);
		// --------------------
		tx.commit();
		session.close();
	}

2.4 Criteria(没有HQL和SQL语句)

2.4.1 查询全部

  • 没有添加任何条件,即为查询全部。
@Test
	// 查询全部
	public void test01() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		Criteria criteria = session.createCriteria(Customer.class);
		List list = criteria.list();
		System.out.println(list);
		// --------------------
		tx.commit();
		session.close();
	}

2.4.2 条件查询

  • 添加条件:criteria.add(Restrictions.le("lkm_id", 9l));
@Test
	// 条件查询全部
	/*
	 * = 		eq(属性名,值)
	 * >		gt(属性名,值)
	 * >=		ge
	 * <		lt
	 * <=		le
	 * <>		ne
	 * in		in(属性名,集合)
	 * between and	between(属性名,值1,值2)
	 * like		like
	 * is null	isNull
	 * is not null	isNotNull
	 * or 		or(条件1,条件2,条件3...)	
	 * and		add/and
	 * select * from cst_linkman where lkm_name like '%李%' or lkm_id > 8
	 */
	public void test06() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		Criteria criteria = session.createCriteria(Linkman.class);
		criteria.add(Restrictions.le("lkm_id", 9l))
			.add(
				Restrictions.or(
						Restrictions.like("lkm_name", "%李%"),
						Restrictions.gt("lkm_id", 8l)));
		List list = criteria.list();
		System.out.println(list);
		// --------------------
		tx.commit();
		session.close();
	}

2.4.3 分页查询

  • 设置起始数据位置:criteria.setFirstResult(0);
  • 设置最大查询数量:criteria.setMaxResults(2);
@Test
	// 分页查询
	public void test02() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		
		Criteria criteria = session.createCriteria(Customer.class);
		criteria.setFirstResult(0);
		criteria.setMaxResults(2);
		
		List list = criteria.list();
		System.out.println(list);

		// --------------------
		tx.commit();
		session.close();
	}

2.4.4 聚合函数

  • 添加聚合函数字段:criteria.setProjection(Projections.rowCount());
@Test
	// 聚合函数
	public void test03() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		Criteria criteria = session.createCriteria(Customer.class);
		// select count(*) from customer where id>5;
		// Projections
		criteria.setProjection(Projections.rowCount());
//		criteria.add(arg0)
		
		List list = criteria.list();
		criteria.uniqueResult();
		System.out.println(list);
		// --------------------
		tx.commit();
		session.close();
	}

2.4.5 排序

  • 添加排序规则:criteria.addOrder(Order.desc("cust_id"));
@Test
	// 排序
	public void test04() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		// --------------------
		Criteria criteria = session.createCriteria(Customer.class);
		
		// criteria添加排序规则
		criteria.addOrder(Order.desc("cust_id"));
		
		List list = criteria.list();

		System.out.println(list);
		// --------------------
		tx.commit();
		session.close();
	}

2.5 Criteria离线查询(在session前添加条件)

  • 创建离线Criteria对象:DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
  • 添加条件:dc.add(Restrictions.like("cust_name", "%李%"));
@Test
	public void test01() {
		// 模拟web层/service
		// 凭空创造Criteria对象
		DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
		// dc拼接条件的用法和Criteria完全一样
		dc.add(Restrictions.like("cust_name", "%李%"));
		
		List<Customer> list = findCustomerByCriteria(dc);
		System.out.println(list);
	}
	
	// 条件的拼接,以后可以在web层、也可以在service层
	// 问题 Criteria 和 session绑定 
	// session最大范围,service层
	// web层拼接条件 提供了离线版Criteria对象
	// 模拟dao
	public List<Customer> findCustomerByCriteria(DetachedCriteria dc) {
		Session session = HibernateUtils.openSession();
		Criteria criteria = dc.getExecutableCriteria(session);
		return criteria.list();
	}

2.6 SQL查询

  1. 写SQL语句:String sql = "select * from cst_customer where cust_name=?";
  2. 获得SQLQuery对象:SQLQuery sqlQuery = session.createSQLQuery(sql);
  3. 设置查询参数:sqlQuery.setParameter(0, "阿里巴巴");
  4. 获得结果集:Customer customer = (Customer) sqlQuery.uniqueResult();
  • 具体代码见Hibernate_02笔记中的5.3部分

三、查询优化策略

3.1 类级别:懒加载

  • lazy - true/false
    • 默认true
  • get - 不存在查询优化策略,查的时候直接获得结果
  • load - 查询时,得到代理对象
    • 使用对象的时候,才真正查询,获得内容

3.2 表关联级别

  • fetch:
    • select :默认,单表查询
    • join:关联查询
    • subselect:子查询
  • lazy:
    • true :默认,懒加载
    • false:关闭懒加载
    • extra:及其懒惰
  • 结论:优化策略使用默认值
package test;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.junit.Test;

import beans.Customer;
import utils.HibernateUtils;

public class QueryDemo {
	@Test
	public void test01() {
		Session session = HibernateUtils.openSession();
		
		Customer customer = session.load(Customer.class, 3l);
		
		System.out.println(customer);
	}
	
	@Test
	// fetch: select - 单表查询 (默认)
	// lazy: true - 懒加载 (默认)
	// 		 false - 立即加载(查父表时,子表同步查询)
	//		 extra - 极其懒惰,用到什么数据,查什么数据
	public void test02() {
		Session session = HibernateUtils.openSession();
		
		Customer customer = session.load(Customer.class, 3l);
		
		System.out.println(customer);
		
		System.out.println(customer.getLinkmen().size());
		System.out.println(customer.getLinkmen());
	}
	@Test
	// fetch: join - 关联查询
	// lazy: 失效
	public void test03() {
		Session session = HibernateUtils.openSession();
		
		Customer customer = session.load(Customer.class, 3l);
		
		System.out.println(customer);
		
		System.out.println(customer.getLinkmen().size());
		System.out.println(customer.getLinkmen());
	}
	
	@Test
	// fetch: subselect - 子查询
	// lazy: true
	public void test04() {
		Session session = HibernateUtils.openSession();
		
		String sql = "from Customer";
		Query query = session.createQuery(sql);
		
		List<Customer> list = query.list();
		
		for (Customer c : list) {
			System.out.println(c);
			System.out.println(c.getLinkmen().size());
			System.out.println(c.getLinkmen());
		}
		
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值