JPA进阶_day02

本文详细介绍了JPA的主键生成策略,包括auto、table、sequence和identity策略,并探讨了JPA实体的状态管理,如临时、持久、游离和删除状态。此外,还讨论了JPA中的一级缓存、二级缓存配置及其使用场景,以及域对象之间的关联关系和fetch策略。最后,阐述了缓存理论,包括缓存命中条件和何时使用二级缓存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 JPA主键生成策略

1.1 主键介绍

含义:主键是关系数据库中的一个基本概念,它用来保证记录的唯一性。简单来说,就是同一张数据库表中,不允许存在多条相同主键的记录
特点:非空且唯一
主键分类:

  1. 自然主键 具有实际意义的列来作为主键
  2. 代理主键 没有实际意义的列来作为主键

1.2 JPA标准策略有4种:

  1. auto策略
    根据数据库方言来选择的到底使用哪种策略
  2. table策略
    表的生成策略 额外创建一张表来维护主键
  3. sequence策略
    序列策略
  4. identity策略
    自增策略

2 JPA的状态

2.1 JPA状态的含义

一个实体交个JPA维护的时候 这个实体在不同的时期 状态也就不一样

  1. 临时状态(transient):瞬时状态 :
    刚刚创建出来 还没有和JPA发生关系
  2. 持久化状态(persistent):托管状态:
    这个实体对象已经和JPA发生关系
  3. 游离状态(detached):脱管状态:
    脱离entityManager 已经被持久化 不存在entityManager中
  4. 3.5.删除状态(removed):从JPA才开始有的状态:
    即将删除的时候

2.2 脏数据更新

一个持久状态对象在事务管理内,如果改变原来的数据(非主键),此时出现脏数据,在事务提交的时候自动发出update语句去修改

@Test
public void update() throws Exception {
  save();

  EntityManager entityManager = JPAUtils.getEntityManager();
  entityManager.getTransaction().begin();
  
  StateDomain stateDomain = entityManager.find(StateDomain.class, 1L);// 持久状态
  stateDomain.setName("stateDomain");
  // 写不写效果是一样
  entityManager.merge(stateDomain);// 持久状态

  entityManager.getTransaction().commit();
  entityManager.close();// 游离状态
}
2.2.1 执行流程分析

第一步
拿到entityManager,开启事务
第二步
通过entityManager拿到一个对象,那么现在这个对象就是持久化的对象
这个对象会放到一级缓存里面
JPA会为当前这个对象准备一个快照(把这个对象进行了备份)
第三步:提交事务
它会把快照 与 你现在这个对象的数据进行对比
如果相同,就不需要修改,也不会发送SQL(性能就高了)
当不相同的时候,JPA就会认为现在这个数据是脏数据
脏数据它就会在事务提交的时候,把它进行数据库的同步(发送update SQL语句)

2.3 n-to-n 问题

持久状态的对象是不能修改OID(不能修改主键)
报错信息:
org.hibernate.HibernateException: identifier of an instance of cn.itsource.jpa.state.StateDomain was altered from 1 to 200

2.4 持久对象(domain层)定义规则

  1. 类不能定义为final类,否则会延迟加载收到影响
  2. 所有属性的类型都必须是包装类型,因为JPA内部代码很多判断都是基于是否等于null,不能是8个基本类型(int,byte,short,long,char,boolean,float,double)
  3. 必须有默认无参构造方法,因为find方法获取的时候会在内存实例化domain对象

entityManager的方法改变持久对象的状态

在这里插入图片描述

3 域对象之间的关系在这里插入图片描述

3.1 域对象之间的关系

对象和对象之间 存在一定的关系

依赖关系
一般指controll,service,dao层之间的关系
Controller表现层依赖于Service业务层,Service依赖于Dao持久层

关联关系:
关联按照多重性可分为一对一、一对多、多对一和多对多。 主要指的数据库(类)的关系
按照导航性可分为单向关联双向关联
注意:
不管理一对多,还是多对多(不管它们是双向还是单向),数据库的结构不变(多方有外键)
多对多:必有一张中间表
一对一:设计方案(共享主键,唯一外键),主从关系

聚合关系(本质还是双向多对一,一对多)
整体和部分关系,整体和部分可以分开存在

组合关系(特殊一对多)
强聚合关系,整体和部分之间不能分开

3.2 单向多对一

3.2.1 基本配置

Product 类

@Entity
@Table(name = "t_product")
@Cacheable(true)
public class Product {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dir_id")//指定外键名称
    private ProductDir dir;
    get/set方法
}

