1. 概述
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任
1.1 CRM
> ```
> 客户关系管理是指企业为提高核心竞争力,利用相应的信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互,从而提升其管理方式,向客户提供创新式的个性化的客户交互和服务的过程。其最终目标是吸引新客户、保留老客户以及将已有客户转为忠实客户,增加市场。
> ```
1.2 ORM
对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。
Object Relational Mapping,对象关系映射,将对象与关系型数据库的表建立映射关系,操作对象就可以操作表。
2.配置文件和XML表头
xml文件头信息可以到
D:\JavaWeb\TestHibernate\lib\hibernate-core-5.0.7.Final.jar!\org\hibernate\hibernate-configuration-3.0.dtd
下找到,即进入core包里面的org\hibernate/就有
2.1 映射文件 Customer.hbm.xml
- 类中的属性名和表中的字段名如果一致,column可以省略
【id标签】:
- 属性:
- name、column、length、type
【property标签】
- 属性:
- name、column、length、type、not-null,uinque
<?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>
<!--建立类与表的映射-->
<class name="com.gxufe.hibernate.demo01.Customer" table="cst_customer">
<!--建立类中的属性与表中的主键对应-->
<id name="cust_id" column="cust_id">
<generator class="native"></generator><!--生成策略-->
</id>
<property name="cust_name" column="cust_name"/>
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
</class>
</hibernate-mapping>
2.2 配置文件 hibernate.cfg.xml
方言
方言对应不同的数据库使用不同的方言,可以去下载下来的hibernate文件的`hibernate-release-5.0.7.Final\project\etc`目录下的`hibernate.properties`文件里面找对应的方言
可选的配置
- 显示SQL :hibernate.show_sql
- 格式化sql :hibernate.format_sql
- 自动建表 : hibernate.hbm2ddl.auto
- none :不使用hibernate的自动建表
- create :如果数据库已经有表,删除原有的表,重新创建,如果没有表,新建表(测试)
- create-drop :如果数据库中有表了,删除原有表,执行操作,删除这个表,如果没有表,新建一个表,使用完了也删除改表(测试)
- update :如果数据库中有表,使用原有表,如果没有表,创建新表(可以更新表结构)
- validate :如果没有表,不会创建表,只会使用数据库中存在的表(校验映射和表结构)
映射文件的引入
也可以在cfg.xml添加c3p0的配置(了接即可)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate?serverTimezone=UTC</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">162263</property>
<!--=========可选配置======-->
<!--打印sql语句-->
<property name="hibernate.show_sql">true</property>
<!--格式化sql-->
<property name="hibernate.format_sql">true</property>
<!--自动创建表-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!--设置事务的隔离级别 4 是mysql的默认级别-->
<property name="hibernate.connection.isolation">4</property>
<!--配置当前线程绑定的Session-->
<property name="hibernate.current_session_context_class">thread</property>
<!--========b必须配置============-->
<!--配置hibernate的方言-->
<!--<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--配置映射文件-->
<mapping resource="com/gxufe/hibernate/demo01/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
2.3 Hibernate加载文件工具类
public class HibernateUtils { public static final Configuration cfg ; public static final SessionFactory sf; static{ cfg = new Configuration().configure(); sf = cfg.buildSessionFactory(); } public static Session openSession(){ return sf.openSession(); } public static Session getCurrentSession(){ //线程绑定 return sf.getCurrentSession(); } }
3. Hibernate核心API(对象)
3.1 Configuration
Hibernate的配置对象
- 作用
3.2 SessionFactory
Session工厂:SessionFactory内部维护了hibernate的连接池和Hibernate的二级缓存(被redis替换了)。是线程安全的对象,一个项目创建一个对象即可
- 抽取工具类
public class HibernateUtils { public static final Configuration cfg ; public static final SessionFactory sf; static{ cfg = new Configuration().configure(); sf = cfg.buildSessionFactory(); } public static Session openSession(){ return sf.openSession(); } }
3.3 Session
类似JDBC中的Connection对象
Session代表的是Hibernate与数据库的连接对象,不是线程安全的。所以不能设置成全局变量,必须是局部变量,内部维护了一级缓存,是与数据库交互的桥梁
保存
- Serializable save(Object obj) -->返回的是可序列化的id
查询方法(get()和load()的区别)
T get(Class c, Serializable id);
T load(Class c,Serializable id);
@Test //查询------->get方法和load方法的区别 public void demo02(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); /** * get方法 * * 采用的是立即加载,执行到这行代码的时候,就会马上发送Sql语句去查询 * * 查询返回是真实对象本身 * * 查询一个找不到的对象的时候,返回null * * load方法 * * 采用的是延迟加载(lazy懒加载),执行到这行代码的时候,不会发送SQL语句,当真正使用这个对象的时候才会发送SQL语句 * *查询返回的是代理对象。 javassist-3.18.1.GA.jar,利用javassist技术产生的代理 * * 查询一个找不到的对象的时候,返回ObjectNotFoundException */ //使用get的方法 //因为是Long类型,所以后面需要加一个L /* Customer customer = session.get(Customer.class, 1l); /* Customer customer = session.get(Customer.class, 100l);//数据库没这个数据 System.out.println(customer);*/ //使用load()方法查询 Customer customer = session.load(Customer.class, 2l); /* System.out.println(customer);//使用了除id以为的属性才发sql语句*/ System.out.println(customer.getCust_id());//没有发送sql语句,直接从上面的load方法参数获得了id transaction.commit(); session.close(); }
修改方法
void update(Object obj);//无返回值的方法
public void demo03(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); //1.直接创建对象修改,数据库的其他属性没了,没有设置的覆盖为null,就是没有来源, /*Customer customer = new Customer(); customer.setCust_id(1l); customer.setCust_name("马尔扎哈"); session.update(customer);*/ //2.(建议)先查询再修改,有来源,来源于查询得到的这行的其他属性,Bean里面的get的方法就有值 Customer customer = session.get(Customer.class, 1l);//修改的必须是查询放回的那个对象否则就报错 customer.setCust_name("古力娜扎"); session.update(customer); transaction.commit(); session.close(); }
删除方法
void delete(Object obj)
//删除方法 public void delete(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); //1.直接删除对象 /*Customer customer = new Customer(); customer.setCust_id(1l);//删除第一条 session.delete(customer);*/ //2.先查询再删除(推荐) ---级联删除 --》的前提就是先查询再删除 Customer customer = session.get(Customer.class, 2l); session.delete(customer); transaction.commit(); session.close(); }
保存或更新
//保存或更新 public void demo05(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); //保存 Customer customer = new Customer(); /*customer.setCust_name("汪大东"); session.saveOrUpdate(customer);*/ //修改 customer.setCust_id(3l); customer.setCust_name("李大而已"); session.saveOrUpdate(customer); transaction.commit(); session.close(); }
查询所有
//查询所有 public void demo06(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); //接受HQL:Hibernate Query Language 面向对象的查询语言 /* Query query = session.createQuery("from Customer"); List<Customer> list = query.list(); for (Customer customer : list){ System.out.println(customer); }*/ //接受Sql语句 SQLQuery sql = session.createSQLQuery("select * from cst_customer"); List<Object[]> list1 = sql.list(); for (Object[] objects: list1) { System.out.println(Arrays.toString(objects)); } transaction.commit(); session.close(); }
3.4 Transaction:事务对象
hibernate中管理事务的对象
- commit()
- Rollback()
4. 持久化
4.1 概述
- 什么是持久化类
持久化:将内存中的一个个对象持久化到数据库中过程。
持久化类:一个java对象与数据库的表建立了映射关系,那么这个类就hibernate中称为持久化类。(持久化类 =java类 + 映射文件)
- 编写规则
对持久化类提供一个无参数的构造方法 :Hibernate底层需要使用反射生成实例。
对属性需要私有,对私有属性提供pblic的get和set方法 :Hibernate中获取,设置对象的值
对持久化类提供一个唯一的标识oid与数据库主键对应 :java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录,hibernate中通过持久化类的OID属性区分是否是同一个对象
持久化类中的属性尽量使用使用包装类型(Integer Long String Double),因为基本数据类型默认是0,那么0就会有很多的歧义。包装类型类型默认是null,不会容易产生歧义
持久化类不要使用
final
进行修饰,延迟加载本身就是hibernate一个优化的手段,返回的是一个代理对象(javassistkey对没有实现接口的类产生一个代理–使用了非常底层的字节码增强技术,继承这个类进行代理),如果不能被继承,不能产生代理对象,延迟加载就失效, load方法和get方法一致了。
5. 主键的生成策略
5.1主键的分类
5.1.1 自然主键
- 主键的本身就是表中的一个字段(实体中的一个具体属性)
- 创建一个人员表,人员都会有提供身份证号(唯一的不可重复),使用身份证作为主键,这种主键称为自然主键。
5.1.2 代理主键
- 代理主键:主键的本身不是表中的必须的一个字段(不是实体中的某个具体的属性)
- 创建一人员表,没有使用人员中的身份证号码,用了与这个表不想关的字段id(pno)。这种主键称为是代理主键。
- 在实际开发中,尽量使用代理主键。
- 一旦自然主键参与业务逻辑中,后期可能需要修改源代码。
- 好的程序设计满足ocp原则,对程序的扩展是open的,对修改源码是close的
5.1.3 主键的生成策略
在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置(UUIDUtils)。在Hibernate中为了减少程序的编写,提供了很多种的主键的生成策略。
- increment:hibernate中提供的自动增长的机制,使用short、int、long类型的主键,在集群下不要使用,在单线程中的程序中使用。
- 首先发送一条数据,select max(id) from 表,然后让id+1作为下一条记录的主键。
- identity : 使用short、int、long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增长的机制的数据库(MySql、MSSQL),但是Oracle没有自动增长。
- squence : 适用short、int、long类型的主键,采用的是序列的方式。(Oracle支持序列),像mysql就不支持序列
- uuid : 使用于字符串类型的主键,使用Hibernate中随机的方式生成的字符串主键
- native : 本地策略,可以在identuty和sequence之间进行自动切换
- assigned : Hibernate放弃外键的管理,需要手动编写程序或者用户自己设置
- foreign : 外部的。一对一的一种关系映射的情况下使用(了解)
6. 持久化的三种状态
Hibernate是持久层框架,通过持久化类完成ORM操作,Hibernate为了更好的管理持久化类,将持久化类分成三种状态。
6.1 瞬时态(transient)
- 这种对象没有唯一的标识OID,没有被session管理,称为瞬时态对象
6.2 持久态(persistent)
- 这种对象有唯一标识的OID,被session管理,称为是持久态对象
6.3 脱管态(游离)(detached)
- 这种对象有唯一标识的OID,没有被session管理,称为托管态对象
6.4 区分
- 持久化类的持久态对象,可以自动更新数据库。
@Test //三种状态的区分 public void demo01(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); //1.刚new出来,瞬时态对象,没有唯一标识OID,就是数据库还没有ID ,没有被session管理 Customer customer = new Customer(); customer.setCust_name("马尔扎哈"); Serializable id = session.save(customer);//2.变成持久态,数据库中已经有了唯一标识的OID,被session管理 session.get(Customer.class,id);//操作持久化对象 //把资源关闭了 transaction.commit(); session.close(); System.out.println("客户名称:"+customer.getCust_name());//3.变成脱管态对象:有唯一标识的OID,没有被session管理 }
6.5 持久化状态的装换
获得:
Customer customer = new Customer();
状态转换
- 瞬时–》持久
- save(obj)
- saveOrUpdate
- 瞬时–》托管
- customer.setCust_id(1l),有唯一标识,但是没有被session管
- 获得
- get()、load()、find()、iterate()
- session.get(Customer.class, 1l );
- 状态转换
- 持久–>瞬时
- delete();
- 持久–托管
- session.close(); clear() 、 evict()
获得
Customer customer = new Customer(); customer.setCust_id(1);//有唯一标识,没有被session管
状态转换
托管—》持久
- update()、saveOrUpdate();
托管—》瞬时
customer.setCust_id(null);
6.6 持久态对象自动更新
原理:依赖hibernate的一级缓存
@Test //持久态对象自动更新数据库 public void demo02(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); Customer customer = session.get(Customer.class, 1l);//已经是一个持久化对象 customer.setCust_name("古力娜扎");//修改 //session.update(customer);//不是持久化对象需要执行该语句,已经是持久化对象回自动发sql更新语句 //把资源关闭了 transaction.commit(); session.close(); } }
7. Hibernate的一级缓存
7.1 缓存的概述
-
什么是缓存
缓存是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。
7.2 Hibernate的一级缓存
Hibernate框架中提供了优化手段:缓存和抓取策略。Hibernate中提供了二种缓存机制;一级缓存和二级缓存。
- Hibernate的一级缓存
称为是Session级别的缓存,一级缓存的生命周期与Session一致(一级缓存是由Session中的一系列的java集合构成)。一级缓存是**自带的不可卸载。**
- Hibernate的二级缓存是SessionFactory级别的缓存,需要配置的缓存
7.3 一级缓存的存在测试
@Test
//证明一级换出的存在
public void demo01(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
//连续发送两次查询数据,只会发送一条sql语句
/* Customer customer1 = session.get(Customer.class,1l);//发送
System.out.println(customer1);
Customer customer2 = session.get(Customer.class,1l);//不发送
System.out.println(customer2);
//查看对象是否相等
System.out.println(customer1 == customer2);//true*/
Customer customer = new Customer();
customer.setCust_name("马大哈");//发送一条sql语句
Serializable id = session.save(customer);
Customer customer1 = session.get(Customer.class, id);
System.out.println(customer1.getCust_name());//不发送sql语句
//把资源关闭了
transaction.commit();
session.close();
}
7.4 一级缓存的内部结构
比较缓存区和快照区的数据
一致:不更新数据库
不一致:更新数据库
@Test //一级缓存的快照区 public void demo02() { Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); //发送sql语句查询,同时放入到一级缓存中 Customer customer = session.get(Customer.class,1l); customer.setCust_name("古力娜扎!!"); //把资源关闭了 transaction.commit(); session.close(); }
8. 事务管理
8.1 什么是事务
- 事务:事务是指的逻辑上的一组操作,组成这组操作的各个逻辑单元要么成功,要么失败
8.2 事务的特性
- 原子性 :代表事务不可分割。
- 一致性 : 代表事务执行的前后,数据的完整性保持一致
- 隔离性 : 代表一个事务执行过程中,不受到其他事务的干扰
- 持久性 : 代表事务执行完成后,数据就持久到数据库中。
- 如果不考虑隔离性,就会引发安全性问题
- 读问题:
- 脏读 一个事务读到另一个事务未提交的事务
- 不可重复 : 一个事务读到了另一个事务已经提交的update数据,导致在前一个事务多次查询结果不一致
- 虚读 : 一个事务读到另一个事务已经提交的insert数据,导致前一个事务多次查询结果不一致。
- 写问题(引发两类丢失更新)
*
8.2 读问题的解决(隔离级别)
- 设置事务的隔离级别
- Read uncommited : 以上问题都不解决
- Read commited : 解决脏读,但是不可重复读和虚读都有可能发生
- Repeatable read : 解决脏读和不可重复读,但是虚读有可能发生
- Serializable : 解决所有读问题
8.3 Hibernate设置隔离级别
- 在application.cfg.xml里面配置隔离级别
<property name="hibernate.connection.isolation">4</property>
- 隔离级别分别对应
- Read uncommited : 1
- Read commited : 2
- Repeatable read : 4
- Serializable : 8
8.4 事务需要加载业务层上
银行转账
- service中封装业务逻辑操作,service里面有个方法aa()用到了到层的dao1()和dao2()两个方法,但是dao1里面的连接(Hibernate称为Session,JDBC称为Connection)和dao2用的不是用一个连接。
- 必须保证连接对象 是同一个
- JDBC中的处理方法
- 向下传递 DBUtils使用的方法
- 使用ThreadLocal对象(绑定线程对象)
- 将这个连接绑定到当前线程中
- 在Dao的方法中,通过当前的线程得到连接对象
8.5 Hibernate绑定线程
Hibernate 框架内部已经绑定好了一个ThreadLocal
在SessionFactory中提供了绑定线程的一个方法
getCurrentSession();
通过一个配置完成
<!--配置当前线程绑定的Session--> <property name="hibernate.current_session_context_class">thread</property>
9. * Hibernate的其他API
9.1 Query(HQL)
Query接口用于接收HQL。查询多个对象
HQL:Hibernate Query Language :Hibernate查询语言
Query的简单使用
@Test //Query public void demo01(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //通过Session获得Query接口 //查询所有 // String hql = "from Customer"; //条件查询 // String hql = "from Customer where cust_name like ?"; //分页查询 String hql = "from Customer "; Query query = session.createQuery(hql); //设置条件问号参数 //query.setParameter(0,"李%");//参数从0开始的 //设置分页 query.setFirstResult(0); //从第几个开始,默认从o开始 query.setMaxResults(3); //返回最多少个数据 List<Customer> list = query.list(); for (Customer customer: list) { System.out.println(customer); } transaction.commit(); }
9.2 Criteria (QBC)
QBC(Query By Criteria) : 条件查询
更加面向对象的一种查询方式
@Test //Criteria(标准) public void demo02(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //通过session获得Criteria的对象 /*Criteria criteria = session.createCriteria(Customer.class);//查询所有对象 List<Customer> list = criteria.list();*/ //条件查询 Criteria criteria = session.createCriteria(Customer.class); //Restrictions(限制条件) add的方法是添加条件限制 //criteria.add(Restrictions.like("cust_name","李%")); //分页 criteria.setFirstResult(0); criteria.setMaxResults(3); List<Customer> list = criteria.list(); for (Customer customer : list) { System.out.println(customer); } transaction.commit(); }
9.3 SQLQuery
用于接收SQL,查询条件特别复杂情况下使用
10 Hibernate的一对多关联映射
10.1 一对多关系
10.1.1 一对多的建表原则:
在多的一方创建外键指向一的一方
创建对象需要注意
- 在一的一方创建集合来存储多(Hibernate默认使用Set)
//通过ORM方式表示:一个客户对应多个联系人 //放置多的一方的集合,一个订单分类有多个订单 //Hibernate默认使用的是Set集合 private Set<LinkMan> linkManSet;
在多的一方创建一的对象代替外键属性
//通过ORM方式表示:一个联系人只能属于某一个客户 //放置的是一的一方的对象 //外键名称改为一的一方的 private Customer customer;
映射文件配置一对多映射关系
多对一(LinkMan.hbm.xml)
- name : 另一方在本类(LinkMan)的属性名称
- class : 一的一方的类的全路径
- column : 在多的一方的表里面的外键名称
<!--配置多(linkman)对一(customer)的关系:放置的是一的一方的对象 配置三个属性,name : 一的一方对象的属性名称 class : 一的一方的类的全路径 column : 在多的一方的表的外键名称 --> <many-to-one name="customer" class="com.gxufe.hibernate.domain.Customer" column="lkm_cust_id"/>
一对多(Customer)
<!--配置一对多的映射:放置多的一方的集合 set标签: * name : 多的一方的对象在本类对应的属性名称 key标签: * column : 多的一方的外键名称 one-to-many标签: * class : 多的一方的类全路径 --> <set name="linkManSet" > <key column="lkm_cust_id"/> <one-to-many class="com.gxufe.hibernate.domain.LinkMan"/> </set>
引入映射文件
<mapping resource="com/gxufe/hibernate/domain/LinkMan.hbm.xml"/>
10.1.2 一对多级联操作
级联指的是:操作一个对象的时候,是否同时操作其他关联的对象
- 级联是由方向性
- 操作一的一方的时候,是否操作到多的一方
- 操作多一方的时候,是否操作到一的一方
- 级联保存或更新
- 保存客户级联联系人
- 保存联系人级联客户
xml设置cascde属性
<set name="linkManSet" cascade="save-update"> <key column="lkm_cust_id"/> <one-to-many class="com.gxufe.hibernate.domain.LinkMan"/> </set>
<many-to-one name="customer" class="com.gxufe.hibernate.domain.Customer" column="lkm_cust_id" cascade="save-update"/>
10.1.3 一对多级联删除
设置映射文件(配置的主体是需要执行被删除的项)
- 要删除的是Customer,级联删除就会把LinkMan相对应的也删除
添加一个级联属性值:delete
<set name="linkManSet" cascade="save-update,delete"> <key column="lkm_cust_id"/> <one-to-many class="com.gxufe.hibernate.domain.LinkMan"/> </set>
测试类
/** *级联删除 * * 删除客户级联删除联系人,删除的主体的是客户,需要在Customer.hbm.xml配置 */ public void demo04(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //没有设置级联删除,默认情况下:修改了联系人的外键为null,删除客户 Customer customer = session.get(Customer.class,1l); /*session.delete(customer);*/ //删除客户,同时删除联系人 session.delete(customer); transaction.commit(); }
10.1.3 一对多设置双向关联产生多余SQL语句
多的一方可以通过一的对象属性(外键)维护外键,一的一方可以通过Set集合去维护多的一方
解决多余的SQL语句
单向维护
是一方放弃外键维护权
一的一方放弃维护,
inverse="true"
<set name="linkManSet" cascade="save-update,delete" inverse="true">
一对多的关联查询的修改时候 (CRM)
区分cascde 和 inverse
10.2 多对多
- 多对多建表原则:
- 创建中间表,中间表至少有两个字段,分别作为外键指向多对多双方的主键 例如:学生表和选课表是多对多,创建中间表为学生选课表,学生选课表最少要有两个字段分别为学生id和课程id
- 二者关系主要通过集合来操作
- 首先查询
- 使用Set集合里面的add、remove方法
10.2.1 多对多的类设置集合
两个类都设置一个存放对方Set集合
例如:
private Set<Role> roles= new HashSet<Role>() ; private Set<User> users = new HashSet<User>();
10.2.2 多对多的映射文件Set属性设置
- set:
name
属性是当前类里边的Set变量的名称,多对多要使用中间表,table
属性就是写中间表- **key:**里面标签的
column
,-当前-对象的对应中间表的外键名称- m-to-m:
class
是对方类的全路径column
对方的对象在中间表中的外键名称<!-- 主要设置关联中间表的属性 --> <set name="roles" table="sys_user_role" cascade="save-update,delete" inverse="false"> <key column="user_id"/> <many-to-many class="com.gxufe.hibernate.domain.Role" column="user_id"/> </set>
10.2.3 测试类
- 保存:多对多建立了双向的关系必须有一方放弃外键维护,一般是被动方放弃外键维护(角色被用户选择)
@Test
/**
* 保存多条记录:保存多个用户角色
*/
public void demo01(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//创建2个用户
User user1 = new User();
User user2 = new User();
user1.setUser_name("迪丽热巴");
user2.setUser_name("古力娜扎");
//3个角色
Role role1 = new Role();
Role role2 = new Role();
Role role3 = new Role();
role1.setRole_name("研发部");
role2.setRole_name("市场部");
role3.setRole_name("营销部");
//设置双向的关联关系:
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user2.getRoles().add(role2);
user2.getRoles().add(role3);
role1.getUsers().add(user1);
role2.getUsers().add(user1);
role2.getUsers().add(user2);
role3.getUsers().add(user2);
//保存:多对多建立了双向的关系必须有一方放弃外键维护
//一般是被动方放弃外键维护(角色被用户选择)
session.save(user1);
session.save(user2);
session.save(role1);
session.save(role2);
session.save(role3);
transaction.commit();
}
10.2.4 多对多的级联保存或更新
配置User.hbm.xml的cascde属性
cascade="save-update"
保存测试
@Test //多对多级联保存 //保存用户级联保存角色,在用户的映射文件中配置 public void demo02(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); User user1 = new User(); user1.setUser_name("迪丽热巴"); Role role1 = new Role(); role1.setRole_name("研发部"); user1.getRoles().add(role1); role1.getUsers().add(user1); session.save(user1); transaction.commit(); }
10.2.5 多对多级联删除(基本用不上)
因为删除学生选课不能也把这个课程给删除了
10.2.6 多对多的常用其他操作
给用户选择课程,给用户改选课程,给用户删除课程
- 添加角色
@Test //给用户选择角色 public void demo03(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //给1号用户多选2号角色 //查询1号用户 User user = session.get(User.class,1l); //查询2号角色 Role role = session.get(Role.class,3l); //给1号用户添加3号角色 user.getRoles().add(role); transaction.commit(); }
- 修改所选之一
思想:先删除后修改
@Test //给用户改选角色 public void demo04(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //给2号用户将原有的2号角色改为3号角色 //查询2号用户 User user = session.get(User.class,2l); //查询2 . 3号角色 Role role2 = session.get(Role.class,2l); Role role3 = session.get(Role.class,3l); //给2号用户修改2号为3号角色 user.getRoles().remove(role2); user.getRoles().add(role3); transaction.commit(); }
- 删除一门课
@Test //给用户删除角色 public void demo05(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //给2号用户删除1号角色 //查询2号用户 User user = session.get(User.class,2l); //查询1号角色 Role role = session.get(Role.class,1l); //给2号用户删除1号角色 user.getRoles().remove(role); transaction.commit(); }
10.3 一对一
- 一对一建表原则:
- 唯一外键对应
- 主键对应
11 Hibernate查询方式
Hibernate提供了很多种查询方式。Hibernate提供了5中查询方式
11.1 OID查询
OID:Hibernate根据对象的OID(主键)进行检索
分别是:
使用get方法
User user = session.get(User.class,1);
使用load方法
User user = session.load(User.class,1);
11.2 对象导航查询
Hibernate根据一个已经查询到的对象,获得其关联对象的一中查询方式
User user = session.get(User.class,1); Role role = user.getRole();
* 11.3 HQL查询
Hiernate Query Language,Hibernate的查询语言,是一种面向对象的查询语言,语法类似SQL。通过session.createQuery(),用于接受一个HQL进行查询方式
11.3.1 简单查询
public void demo02(){ Session sesion = HibernateUtils.getCurrentSession() ; Transaction transaction = sesion.beginTransaction(); //简单写法 // Query query = sesion.createQuery("from Customer"); //别名查询 select c from Customer c Query query = sesion.createQuery("from Customer c"); List<Customer> list = query.list(); /* sql中支持的 * 号写法 select * from 表 HQL不支持 * 写法 */ for (Customer customer : list){ System.out.println(customer); } transaction.commit(); }
11.3.2 排序查询
》
@Test
/**
* 排序查询
*/
public void demo03(){
Session sesion = HibernateUtils.getCurrentSession() ;
Transaction transaction = sesion.beginTransaction();
//排序,默认升序(asc)
//设置降序(desc)
Query query = sesion.createQuery("from Customer order by cust_id desc");
List<Customer> list = query.list();
for (Customer customer : list){
System.out.println(customer);
}
transaction.commit();
}
11.3.3 条件查询
@Test /** * 条件查询 */ public void demo04(){ Session sesion = HibernateUtils.getCurrentSession() ; Transaction transaction = sesion.beginTransaction(); //条件查询 //一、 按位置绑定 //一个条件 // Query query = sesion.createQuery("from Customer where cust_name = ? "); // query.setParameter(0,"马尔扎哈"); /* //两个条件 Query query = sesion.createQuery("from Customer where cust_name = ? and cust_source = ? "); query.setParameter(0,"马尔扎哈"); query.setParameter(1,"lol"); */ //二、按名称绑定 Query query = sesion.createQuery("from Customer where cust_source = :aaa and cust_name like :bbb"); query.setParameter("aaa","lol"); query.setParameter("bbb","马尔%"); List<Customer> list = query.list(); for (Customer customer : list){ System.out.println(customer); } transaction.commit(); }
11.3.4 投影查询
投影查询:查询对象的某个或某些属性
单个查询
Session sesion = HibernateUtils.getCurrentSession() ; Transaction transaction = sesion.beginTransaction(); //投影查询(某些属性) //因为只是返回某一列不是整个Customer对象,所以泛型不能是Customer //单个属性 // List<Object> objects = sesion.createQuery("select c.cust_name from Customer c").list(); //多个属性,每一列的属性不一样 /*List<Object[]> objects = sesion.createQuery("select c.cust_name ,c.cust_source from Customer c").list(); for (Object[] object : objects){ System.out.println(Arrays.toString(object)); }
多个查询
//多个属性,每一列的属性不一样 /*List<Object[]> objects = sesion.createQuery("select c.cust_name ,c.cust_source from Customer c").list(); for (Object[] object : objects){ System.out.println(Arrays.toString(object)); }*/
查询多个属性并封装到对象中
//查询多个属性,但是我想封装到对象中 //首先要提供构造方法 List<Customer> list = sesion.createQuery("select new Customer(cust_name,cust_source) from Customer").list(); for (Customer customer : list){ System.out.println(customer); } transaction.commit(); }
11.3.5 分组统计查询
- sql的分组查询语句:
select cust_source,count(*) from cust_customer group by cust_source
聚合函数的使用: count(*),max(),min(),avg(),sum()
只统计cust_source个数大于2的
select cust_source ,count(*) from Customer group by cust_source having count(*)>=2
@Test /** * 分组统计查询 */ public void demo07(){ Session sesion = HibernateUtils.getCurrentSession() ; Transaction transaction = sesion.beginTransaction(); //聚合函数的使用: count(*),max(),min(),avg(),sum() //uniqueResult (只能查到一个值) Object o = sesion.createQuery("select count(*) from LinkMan").uniqueResult(); System.out.println(o);//可以强转为Long //分组统计查询 sql:select cust_source,count(*) from cust_customer group by cust_source //统计cust_source的个数 // List<Object[]> list = sesion.createQuery("select cust_source ,count(*) from Customer group by cust_source").list(); //只统计cust_source个数大于2的 List<Object[]> list = sesion.createQuery("select cust_source ,count(*) from Customer group by cust_source having count(*)>=2").list(); for (Object[] object : list){ System.out.println(Arrays.toString(object)); } transaction.commit(); }
11.3.6 分页查询
@Test /** * 分页查询 */ public void demo06(){ Session sesion = HibernateUtils.getCurrentSession() ; Transaction transaction = sesion.beginTransaction(); //分页 //创建一个客户 Query query = sesion.createQuery("from LinkMan"); query.setFirstResult(10); query.setMaxResults(10); List<LinkMan> list = query.list(); for (LinkMan linkMan : list){ System.out.println(linkMan); } transaction.commit(); }
* HQL的多表查询
-
SQL的多表查询
-
连接查询
-
交叉连接:笛卡尔积(不用)
select * from A,B
-
内连接 : inner join (inner 可以省略)
查的是两个表的公共部分
-
隐式内连接
select * from A,B where A.id = B.aid;
-
显示内连接
select * from A inner join B on A.id=B.aid;
-
-
外连接 :
-
左外连接 :
left outer join
(outer 可以省略)查到的是左边表的所有信息,和另外一个表的公共部分
select * from A left outer join B on a.id=B.aid;
-
右外连接 :
right outer join
(outer 可以省略)查到的是右边的全部,以及两个表的公共部分
select * from A right join B on a.id=B.aid;
-
-
-
子查询
-
-
HQL的多表查询
连接查询
交叉连接
内连接
显示内连接
#SQL: select * from A inner join B on A.id=B.aid;
#HQL: from Customer c inner join c.linkManSet
隐式内连接
sql:
select * from A,B where A.id = B.aid;
HQL:
Query query = session.createQuery("from Customer c,LinkMan l where c.cust_id=l.customer");
迫切内连接
HQL:迫切内连接,其实就是在普通的内连接inner join后添加一个关键字fetch
from Customer c inner join fetch c.linkManSet
//迫切内连接,fetch :通知hibernate,将另一个对象封装到该对象中,所以返回的对象是Customer //设置distinct c(不同) 的,不然会重复 Query query = session.createQuery("select distinct c from Customer c inner join fetch c.linkManSet"); List<Customer> list = query.list(); for (Customer customer : list ) { System.out.println(customer+"\n"); }
外连接
左外连接
右外连接
迫切左外连接
//迫切左外连接 Query query = session.createQuery("select distinct c from Customer c left join fetch c.linkManSet"); List<Customer> list = query.list(); for (Customer customer : list ) { System.out.println(customer+"\n"); }
* 11.4 QBC查询
QBC查询:Query By Criteria,条件查询。是一种更加面向对象化的查询方式
- 查询需要使用的对象
- add : 普通的条件 ,where后面的条件
- addOrder :排序
- setProjection :聚合函数group by having
11.4.1 简单查询
@Test /** * 简单查询 */ public void demo01(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //获得Criteria的对象 Criteria criteria = session.createCriteria(Customer.class); List<Customer> list = criteria.list(); for (Customer customer : list){ System.out.println(customer); } transaction.commit(); }
11.4.2 排序查询
调用Criteria对象有排序的方法
criteria.addOrder(Order.desc("cust_id"));
@Test /** * 排序查询 */ public void demo02(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //获得Criteria的对象 Criteria criteria = session.createCriteria(Customer.class); //调用 criteria.addOrder(Order.desc("cust_id")); List<Customer> list = criteria.list(); for (Customer customer :list){ System.out.println(customer); } transaction.commit(); }
11.4.3 分页查询
设置两个参数
criteria.setFirstResult(0); criteria.setMaxResults(10);
11.4.4 条件查询
需要记住一些方法名称
@Test /** * 条件查询 */ public void demo04(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //获得Criteria的对象 Criteria criteria = session.createCriteria(Customer.class); //设置条件 /** * = eq * > gt (greater than) * >= ge * < lt * <= le less than * <> ne (不等于) * like * in * and * or */ //一个条件 //criteria.add(Restrictions.eq("cust_source","lol")); //两个条件 criteria.add(Restrictions.eq("cust_source","lol")); criteria.add(Restrictions.like("cust_name","马%")); //or //criteria.add(Restrictions.or(Restrictions.like("cust_name","马%"))); List<Customer> list = criteria.list(); for (Customer customer :list){ System.out.println(customer); } transaction.commit(); }
11.4.5 统计查询
@Test /** * 统计查询 */ public void demo05(){ Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //获得Criteria的对象 Criteria criteria = session.createCriteria(Customer.class); /** * add :普通的条件 ,where后面的条件 * addOrder :排序 * setProjection :聚合函数group by having */ //统计 criteria.setProjection(Projections.rowCount());//返回行数 Long num =(Long) criteria.uniqueResult(); System.out.println("num="+num); transaction.commit(); }
* 11.4.6 离线条件查询(DetachedCriteria)
离线:脱离session使用
在web层上实现
//web层 DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); dc.add(Restrictions.like("cust_name","马尔%"));
在dao层代码
//dao层 Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Criteria criteria = dc.getExecutableCriteria(session); List<Customer> list = criteria.list(); for (Customer customer : list){ System.out.println(customer); } transaction.commit();
11.5 SQL查询
通过使用基本的sql查询
@Test /** * sql基本查询 */ public void demo07() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //存放到数组当中 /* SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer"); List<Object[]> list = sqlQuery.list(); for (Object[] objects : list){ System.out.println(Arrays.toString(objects));*/ //存放到对象当中 SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer"); sqlQuery.addEntity(Customer.class); List<Customer> list = sqlQuery.list(); for (Customer customer : list) { System.out.println(customer); } transaction.commit(); }
12 抓取策略(优化)
12.1 延迟加载
延迟加载:lazy (懒加载)。执行该代码的时候,不会发送语气进行查询,在真正使用这个对象的属性的时候才会发送sql语句进行查询
12.1.1 延迟加载的分类
类级别的延迟加载
就是在class标签里面的设置
指的是通过load方法查询某个对象的时候,是否采用延迟。、
session.load(Customer.class,1);
<class name="com.gxufe.hibernate.domain.Customer" table="cst_customer" lazy="true"> <!--lazy默认是true,设置为false,就会失效,load的方法就会马上加载-->
- 使其失效的方法
- 设置lazy为false
- 将持久化类使用final修饰
- Hibernate.initialize(obj)
关联级别的延迟加载
在set、many-to-one 标签里面设置lazy
指的是在查询到某个对象的时候,查询其关联对象的时候,是否采用延迟加载。
Customer customer = session.get(Customer.class,1);//里面有个Set<LinkMan>集合
customer.getLinkManSet();–通过客户获得联系人的时候,是否采用了延迟加载,称为是关联级别的延迟。
12.1.2 抓取策略
通过一个对象抓取到关联对象需要发送sql语句,SQL如何发送,发送称什么样的格式进行策略进行配置
-
通过
<set>
或者<many-to-one
上通过fetch属性进行设置 -
fetch和这些标签上的lazy如何设置优化发送的Sql语句
<set name="linkManSet" fetch="join" lazy="true" >
<set>
上的fetch和lazy
- fetch:抓取策略,控制SQL语句格
- select :默认,发送普通的select语句,查询其关联对象
- join :发送一条迫切左外连接查询关联对象
- subselect :发送一条子查询查询其关联对象
- lazy:延迟加载,控制查询关联对象的时候是否采用延迟
- true :默认,查询其关联对象的时候,采用延迟加载
- false :查询关联对象的时候,不采用延迟加载
- extra :极其懒惰。
在实际开发中,一般采用默认值。有特殊的需求,可能需要配置join(迫切左查询)