JPA-01
- jpa介绍
1、什么是jpa?
jpa:Java Persistence API它就是orm的规范(对象关系映射(ORM) 规范)
2、jpa与Hibernate的关系
JPA它是一个orm规范,而hibernate是其最完美的实现,因为JPA和hibernate都是同一个作者
3、jpa与JDBC的优缺点
1.JPA它底层是基于jdbc实现的,它是面向对象的编程,如果你要操作crud,直接调用其api文档传递对象,它会自动生成对应的sql语句
优点:
1.操作非常方便
2.它给我们提供了缓存(1级缓存/2级缓存)
缺点:因为sql语句是自动生成,所以性能不好控制
2.JDBC它是操作数据库的唯一技术,它是各大数据库厂商实现的,我们在做crud的时候,必须手动编写sql语句
优点:性能好控制
缺点:操作太过于麻烦了
jdbc和jpa都不适合用于大型项目,只适用于小型项目,中大型项目还是使用mybatis来做
二、jpa程序及配置
1、使用jpa的步骤
1.导入jar包
2.配置jpa的核心文件
1).配置持久化单元
2).配置jpa的基本属性
1.用户名
2.密码
3.驱动
4.url地址
5.方言
6.是否显示sql语句
7.建表策略
3.创建domain
4.抽取EntityManagerFactory对象----》因为整个应用系统中,我们只需要一个该对象就可以了
核心方法:
添加: persist
修改: merge
删除: remove()只能传递对象
查询: find/query
2、ORM的概念
持久 英文即 persistence。就是把数据保存到可掉电式存储设备中。持久层就是dao层,原来叫数据访问层,现在一般叫持久层,是一个意思
Object Relational Mapping, 对象关系映射框架,ORM 就是通过将Java对象映射到数据库表,通过操作Java对象,就可以完成对数据表的操作
三、jpa的CRUD
1、提取工具类
public class JPAUtils {
// 保证EntityManagerFactory是单例
private static EntityManagerFactory entityManagerFactory;
static {
try {
// 对应配置文件里面的persistence-unit name="cn.itsource.jpa"
entityManagerFactory = Persistence.createEntityManagerFactory("cn.itsource.jpa");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("解析配置文件或者映射配置出现异常:" + e.getMessage());
}
}
public static EntityManager getEntityManager() {
return entityManagerFactory.createEntityManager();
}
}
2、jpa里面的CRUD方法
1.persist持久化方法,保存方法
2.merge方法,修改,必须要有id值
3.remove方法,删除,必须要有id值,先从数据库查出来,还要判断这个对象是否为空
4.查询方法可以不要事务,提高性能
5.find方法,通过主键获取domain对象,传入的参数一定要和domain的id的类型匹配
6.jpql是JPA的查询语言,hql是hibernate的查询语言
jpql:不能出现表名,列名,*,只能是Java的类名,属性名,别名
如果在jpql出现的sql关键字,大小写不敏感,Java的类名,属性名,别名大小写敏感
3、jpa的建表策略
1.create: 先删除表,再创建表 使用场景: 测试阶段,学习阶段
2.create-drop: 先删除表,再创建表,再删除表(EntityManagerFactory要关闭) 使用场景: 测试阶段,学习阶段
3.update: 只增不减(使用场景:真实开发就用它)
4.validate:校验,检查domain中的字段和表中的列是否对应的(个数,类型,名字) 校验标准是根据domain来判断的(使用场景:别人把表给你创建好了,你要根据表生成对应的domain)
四、jpa的核心对象
1、Persistence
主要负责创建EntityManagerFactory对象
2、EntityManagerFactory
它是一个重量级对象
1.二级缓存
2.连接池
3.管理着所有的doamin
4.解析xml,把xml里面的值都放到EntityManagerFactory中
5.它存储预定义jpql语句
使用EntityManagerFactory的规则,遵循1:1:1 1个项目对应一个EntityManagerFactory对应1个数据库
EntityManagerFactory的生命周期: 随着tomcat的启动而创建,tomcat关闭而销毁 EntityManagerFactory是线程安全的
3、EntityManager
它是一个轻量级对象
1.一级缓存
2.连接对象
EntityManager的生命周期: 应该随着你的访问而创建,访问结束之后再销毁 EntityManager是线程不安全的
4、Transactions事务对象
在同一个EnittyManager中拿到都是同一个事务对象
五、缓存
1、一级缓存
EntityManager的一级缓存
查询对象执行的原理: 通过oid在一级缓存中去查询对应的对象,如果没找到,则发送sql语句到数据库中进行查找,它会把查找的对象放到一级缓存中
如果在一级缓存中找到了,直接引用
一级缓存命中的条件
1.同一个oid
2.同一个EntityManager
3.同一个EntityManagerFactory
JPA-02
1、主键生成策略
1、主键介绍
主键是关系数据库中的一个基本概念,它用来保证记录的唯一性。简单来说,就是同一张数据库表中,不允许存在多条相同主键的记录
2、主键策略:
1、identity策略
表示主键自增(真实开发不用它,因为它兼容性不好)只是某些数据库支持 MySQL, SQL Server, DB2, Derby, Sybase, PostgreSQL MYSQL(推荐使用)
2、sequence策略
mysql是不支持的 但是ORACLE推荐使用(真实开发也不用它,因为兼容性不是特别好)
3、auto策略(默认就是它)
以后开发我们就用它,它会根据你当前数据库的方言来判断我应该使用identity策略还是sequence策略
4、table策略
实际开发不用它,因为性能太差了
优点:市场上所有的数据库都支持
特点: 它会单独创建一张表来维护主键
2、JPA持久对象的状态
1、为什么要学习状态?
学习状态的目的是让我们更好的分析jpa执行的顺序与流程,如果你没有学习状态,在你使用jpa的时候
你不好控制它的性能,你会觉得jpa很难用,而且会出现很多情况你解释不了
2、JPA提供了四种状态
1. 临时状态/瞬时状态: 对象new出来之后,还没有和entityManager产生关系的时候
2. 持久化状态/托管状态:该对象和entityManager产生了关系-----》它就会成为一个持久化状态(该对象必定有主键id)
3. 游离状态/脱管状态: 当entityManager关闭资源的时候,该对象就成为一个游离状态
4. 删除状态(JPA特有的): 当entityManager.remove()之后,该对象就是一个删除状态
3、脏数据更新
一个持久状态对象在事务管理内,如果改变原来的数据(非主键),此时出现脏数据,在事务提交的时候自动发出update语句去修改。
4、执行流程分析
第一步:拿到entityManager,开启事务
第二步:通过entityManager拿到一个对象,那么现在这个对象就是持久化的对象,这个对象会放到一级缓存里面,JPA会为当前这个对象准备一个快照(把这个对象进行了备份)
第三步:提交事务
它会把快照 与 你现在这个对象的数据进行对比,如果相同,就不需要修改,也不会发送SQL(性能就高了)
当不相同的时候,JPA就会认为现在这个数据是脏数据
脏数据它就会在事务提交的时候,把它进行数据库的同步(发送update SQL语句)
5、删除状态是JPA特有的
6、持久对象(domain层)定义规则:
1.类不能定义为final类,因为domain类需要被继承的,否则延迟加载会失效
2.所有属性的类型都必须是包装类型
3.不能是8个基本类型
(int,byte,short,long,char,boolean,float,double)
因为JPA内部代码很多判断都是基于是否等于null
4.必须有默认无参构造方法
因为find方法获取的时候会在内存实例化domain对象,否则报org.hibernate.InstantiationException: No default constructor for entity异常
7、entityManager的方法改变持久对象的状态
3、域对象(domain对象)之间的关系
1、依赖关系
JavaBean之间的依赖关系
分层:表现层,业务层,持久层(依赖关系)
依赖:一般指controll,service,dao层之间的关系
类之间访问关系。无需定义成属性。在A中访问B中的方法或属性,或者A负责实例化B。
xxxService{
IXxxDao xxxDao = new XxxDaoImpl();//原来
使用Spring之后,通过set方法依赖注入xxxDao的实例
(对象实例化交给spring完成)
IXxxDao xxxDao ;
public void setXxxDao(IXxxDao xxxDao){
this.xxxDao=xxxDao;
}
}
Controller表现层依赖于Service业务层,Service依赖于Dao持久层
2、关联关系
类之间的引用关系,以属性定义的方式表现。
关联按照多重性可分为一对一、一对多、多对一和多对多。 主要指的数据库(类)的关系
按照导航性可分为单向关联和双向关联。
主要指的java类的导航,导航可以从一个类获取另一个的类的实例
员工和部门是什么关系? 多个员工对应一个部门 多对一
一个类的对象与它的属性之间的关系
-------------------------------------------------------------------------
注意:关系的判定一定要根据一般的情况,而不是特殊的情况
注:我们讲的是关系是域对象的关系,与数据库无关
一对一 : QQ(主一)与QQ空间(从一),一夫一妻,一个人一个身体证
多对一(反过来就是一对多) :多个员工都是一个老板 , 多把钥匙对于一把锁,一个部门多个员工,一个产品类型有多个产品
多对多:老师与学生,司机与公交车,用户与角色
注意:
不管理一对多,还是多对多(不管它们是双向还是单向),数据库的结构不变(多方有外键)
多对多:必有一张中间表
一对一:设计方案(共享主键,唯一外键),主从关系
JPA-03
1、域对象(domain对象)之间的关系(补充)
1、聚合关系(本质还是双向多对一,一对多)
表示整体与部分的关系,整体和部分可以分开单独存在。
电脑(主板,CPU,IO,内存条)
2、组合关系(本质还是聚合关系,双向多对一,一对多)
强聚合关系,整体和部分之间不能分开。如人(头,手),订单模型,超市购物购物清单等等
有的设计,是一定要在强聚合关系下才使用
2、fetch抓取策略
1、即时加载
FetchType.EAGER:立即加载,一般情况下的默认值
2、懒加载
FetchType.LAZY:延迟加载,需要的时候才发出sql
3、二级缓存
1、导包
添加二级缓存jar文件
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.8.Final</version>
</dependency>
2、配置
persistence.xml配置
<!-- 启用二级缓存 -->
<property name="hibernate.cache.use_second_level_cache" value="true" />
<!-- 二级缓存的实现类,文档里面的包名有错的 -->
<!-- 错误 org.hibernate.cache.internal.EhCacheRegionFactory -->
<!-- 正确的 org.hibernate.cache.ehcache.EhCacheRegionFactory -->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
<!-- 启用查询缓存 -->
<property name="hibernate.cache.use_query_cache" value="true" />
3、在<properties>上面添加配置二级缓存扫描的策略
<!-- ALL:所有的实体类都被缓存 -->
<!-- NONE:所有的实体类都不被缓存. -->
<!-- ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存 -->
<!-- DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类 -->
<!-- UNSPECIFIED:默认值,JPA 产品默认值将被使用 -->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
4、domain类的二级缓存
二级缓存类配置:命中条件:同一个SessionFactory,不同的entityManager,OID相同
// 一条sql
// @Entity
// @Cacheable(true)
// public class Department{
@Test
public void test2() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
//先从一级缓存,二级缓存取数据?取不到,发出sql去获取,填充一级缓存,二级缓存
Department department1 = entityManager.find(Department.class, 1L);
//先从一级缓存,二级缓存取数据?一级缓存取到了,返回
Department department2 = entityManager.find(Department.class, 1L);// 一级缓存命中
entityManager.close();
EntityManager entityManager2 = JPAUtils.getEntityManager();
//先从一级缓存,二级缓存取数据?一级缓存没有取到,但是二级缓存取到了,返回
Department department3 = entityManager2.find(Department.class, 1L);// 二级缓存命中
//先从一级缓存,二级缓存取数据?一级缓存取到了,返回
Department department4 = entityManager2.find(Department.class, 1L);// 一级缓存命中
entityManager2.close();
}
}
4、各个关系介绍
1、单向一对多
注意的点:
关联的集合先new出来,方便操作
声明必需使用接口
判断有没有关系对象,判断集合的size
List,Set(区别:List是有序的,Set是去重),组合关系使用List
单向的一对多性能太差,尽量不要使用
必须配置外键id,否则会多生成一张表,形成多对多的关系
2、单向多对一
注意的点:
配置懒加载 @ManyToOne(fetch=FetchType.LAZY)
关联对象不能先new出来
判断有没有关系对象:直接判断对象是否为空
先保存一方,再保存多方
3、双向一对多/多对一
注意的点:
多对一 + 一对多 = 双向
名称必需一致(边外键的列名必需一致,放弃维护后一方不需要加列名)
一方放弃着关系的维护 @OneToMany(mappedBy="productDir")
4、多对多
注意的点:
多一张中间表
如果是双向的话,保存表名与列名都一致
domain的类toString不用加关联的对象,不然在级联删除时会出现递归现象
5、一对一
注意的点:
唯一外键一对一(建议使用)
主表:放弃关系的维护
从表:关系维护的字段(外键)
共享主键一对一(不键使用,jpa不支持,hiberante支持)
6、特别注意
注意:
1.多方默认就是LAZY
2.类不要使用final
3.关联对象在toString不要打印出来
4.所有属性的类型都必须是包装类型
在表中:
1.有外键就是多对一,有外键的这一方就是多方
2.外键是唯一键就是一对一,有外键的这方就是从表
3.出来了中间表,就是多对多
7、级联
1.级联保存:
保存1方的时候,要把多方一并保存(不保证外检有值,但是有个前提:一方必须要关联多方)
如果外键要有值,多方必须要去关联1方
2.级联删除:
删除A方的时候,会把A方关联的对象全部删除(但是该功能不能做到,保留一方的同时去删除多方)
如果开发中你配置了级联删除,你可以配置最强级联
orphanRemoral=true:孤儿删除,既能保留一方也能删除多方
3.级联修改:一般不用
JPA-04
- JPQL
1.JPQL基本格式
1.只能写java的类名和属性名
SELECT o[o.property,o.property*] FROM Entity o
[WHERE conditions]
类------表
属性-----列名
[GROUP BY conditions]
[HAVING conditions]
[ORDER BY o.property[ASC|DESC]]
2.JPQL本质是JPA通过antlr-2.7.7.jar翻译成sql并且封装执行的
2.JPQL关键点
1. JPQL和SQL很像,查询关键字都是一样的
2. 唯一的区别是:JPQL是面向对象的
3.JPQL规则
1.JPQL两大准则:
1、如果要连表查询,不是关联的表,而是关联的属性
2、不需要加on,它会自动消除笛卡尔积
2.需要注意的点:
1.里面不能出现表名,列名,只能出现java的类名,属性名,区分大小写
2.出现的sql关键字是一样的意思,不区分大小写
3.不能写select * 要写select 别名
4.JPQL查询
1.简单查询
1.查询所有:String jpql = "FROM Employee";
2.查询特定属性:String jpql = "select o.name,o.depatment FROM Employee o";
3.根据条件查询:String jpql = "from Employee where department.city=? or department.city=?";
4.根据条件排序: "from Employee order by salary desc(dept.cn,相关联类的字段)";//desc是降序排列,默认为升序
5.根据in的条件查询:"from Employee where department.street in(?,?)";//in中的条件可以多个,表示或的意思
6.查询范围:"from Employee where salary between ? and ?";// 相当于 salary>=? and salary<=? 闭区间
7.模糊查询:"from Employee where name like ? or name like ?";
?都可以根据方法query.setParameter(1, "%er%").setParameter(2, "%en%")使用
2.去重
distanct:String jpql = "select distinct o.department from Employee o";
3.JPQL集合操作
4.JPQL中join操作
5.JPQL聚集函数
6.JPQL分页查询
1、假分页
2、真分页
2、原生SQL用于JPA
3、事务并发(乐观锁)
1.并发问题
1.第一类数据丢失
2.第二类数据丢失
3.脏读
4.虚读
5.不可重复读
2.数据库隔离级别
MySql默认隔离级别为:Repeatable Read(可能会出现虚读)
Oracle 支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别
默认隔离级别为READ COMMITTED
READ UNCOMMITTED 幻想读、不可重复读和脏读都允许。
READ COMMITTED 允许幻想读,不可重复读,不允许脏读 只给你处理了脏读(oracle默认隔离级别)
REPEATABLE READ 允许幻想读,不允许不可重复读和脏读(mysql) 它给你处理了不可重复读和脏读
SERIALIZABLE 幻想读、不可重复读和脏读都不允许 把这三个问题都给你处理好了
3.悲观锁
session.get(Product.class, 1L, LockOptions.UPGRADE);一般不使用
4..乐观锁
使用版本version,主要用于防止第二类数据丢失