JPQL&事务并发(乐观锁)

JPQL

Jpql查询语言:

与sql语言具有相同的特征,但是JPQL是完全面向对象的的,具有继承、多态等特性,而sql是面向数据库的

Jpql语言注意点:

1.Jpql语句中不能出现表名以及列名,要以实体类名,属性名作为查询条件,且区分大小写
2.Jpql关键字与sql中的关键字一样,且不分大小写
3.查询时不能写select * 要写select 别名(当前别名为实体类名的别名)

Jpql支持聚合查询:

AVG()、SUM()、 COUNT()、MAX()、 MIN()

Jpql使用原生sql语法:

通过EntityManager(实体管理)对象的createNativeQuery(创建原生查询)()方法执行sql语法

在默认的查询中,一的一端采用立即加载,多的一端采用懒加载

事务

事务的四个特性(ACID)

原子性:事务必须是原子工作单元,对于数据的操作,要么全都执行,要么都不执行
一致性:事务完成时,必须所有的数据保持一致状态
隔离性:由事务并发所作的修改必须与任何其它并发事务所作的修改隔离
持久性:事务完成后,它对于系统的影响是永久性

事务并发

为了获得更好的运行性能,各种数据库都允许多个事务同时运行,这就是事务并发。

隔离机制

当并发的事务访问或者修改数据库中同一行同一列的数据时,就要采取必要的隔离机制,实现隔离机制就需用锁

事务并发带来的问题

JPA只能处理第一、二类丢失更新,其他3种必须由数据库自己处理

Jpa第一类丢失更新(体现在秒杀场景):

库存是1件
当事务A和事务B同时修改某行的值,
1.事务A将数值改为0并提交,购买了一件
2.事务B将数值改为0并提交,也购买了一件。这时数据的值为0,事务A所做的更新将会丢失。(相当于就卖出去2件商品)
解决办法:对行加锁,只允许并发一个更新事务。(JPA中的悲观锁,乐观锁)

脏读

1.张三的原工资为4000, 财务人员将张三的工资改为了8000(但未提交事务)
2.张三读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!(在缓存中读取)
3.而财务发现操作有误,回滚了事务,张三的工资又变为了4000 像这样,张三记取的工资数8000是一个脏数据。
解决办法:如果在第一个事务提交前,任何其他事务不可读取其修改过的值,则可以避免该问题。

虚读(幻读)

目前工资为4000的员工有10人。
1.事务1,读取所有工资为4000的员工。
2.这时事务2向employee表插入了一条员工记录,工资也为4000
3.事务1再次读取所有工资为4000的员工共读取到了11条记录,
解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题。

不可重复读

在一个事务中前后两次读取的结果并不致,导致了不可重复读。
1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000
解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。

Jpa第二类丢失更新

多个事务同时读取相同数据,并完成各自的事务提交,导致最后一个事务提交会覆盖前面所有事务对数据的改变

悲观锁

如果使用了悲观锁(加了一个行锁),如果事务没有被释放,就会造成其他事务处于等待,一般不使用悲观锁

乐观锁

给字段添加一个版本号或者事件戳

// 添加一个私有字段Integer version,不由程序员维护,由JPA自己维护
  @Version
  private Integer version;

乐观锁体现场景:商品秒杀
设置某商品库存1,代码如下:

// 事务操作流程:先查询,获取库存,在购买,再更新
// 事务1,事务2交替运行
@Test
public void update2() throws Exception {
 try {
   EntityManager entityManager1 = JPAUtils.getEntityManager();
   EntityManager entityManager2 = JPAUtils.getEntityManager();
   entityManager1.getTransaction().begin();
   entityManager2.getTransaction().begin();
   Product product1 = entityManager1.find(Product.class, 1L);
   Product product2 = entityManager2.find(Product.class, 1L);
   product2.setNum(product2.getNum() - 1);
   entityManager1.merge(product2);
   product1.setNum(product.getNum() - 1);
   entityManager2.merge(product);
   entityManager1.getTransaction().commit();
   entityManager2.getTransaction().commit();
     entityManager1.close();
   entityManager2.close();
 } catch (Exception e) {// 乐观锁异常
   System.out.println("商品已经抢完了!");
 }
}

JPA优化

  1. 使用双向一对多关联,不使用单向一对多–效率高一点
  2. 灵活使用单向多对一关联 --多方来维护外键效率高一点
  3. 不用一对一,用多对一取代(不要使用共享主键一对一,使用唯一外键一对一)
  4. 配置对象二级缓存(配置 Cacheable),查询缓存(jpql查询),
    没有查询条件才使用查询缓存(如果有条件命中是很低)
    查询缓存:
    同一个EntityManagerFactory 不同entityManager 发送jpql相同和条件值相同
  5. 组合关系集合使用list(顺序,重复)–第二个项目 单据模块,多对多集合使用set
  6. 设计表的时候,表字段要少,表关联不要怕多,有二级缓存撑腰,设计表尽量达到第三范式(外键)
    数据库范式:数据库规则
    1NF:设计的时候,尽量保证 表里面列不能存放多个值,列最小单元,不能分割 --体系列原子性
    2NF:表里面每一行数据都应该有一个唯一能够区分的值,建议你都创建主键
    3NF:表里面不应该存放另外一个表非主键的信息 --建议你一个外键
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值