Java
Java Web框架

Hibernate(8)

简介:Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

1. 整合log4j(了解)

slf4j核心jar包slf4j-api-1.6.1.jar是日志框架,将其他优秀的日志第三方进行整合。

1. 整合导入jar包

  • log4j-1.2.17.jar:log4j核心包
  • slf4j-log4j12-1.7.5.jar:过渡jar(整合jar)
  1. 导入配置文件
  • log4j.properties,此配置文件通知log4j如何输出日志
  • 配置文件内容:

有以下几项可以配置:

  1. 记录器

例如:log4j.rootLogger=info, stdout, file。它的格式一般是log4j.rootLogger=日志级别, 输出源1, 输出源2...。log4j日志级别有以下几类:

- fatal:致命错误
- error:错误
- warn:警告
- info:信息
- debug:调试信息
- trace:堆栈信息(由高到底顺序)

  1. 输出源

例如:log4j.appender.file=org.apache.log4j.FileAppender。它的格式一般是log4j.appender.输出源的名称=输出源的实现类,名称可以自定义,实现类由log4j提供。

  • 输出源属性例如:log4j.appender.file.File=d\:mylog.log
  • 输出源属性格式:log4j.appender.名称.属性=值

每一个输出源对应一个实现类,实现类都属性(setter),底层执行setter方法进行赋值。

常见的输出源实现类有:

  • org.apache.log4j.FileAppender:输出文件中,file表示文件输出位置。
  • org.apache.log4j.ConsoleAppender:输出到控制台,Target表示使用哪种输出方式。

注:在控制台打印内容,取值:System.out / System.err

  1. 布局,确定输出格式

例如:log4j.appender.stdout.layout=org.apache.log4j.PatternLayout,它的格式一般是log4j.appender.数据源.layout=org.apache.log4j.PatternLayout,布局属性可使用log4j.appender.数据源.layout.ConversionPattern=值的方式设置。

注:可以对指定的目录设置日志级别,格式一般是log4j.logger.包结构=级别,例如:log4j.logger.org.hibernate.transaction=debug

2. 一对一

  • 情况一:主表的主键,与从表的外键(唯一),形成主外键关系。
  • 情况二:主表的主键,与从表的主键,形成主外键关系(从表的主键又是外键),主键同步。

2.1. 情况一的配置

Company.hbm.xml文件:

  • <?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="com.coderap.domain" >
  • <class name="Company" table="t_Company" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 配置一对一
  • one-to-one:默认使用主键同步策略完成一对一的表关系体现
  • property-ref:指定company在一对一关联时,指向哪个属性
  • -->
  • <one-to-one name="address" class="Address" property-ref="company" > </one-to-one>
  • </class>
  • </hibernate-mapping>

Address.hbm.xml文件:

  • <?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="com.coderap.domain">
  • <class name="Address" table="t_Address">
  • <id name="id" column="id">
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string"></property>
  • <!--
  • unique:唯一,外键唯一
  • -->
  • <many-to-one name="company" class="Company" column="cid"
  • unique="true"></many-to-one>
  • </class>
  • </hibernate-mapping>

相关测试代码:

  • @Test
  • // 保存一对一数据
  • // 注意: 在一对一使用外键时,外键所在的对象才能维护关系,另一方无法维护关系
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Company company = new Company();
  • company.setName("公司1");
  • Address addr = new Address();
  • addr.setName("地址1");
  • addr.setCompany(company);
  • session.save(company);
  • session.save(addr);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  • @Test
  • // 查询一对一数据
  • // Hibernate查询一对一数据,会使用表关联查询
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Company company = (Company) session.get(Company.class, 1);
  • System.out.println(company);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

2.2. 情况二的配置

Company.hbm.xml文件:

  • <?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="com.coderap.domain">
  • <class name="Company" table="t_Company">
  • <id name="id" column="id">
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string"></property>
  • <!--
  • 配置一对一
  • one-to-one:默认使用主键同步策略完成一对一的表关系体现
  • 同时,cascade、fetch及lazy都可以使用
  • -->
  • <one-to-one name="address" class="Address">
  • </one-to-one>
  • </class>
  • </hibernate-mapping>

