JPA的进阶

一JPA的主键的生成策略

主键:
是能够进行区分表里面的每一行的数据的
主键有这非空且唯一的特点
主键的分类:
1.自然主键:
是具有实际意义的列来做为主键的
2.代理主键:
是用的没有实际意义的列来作为主键的–是单体项目
什么是主键的生成策略
jpa采取的方案方式去生成主键
jps:
1. auto策略 ,这种方法是默认使用的:根据数据库的方式来进行选择的是到底使用哪 种策略就比如,配置mysql的方式 策略 mysql的策略是自增策略(所以一般都喜欢用mysql)
配置oracle方式 策略 序列sequeence策略 这个很多都是不会用的主要是这个是收费的!
create sequece s_employee;
​ insert into table(id,name,…) values(s_employee.nextVal())
2.table策略:
这个是表生成的策略,是额外创建一张表来维护主键

SEQUENCE_COUNTSEQUENCE_NAME
2t_employee

3.sequence策略:这是序列策略
4.identity策略:mysql采用的就是这种自增策略

二.JPA的状态

1.什么叫做JPA的状态:
一个实体交给jpa维护的时候,这个实体在不同的时期,状态不一样

(1)临时(瞬时)New:刚刚创建出来。没有和jpa进行发生关系

(2)==持久(托管)==Managed:这个实体对象已经和jpa发生关系

(3)游离(脱管)Detached:脱离entityManager的管理,已经被持久化,不存在的entityManager里面

(4)删除Remove:即将要删除的时候,remove
2.脏数据
1.脏数据:
脏数据(Dirty Read)是指源系统中的数据不在给定的范围内或对于实际业务毫无意义,或是数据格式非法,以及在源系统中存在不规范的编码和含糊的业务逻辑。
2.脏数据的更新*
一个持久化状态数据,修改非主键的值,会在commit提交的时候,自动会发送sql去更新
3.n-to-n问题
一个持久化状态的数据,如果修改主键的之后,就会报错,也就是说,持久化状态的数据,不能改变主键的值
4.实体的类定义规则
1、属性要是私有的。
    在这里插入图片描述
  2、要有公开的setter和getter方法供外界访问和修改。
    
在这里插入图片描述

3、每一个实体类要有一个属性作为唯一值(一般都是使用对于数据表的主键)。
    
在这里插入图片描述

4、建议数据类型不要使用基本的数据类型,而是使用相应的包装类。
    在这里插入图片描述

5、Java中对应的包装类

基本数据类型对应包装类
intInteger
charCharacter
booleanBoolean
floatFloat
doubleDouble
shortShort
longLong
byteByte

使用包装类的原因:因为有时候使用基本数据类型无法准确表示一些场景。

例:score代表学生的成绩,但是在考试时可能存在缺考的情况,如果使用int数据类型,则score = 0表示考了分,但是缺考还是score =0,这样无法区分;如果使用Integer,则要是缺考,score = null;这样就可以区分是考了0分还是缺考。

三域对象之间的关系

1.域对象之间的关系
对象和对象之间,存在一定关系存在什么关系?
主要是关联关系的细化需要注意强弱,由若到强分别是 依赖 < 关联 < 聚合 < 组合
泛化(generalization),关联(association),依赖(dependency),实现(realization)

泛化(generalization)关系时指一个类(子类、子接口)继承另外一个类(称为父类、父接口)的功能,并可以增加它自己新功能的能力,继承是类与类或者接口与接口最常见的关系,在Java中通过关键字extends来表示。
在这里插入图片描述

实现(realization)是指一个class实现interface接口(一个或者多个),表示类具备了某种能力,实现是类与接口中最常见的关系,在Java中通过implements关键字来表示。

在这里插入图片描述

依赖(dependency)关系也是表示类与类之间的连接,表示一个类依赖于另外一个类的定义,依赖关系时是单向的。简单理解就是类A使用到了类B,这种依赖具有偶然性、临时性,是非常弱的关系。但是类B的变化会影响到类A。举个例子,如某人要过河,则人与船的关系就是依赖,人过河之后,与船的关系就解除了,因此是一种弱的连接。在代码层面,为类B作为参数被类A在某个方法中使用。

在java中,依赖表现为:局部变量,方法中的参数和对静态方法的调用。

在这里插入图片描述

关联(association)关系表示类与类之间的连接,它使得一个类知道另外一个类的属性和方法。

关联可以使用单箭头表示单向关联,使用双箭头或者不适用箭头表示双向关联,不建议使用双向关联,关联有两个端点,每个端点可以有一个基数,表示这个关联的类可以有几个实例。

0…1 表示可以有0个或者1个实例