ProductDir类

@Entity
@Table(name = "t_productDir")
public class ProductDir {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
     get/set方法
}
3.2.2 添加数据保存

性能差写法:先保存多方,再保存一方
正确写法先保存一方,再保存多方

@Test
public void testName()throws Exception{
	  //两个产品  一个分类
	  Product product = new Product();
	  product.setName("旺仔小馒头");
	  Product product1 = new Product();
	  product1.setName("大馒头");
	  ProductDir dir = new ProductDir();
	  dir.setName("食品");
	  product.setDir(dir);
	  product1.setDir(dir);
	  EntityManager entityManager = JUnit.getEntityManager();
	  entityManager.getTransaction().begin();
	  entityManager.persist(dir);
	  entityManager.persist(product);
	  entityManager.persist(product1);
	  entityManager.getTransaction().commit();
	  entityManager.close();
}

3.3 fetch抓取策略

抓取策略就是告诉JPA怎样发出sql获取关联对象
抓取策略配置:
@ManyToOne(fetch = FetchType.LAZY )表示懒加载 – 需要使用的时候,才发送sql查询
@ManyToOne(fetch = FetchType.EAGER) 表示迫切加载 – 不使用都把数据加载出来 发送左外连接查询数据

@Test
public void get() throws Exception {
	  EntityManager entityManager = JPAUtils.getEntityManager();
	  Product product = (Product) entityManager.find(Product.class, 1L);
	  System.out.println(product);
	  System.out.println("多方的外键==一方主键:" + product.getDir().getId());
	  // 延迟加载
	  // 通过javassist-3.18.1-GA.jar代理模式:ProductDir_$$_jvst960_1代理对象
	  System.out.println("一方的class:" + product.getClass());
	  System.out.println("一方的class:" + product.getDir().getClass());
	  //这里执行了SQL语句(因为使用到的类别的name在product表查询中没有,只能从数据库去查询)
	  System.out.println("一方非主键属性:" + product.getDir().getName());
	  entityManager.close();
}

3.4 二级缓存

一级缓存,不需要做任何配置,默认提供

3.4.1 添加二级缓存jar文件

hibernate-release-4.3.8.Final\lib\optional\ehcache\3个jar文件
在这里插入图片描述

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>4.3.8.Final</version>
</dependency>
3.4.2 添加persistence.xml配置信息
<!-- 启用二级缓存 -->
<property name="hibernate.cache.use_second_level_cache" value="true" />
<!-- 二级缓存的实现类,文档里面的包名有错的 -->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
<!-- 启用查询缓存 -->
<property name="hibernate.cache.use_query_cache" value="true" />
3.4.3 在上面添加配置二级缓存扫描的策略
<!-- ALL:所有的实体类都被缓存 -->
<!-- NONE:所有的实体类都不被缓存. -->
<!-- ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存 -->
<!-- DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类 -->
<!-- UNSPECIFIED:默认值,JPA 产品默认值将被使用 -->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

二级缓存:属于EntityManagerFactory级别缓存,它不是自带,如果要使用二级缓存,相应配置之后,才能使用二级缓存 — 通过Encache 框架来实现二级缓存

命中条件:同一个EntityManagerFactory,不同一个entityManager,发出的sql语句必须相同并且查询的条件值也要相同

  1. 如果查询缓存有查询条件,就不要使用查询缓存,因为命中率非常低
  2. 查询缓存里面查询指的是使用jpql,sql进行的查询,就是调用了createQuery方法,find方法不是走查询缓存
  3. 进行domain类的二级缓存的配置

4 缓存理论

4.1 什么时候使用二级缓存

  1. 读取大于修改;
  2. 对数据要有独享控制,数据不会被第三方修改;
  3. 可以容忍出现无效数据,非关键数据(不是财务数据等)
  4. 数据量不能超过内存容量,数据量特别巨大,此时不适合于二级缓存(钝化)

缓存淘汰策略
LRU(Least Recently Used):最近最少使用 --> 时间为参考点
LFU(Least Frequently Uesd):最不经常使用 -->次数为参考点
FIFO(First in First Out):先进先出 quene

4.2 缓存命中条件

4.2.1 一级缓存

同一个EntityManagerFactory, 同一个EntityManager, OID相同

4.2.2 Domain类的二级缓存配置

同一个EntityManagerFactory,不同一个EntityManager,OID相同
Domain类上面配置@Cacheable(true)

4.2.3 查询缓存

同一个EntityManagerFactory,不同一个EntityManager,发出的sql语句必须相同并且查询的条件值也要相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值