Address.hbm.xml文件:

  • <?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="com.coderap.domain">
  • <class name="Address" table="t_Address">
  • <id name="id" column="id">
  • <!-- foreign: 该主键既是主键又是外键 -->
  • <generator class="foreign">
  • <!-- 作为外键时引用哪个属性 -->
  • <param name="property">company</param>
  • </generator>
  • </id>
  • <property name="name" column="name" type="string"></property>
  • <!-- 配置一对一关系 -->
  • <one-to-one name="company" class="Company" constrained="true"></one-to-one>
  • </class>
  • </hibernate-mapping>

相关测试代码:

  • @Test
  • // 保存一对一数据
  • // 注意: 在一对一使用外键时,外键所在的对象才能维护关系,另一方无法维护关系
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Company company = new Company();
  • company.setName("公司1");
  • Address addr = new Address();
  • addr.setName("地址1");
  • addr.setCompany(company);
  • session.save(company);
  • session.save(addr);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  • @Test
  • // 查询一对一数据
  • // Hibernate查询一对一数据,会使用表关联查询
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Company company = (Company) session.get(Company.class, 1);
  • System.out.println(company);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

3. 二级缓存

缓存(Cache)是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘(永久性数据存储源)的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存。

3.1. 什么是二级缓存

Hibernate 提供缓存机制有一级缓存、二级缓存:

  • 一级缓存:session级别缓存,在一次请求中共享数据。
  • 二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。

SessionFactory的缓存两部分:

  • 内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外只读的。不能操作。
  • 外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存hibernate只提供规范(接口),需要第三方实现类。外置缓存有成为二级缓存。

3.2. 二级缓存内部结构

  • 二级就是由4部分构成
  • 类级别缓存
  • 集合级别缓存
  • 时间戳缓存
  • 查询缓存(二级缓存的第二大部分,三级缓存)

3.3. 并发访问策略

访问策略:读写型(read-write)、只读型(read-only)

3.4. 应用场景

适合放入二级缓存中的数据:很少被修改;不是很重要的数据,允许出现偶尔的并发问题。
不适合放入二级缓存中的数据:经常被修改;财务数据,绝对不允许出现并发问题;与其他应用数据共享的数据。

3.5. 二级缓存提供商

  • EHCache:可作为进程(单机)范围内的缓存,存放数据的物理介质可以是内存或硬盘,对 Hibernate的查询缓存提供了支持,支持集群。
  • OpenSymphony:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对 Hibernate的查询缓存提供了支持。
  • SwarmCache:可作为集群范围内的缓存,但不支持Hibernate的查询缓存。
  • JBossCache:可作为集群范围内的缓存,支持Hibernate的查询缓存。

3.6. 二级缓存的配置

  1. 导入jar包:ehcache-1.5.0.jar、commons-logging.jar、backport-util-concurrent.jar。

  2. 开启二级缓存,在hibernate.cfg.xml文件中:

  • <!-- 开启二级缓存 -->
  • <property name="hibernate.cache.use_second_level_cache">true</property>
  1. 确定二级缓存提供商,在hibernate.cfg.xml文件中:
  • <!-- 9.2 提供商 -->
  • <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
  1. 确定需要缓存内容
  • 配置需要缓存的类,在hibernate.cfg.xml确定类缓存配置项:
  • <!-- 类缓存 -->
  • <class-cache usage="read-write" class="com.coderap.a_init.Customer"/>
  • 配置需要缓存的集合,在hibernate.cfg.xml确定集合缓存配置项:
  • <!-- 集合缓存 -->
  • <collection-cache usage="read-write" collection="com.coderap.a_init.Customer.orders"/>
  • <!-- 集合缓存必须配合类缓存来使用,配置了orders集合,则需要配置Order类缓存 -->
  • <class-cache usage="read-write" class="com.coderap.a_init.Order"/>
  • 配置查询缓存,在hibernate.cfg.xml确定查询缓存配置项:
  • <!-- 开启查询缓存
  • hibernate.cache.use_query_cache true
  • -->
  • <property name="hibernate.cache.use_query_cache">true</property>
  1. 配置ehcache自定义配置文件
  • 步骤1:从ehcache-1.5.0.jar包复制ehcache-failsafe.xml文件;
  • 步骤2:将该文件重命名ehcache.xml;
  • 步骤3:将修改后的文件,拷贝到src下。