0…* 表示对实例的数目没有限制

1 表示只能有一个实例

1…* 表示至少有一个实例

关联关系体现的是两个类,或者类与接口之间的强依赖关系,这种关系很强烈,比依赖更强,不是偶然性的,也不是临时性的,而是一种长期性,相对平等的关系,表现在代码层面,为被关联的类B以类属性的形式出现在类A中,也可能是关联类A引用了被关联类B的全局变量。

在Java中,关联关系是使用实例变量来实现的

在这里插入图片描述

聚合(aggregation)是关联关系的特例,是强的关联关系,聚合是整个与个体的关系,即has-a关系,此时整体和部分是可以分离的,他们具有各自的生命周期,部分可以属于多个对象,也可以被多个对象共享;比如计算机和CPU,公司与员工的关系;在代码层面聚合与关联是一致的,只能从语义上来区分。

聚合关系也是使用实例变量来实现的,在java语法上区分不出关联和聚合,关联关系中类出于一个层次,而聚合则明显的在两个不同的层次。
在这里插入图片描述

组合(compostion)也是关联关系的一种特例,体现的是一种contain-a关系,比聚合更强,是一种强聚合关系。它同样体现整体与部分的关系,但此时整体与部分是不可分的,整体生命周期的结束也意味着部分生命周期的结束,反之亦然。如大脑和人类。

体现在代码层面与关联时一致的,只能从语义来区分。

组合与聚合几乎完全相同,唯一区别就是对于组合,“部分”不同脱离“整体”单独存在,其生命周期应该是一致的。

在这里插入图片描述
2.单向多对一
1.基本配置
Product

@Entity
@Table(name="t_product")
public class Product {
    //主键
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToOne//多对一
    @JoinColumn(name = "dir_id")//指定外键名称
    private ProductDir dir ;
     set/get方法
    }

ProductDir

@Entity
@Table(name="t_productDir")
public class ProductDir {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
   	get/set方法
   }

2.保存

先保存一方 在保存多方—少发送sql

 		entityManager.persist(dir);
 		entityManager.persist(p1);
        entityManager.persist(p2);

3.JPA的抓取策略
抓取策略:

​ JPA告诉程序使用哪种方式去获取数据

抓取策略配置:

​ @ManyToOne(fetch = FetchType.LAZY ) 表示懒加载 – 需要使用的时候,才发送sql查询

​ 类不要定义成final

@ManyToOne(fetch = FetchType.EAGER) 表示迫切加载 – 不使用都把数据加载出来 发送左外连接查询数据
4.二级缓存
缓存作用:牺牲内存空间去换取时间

一级缓存:属于EntityManager级别的缓存 ,它是自带的

​ 命中条件:

​ 同一个EntityManagerFactory 同一个EntityManager 同一个OID

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

5.查询缓存
查询缓存方面主要涉及两个参数,query_cache_size用于设置MySQL的查询缓存(query cache)大小,query_cache_type用于设置使用查询缓存的类型,
可以用以下命令查看具体使用情况:

mysql> show global status like 'qcache%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 1         |
| Qcache_free_memory      | 195024544 |
| Qcache_hits             | 0         |
| Qcache_inserts          | 3         |
| Qcache_lowmem_prunes    | 0         |
| Qcache_not_cached       | 121       |
| Qcache_queries_in_cache | 1         |
| Qcache_total_blocks     | 4         |
+-------------------------+-----------+

8 rows in set (0.00 sec)

相关参数解释如下:
Qcache_free_blocks:缓存中相邻内存块的个数。数目大说明可能有碎片。flush query cache会对缓存中的碎片进行整理,从而得到一个空闲块。
Qcache_free_memory:缓存中的空闲内存。
Qcache_hits:多少次命中,通过这个参数可以查看query cache的基本命中效果。
Qcache_inserts:每次插入一个查询时就增大,命中次数除以插入次数就是非命中率。
Qcache_lowmem_prunes:多少条query因为内存不足而被清理出query cache,通过Qcache_lowmem_prunes和Qcache_free_memory参数相互结合,能够
更清楚地了解到系统中query cache的内存大小是否真的足够,是否非常频繁的出现因为内存不足而有query被换出的情况。
Qcache_not_cached:不适合进行缓存的查询数量,通常是由于这些查询不是SELECT语句或用了now()之类的函数。
Qcache_queries_in_cache:当前缓存的查询和响应数量。
Qcache_total_blocks:缓存中块的数量。

关于query_cache的配置:
mysql> show variables like 'query_cache%';
+------------------------------+-----------+
| Variable_name                | Value     |
+------------------------------+-----------+
| query_cache_limit            | 1048576   |
| query_cache_min_res_unit     | 4096      |
| query_cache_size             | 195035136 |
| query_cache_type             | ON        |
| query_cache_wlock_invalidate | OFF       |
+------------------------------+-----------+
5 rows in set (0.00 sec)

