1 JPA主键生成策略
1.1 主键介绍
含义:主键是关系数据库中的一个基本概念,它用来保证记录的唯一性。简单来说,就是同一张数据库表中,不允许存在多条相同主键的记录
。
特点:非空且唯一
主键分类:
- 自然主键 具有实际意义的列来作为主键
- 代理主键 没有实际意义的列来作为主键
1.2 JPA标准策略有4种:
- auto策略
根据数据库方言来选择的到底使用哪种策略 - table策略
表的生成策略 额外创建一张表来维护主键 - sequence策略
序列策略 - identity策略
自增策略
2 JPA的状态
2.1 JPA状态的含义
一个实体交个JPA维护的时候 这个实体在不同的时期 状态也就不一样
- 临时状态(transient):瞬时状态 :
刚刚创建出来 还没有和JPA发生关系 - 持久化状态(persistent):托管状态:
这个实体对象已经和JPA发生关系 - 游离状态(detached):脱管状态:
脱离entityManager 已经被持久化 不存在entityManager中 - 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层)定义规则
- 类不能定义为final类,否则会延迟加载收到影响
- 所有属性的类型都必须是包装类型,因为JPA内部代码很多判断都是基于是否等于null,不能是8个基本类型(int,byte,short,long,char,boolean,float,double)
- 必须有默认无参构造方法,因为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语句必须相同并且查询的条件值也要相同
- 如果查询缓存有查询条件,就不要使用查询缓存,因为命中率非常低
- 查询缓存里面查询指的是使用jpql,sql进行的查询,就是调用了createQuery方法,find方法不是走查询缓存
- 进行domain类的二级缓存的配置
4 缓存理论
4.1 什么时候使用二级缓存
- 读取大于修改;
- 对数据要有独享控制,数据不会被第三方修改;
- 可以容忍出现无效数据,非关键数据(不是财务数据等)
- 数据量不能超过内存容量,数据量特别巨大,此时不适合于二级缓存(钝化)
缓存淘汰策略:
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语句必须相同并且查询的条件值也要相同