3.7. 演示

3.7.1. 类缓存

  • @Test
  • // 演示:类缓存
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer customer1 = (Customer) session.get(Customer.class, 1);
  • session.clear();// 清空一级缓存中的内容
  • Customer customer2 = (Customer) session.get(Customer.class, 1);
  • System.out.println(customer1 == customer2);// false,说明二级缓存在缓存数据时,并不是以对象的形式缓存;缓存的是对象数据的散列,每次从二级缓存取数据会在一级缓存中组装成对象
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

3.7.2. 集合缓存

  • @Test
  • // 演示:集合缓存
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer customer1 = (Customer) session.get(Customer.class, 1);
  • for (Order o : customer1.getOrders()) {
  • System.out.println(o.getName());
  • }
  • session.clear(); // 清空一级缓存
  • Customer customer2 = (Customer) session.get(Customer.class, 1);
  • Iterator<Order> it = customer2.getOrders().iterator();
  • while (it.hasNext()) {
  • Order o = it.next();
  • System.out.println(o.getName());
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

3.7.3. 查询缓存

查询缓存又称为三级缓存,默认不使用,需要手动开启;查询缓存会将HQL语句与查询结果进行绑定。通过HQL相同语句可以缓存内容。默认情况Query对象只将查询结果存放在一级和二级缓存,不从一级或二级缓存获取,查询缓存就是让Query可以从二级缓存获得内容。

  • @Test
  • // 演示:查询缓存
  • // 对HQL语句查询的缓存
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Query query = session.createQuery("from Customer");
  • // 使用二级(查询)缓存
  • // 查询时,会先从二级缓存中取结果,取不到就执行语句,将结果放入二级查询缓存中
  • query.setCacheable(true);
  • List<Customer> list = query.list();
  • session.clear();
  • Query query2 = session.createQuery("select c from Customer c");
  • query2.setCacheable(true);
  • List<Customer> list2 = query2.list();
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

3.7.4. 时间戳缓存

  • @Test
  • // 演示:时间戳
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer customer1 = (Customer) session.get(Customer.class, 1);// 获取时间戳
  • session.createQuery("update Customer set name=:name where id = :id ").setString("name", "rose").setInteger("id", 1).executeUpdate();// 更改了状态,更新时间戳
  • session.clear();
  • Customer customer2 = (Customer) session.get(Customer.class, 1); // 因为两次时间戳不一样,需要重新查询更新数据
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

4. ehcache配置文件

diskStore path="java.io.tmpdir"/>设置临时文件存放位置。(缓存一般内存,一定程度时,写入硬盘。)

1. 缓存详细设置

<defaultCache>所有的缓存对象默认的配置
<cache name="类">指定对象单独配置

  1. 参数设置
  • maxElementsInMemory="10000":内存最大数
  • eternal="false":是否永久(内存常驻留)
  • timeToIdleSeconds="120":对象在内存中最多空闲的秒数
  • timeToLiveSeconds="120":对象在内存中最多存活的秒数
  • overflowToDisk="true":内存过载,是否写入到硬盘
  • maxElementsOnDisk="10000000":过载后硬盘使用的最大对象数量
  • diskPersistent="false":关闭JVM,是否将内存保存硬盘中
  • diskExpiryThreadIntervalSeconds="120":轮询时间间隔
  • memoryStoreEvictionPolicy="LRU":当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略,有以下取值:

    • Least Recently Used (specified as LRU)
    • First In First Out (specified as FIFO)
    • Less Frequently Used (specified as LFU)
  • maxElementsInMemory:设置基于内存的缓存中可存放的对象最大数目;

  • eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSecondstimeToLiveSeconds属性; 默认值是false
  • timeToIdleSeconds:设置对象空闲最长时间,以秒为单位,超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态;
  • timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中。该属性值必须大于或等于timeToIdleSeconds属性值;
  • overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中;
  • diskPersistent:当JVM结束时是否持久化对象,默认是false
  • diskExpiryThreadIntervalSeconds:指定专门用于清除过期对象的监听线程的轮询时间;
  • memoryStoreEvictionPolicy:当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) 。