各个字段的解释如下:
query_cache_limit:超过此大小的查询将不再缓存。
query_cache_min_res_unit:缓存块的最小值。
query_cache_size:缓存大小值
query_cache_type:缓存类型,决定缓存什么样的查询。
query_cache_wlock_invalidate:表示当有其它客户端正在对MyISAM表进行写操作时,读请求是要等write lock释放资源后再查询还是允许直接
从query cache中读取结果,默认为FALSE
查询缓存碎片率=Qcache_free_blocks/Qcache_total_blocks * 100%
查询缓存利用率=(query_cache_size - Qcache_free_memory)/query_cache_size * 100%
查询缓存利用率在25%以下,说明query_cache_size设置的过大,可适当减小。
查询缓存命中率=(Qcache_hits - Qcache_inserts) / Qcache_hits * 100%

四.缓存的理论

缓存是当今各种软件或者硬件系统中不可缺少的技术之一,所以对每个程序员来说都显得异常重要,对ahuaxuan来说亦是如此。

1缓存为什么要存在?
2缓存可以存在于什么地方?
3缓存有哪些属性?
4缓存介质?

搞清楚这4个问题,那么我们就可以随意的通过应用的场景来判断使用何种缓存了.

下面ahuaxuan和大家一一分析这4个问题.

  1. 缓存为什么要存在?
    一般情况下,一个网站,或者一个应用,它的一般形式是,浏览器请求应用服务器,应用服务器做一堆计算后再请求数据库,数据库收到请求后再作一堆计算后把数据返回给应用服务器,应用服务器再作一堆计算后把数据返回给浏览器.这个是一个标准流程.但是随着互连网的普及,上网的人越来越多,网上的信息量也越来越多,在这两个越来越多的情况下,我们的应用需要支撑的并发量就越来越多.然后我们的应用服务器和数据库服务器所做的计算也越来越多,但是往往我们的应用服务器资源是有限的,数据库每秒中接受请求的次数也是有限的(谁叫俺们的硬盘转速有限呢).如果利用有限的资源来提供尽可能大的吞吐量呢,一个办法:减少计算量,缩短请求流程(减少网络io或者硬盘io),这时候缓存就可以大展手脚了.缓存的基本原理就是打破上图中所描绘的标准流程,在这个标准流程中,任何一个环节都可以被切断.请求可以从缓存里取到数据直接返回.这样不但节省了时间,提高了响应速度,而且也节省了硬件资源.可以让我们有限的硬件资源来服务更多的用户.

2 缓存可以存在于什么地方?

Java代码 复制代码 收藏代码
浏览器—浏览器和app之间—分过层的app-数据库
Java代码 收藏代码
浏览器—浏览器和app之间—分过层的app-数据库

在上图中,我们可以看到一次请求的一般流程,下面我们重新绘制这张图,让我们的结构稍微复杂一点点.
(将app分层)
浏览器—浏览器和app之间—分过层的app-数据库

理论上来将,请求的任何一个环节都是缓存可以作用的地方.第一个环节,浏览器,如果数据存在浏览器上,那么对用户来说速度是最快的,因为这个时候根本无需网络请求.第二个环节,浏览器和app之间,如果缓存加在这个地方,那么缓存对app来说是透明的.而且这个缓存中存放的是完整的页面.第三个节点,app中本身就有几个层次,那么缓存也可以放在不同的层次上,这一部分是情况或者场景比较复杂的部分.选择缓存时需要谨慎.第四个环节,数据库中也可以有缓存,比如说mysql的querycache.

那么也就是说在整个请求流程的任何一点,我们都可以加缓存.但是是所有的数据都可以放进缓存的吗.当然不是,需要放进缓存的数据总是有一些特征的,要清楚的判断数据是否可以被缓存,可以被怎样缓存就必须要从数据的变化特征下手.

数据有哪些变化特征?最简单的就是两种,变和不变.我们都知道,不会变化的数据不需要每次都进行计算.问题是难道所有的数据理论上来讲都会变化,变化是世界永恒的主题.也就是说我们把数据分为变和不变两种是不对的,那么就让我们再加一个条件:时间.那么我们就可以把数据特征总结为一段时间内变或者不变.那么根据这个数据特征,我们就可以在合适的位置和合适的缓存类型中缓存该数据.

3缓存有哪些属性
从面向对象的角度来看,缓存就是一个对象,那么是对象,必然有属性.那么下面我们来探讨一下缓存有哪些属性.以下列举我们常用到的3个属性.
(1) 命中率
命中率是指请求缓存次数和缓存返回正确结果次数的比例.比例越高,就证明缓存的使用率越高.

命中率问题是缓存中的一个非常重要的问题,我们都希望自己缓存的命中率能达到100%,但是往往事与愿违,而且缓存命中率是衡量缓存有效性的重要指标.

(2) 最大元素
缓存中可以存放得最大元素得数量,一旦缓存中元素数量超过这个值,那么将会起用缓存清空策略,根据不同的场景合理的设置最大元素值往往可以一定程度上提高缓存的命中率.从而更有效的时候缓存.

(3) 清空策略

1 FIFO ,first in first out ,最先进入缓存得数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去
2 LFU , Less Frequently Used ,一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存。
2 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

4缓存介质
从硬件介质上来将无非就是两种,内存和硬盘(对应应用层的程序来讲不用考虑寄存器等问题).但是往往我们不会从硬件上来划分,一般的划分方法是从技术上划分,可以分成几种,内存,硬盘文件.数据库.
(1) 内存.将缓存放在内存中是最快的选择,任何程序直接操作内存都比操作硬盘要快的多,但是如果你的数据要考虑到break down的问题,因为放在内存中的数据我们称之为没有持久话的数据,如果硬盘上没有备份,机器down机之后,很难或者无法恢复.

(2) 硬盘.一般来说,很多缓存框架会结合使用内存和硬盘,比如给内存分配的空间有满了之后,会让用户选择把需要退出内存空间的数据持久化到硬盘.当然也选择直接把数据放一份到硬盘(内存中一份,硬盘中一份,down机也不怕).也有其他的缓存是直接把数据放到硬盘上.

(3) 数据库.说到数据库,可能有的人会想,之前不是讲到要减少数据库查询的次数,减少数据库计算的压力吗,现在怎么又用数据库作为缓存的介质了呢.这是因为数据库又很多种类型,比如berkleydb,这种db不支持sql语句,没有sql引擎,只是key和value的存储结构,所以速度非常的快,在当代一般的pc上,每秒中十几w次查询都是没有问题的(当然这个是根据业务特征来决定的,如果您访问的数据在分布上是均匀的,那ahuaxuan可不能保证这个速度了).

除了缓存介质之外,ahuaxuan根据缓存和应用的耦合程度将其划分为local cache和remote cache.
Local cache是指包含在应用之中的缓存组件.而remote cache指和应用解耦在应用之外的缓存组件.典型的local cache有ehcache,oscache,而remote cache有大名鼎鼎的memcached.

Localcache最大的优点是应用和cache的时候是在同一个进程内部,请求缓存非常快速,完全不需要网络开销等.所以单应用,不需要集群或者集群情况下cache node不需要相互通知的情况下使用local cache比较合适.这也是java中ehcache和oscache这么流行的原因.
但是Local cache是有一定的缺点的,一般这种缓存框架(比如java中的ehcache或者oscache)都是local cache.也就是跟着应用程序走的,多个应用程序无法直接共享缓存,应用集群的情况下这个问题更加明显,当然也有的缓存组件提供了集群节点相互通知缓存更新的功能,但是由于这个是广播,或者是环路更新,在缓存更新频繁的情况下会导致网络io开销非常大,严重的时候会影响应用的正常运行.而且如果缓存中数据量较大得情况下使用localcache意味着每个应用都有一份这么大得缓存,着绝对是对内存的浪费.

所以这个情况下,往往我们会选择remote cache,比如memcached.这样集群或者分布式的情况下各个应用都可以共享memcached中的数据,这些应用都通过socket和基于tcp/ip协议上层的memcached协议直接连接到memcached,有一个app更新了memcached中的值,所有的应用都能拿到最新的值.虽然这个时候多了很多了网络上的开销,但是往往这种方案要比localcache广播或环路更新cache节点要普遍的多,而且性能也比后者高.由于数据只需要保存一份,所以也提高了内存的使用率.

通过以上分析可以看出,不管是local cache,还是remote cache在缓存领域都有自己的一席之地,所以ahuaxuan建议在选择或者使用缓存时一定要根据缓存的特征和我们的业务场景准确判断使用何种缓存.这样才能充分发挥缓存的功能.

Ahuaxuan认为,缓存的使用是架构师的必备技能,好的架构师能够根据数据的类型,业务的场景来准确的判断出使用何种类型的缓存,并且如何使用这种类型的缓存.在缓存的世界里也没有银弹,目前还没有一种缓存可以解决任何的业务场景或者数据类型,如果这种技术出现了,那架构师就又更不值钱了.呵呵.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值