JPQL及事务并发

本文详细介绍了JPQL的基本概念、查询语句的格式和规则,包括简单查询、集合操作、JOIN和聚集函数等。同时,文章探讨了事务的ACID特性,分析了事务并发可能导致的问题,如脏读、幻读和不可重复读,并介绍了悲观锁和乐观锁的概念及其应用场景。最后,提出了优化JPA的建议。
摘要由CSDN通过智能技术生成

JPQL(JPA的查询语句) {#jpqljpa的查询语句 .1}

JPQL全称Java Persistence(持久化) Query Language

基于首次在EJB2.0中引入的EJB查询语言(EJB QL),Java持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。

其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。

1.1基础知识

1.1.1 最基本的JPQL的格式

只能写java的类名和属性名

SELECT o\[o.property,o.property\*\] FROM Entity o

\[WHERE conditions\]

\[GROUP BY conditions\]

\[HAVING conditions\]

\[ORDER BY o.property\[ASC|DESC\]\]

JPQL本质是JPA通过antlr-2.7.7.jar翻译成sql并且封装执行的。

1.1.2学JPQL记住两个点

1.JPQL和SQL很像,查询关键字都是一样的

2.唯一的区别是:JPQL是面向对象的

1.1.3 JPQL书写规则

JPA的查询语言,类似于sql

1.里面不能出现表名,列名,只能出现java的类名,属性名,区分大小写

2.出现的sql关键字是一样的意思,不区分大小写

3.不能写select * 要写select 别名,如:select e from Employee e;而不是select * form Employee。

1.2 简单查询

1.2.1查询所有员工

@Test

public void TestQ1()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e";

Query query = entityManager.createQuery(jpql);

List<Employee> resultList = query.getResultList();

for (Employee employee : resultList) {

System.out.println(employee);

}

entityManager.close();

}

1.2.2查询所有员工的姓名和所属部门名称【查询特定属性】

@Test

public void TestQ2()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e.name,e.department.name from Employee e";

Query query = entityManager.createQuery(jpql);

List<object[] >resultList = query.getResultList();

for (Object[] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.2.3查询出所有在成都和广州工作的员工【查询结果过滤】

@Test

public void TestQ3()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e where e.department.city like ? or e.department.city like ?";

Query query = entityManager.createQuery(jpql).setParameter(1, "%成都%").setParameter(2, "%广州%");

  List<Employee> resultList = query.getResultList();

for (Employee obj : resultList) {

System.out.println(obj);

}

entityManager.close();

}

1.2.4查询出所有员工信息,按照月薪排序【查询排序】

@Test

public void TestQ4()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e order by e.salary desc ";

Query query = entityManager.createQuery(jpql);

 List<Employee> resultList = query.getResultList();

for (Employee obj : resultList) {

System.out.println(obj);

}

entityManager.close();

}

1.2.5查询出所有员工信息,按照部门编号排序【使用关联对象属性排序】

@Test

public void TestQ5()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e order by e.department.sn ";

Query query = entityManager.createQuery(jpql);

   List<Employee>  resultList = query.getResultList();

for (Employee obj : resultList) {

System.out.println(obj.getDepartment().getSn()+"----"+obj);

}

entityManager.close();

}

1.2.6查询出在恩宁路和八宝街上班的员工信息【使用IN】

@Test

public void TestQ6()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e where e.department.street in (?,?)";

Query query = entityManager.createQuery(jpql).setParameter(1,"恩宁路").setParameter(2, "八宝街");

List<Employee> resultList = query.getResultList();

for (Employee obj : resultList) {

System.out.println(obj);

}

entityManager.close();

}

1.2.7查询出工资在5000-6000的员工【使用BETWEEN…AND…】

@Test

public void TestQ7()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e where e.salary between ? and ? order by e.salary desc";

Query query = entityManager.createQuery(jpql).setParameter(1,new BigDecimal("5000")).setParameter(2, new BigDecimal("6000"));

 List<Employee>  resultList = query.getResultList();

for (Employee obj : resultList) {

System.out.println(obj.getSalary()+"----"+obj);

}

entityManager.close();

}

1.2.8查询出姓名包含er或者en的员工【使用LIKE】

@Test

public void TestQ8()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e where e.name like ? or e.name like ?";

Query query = entityManager.createQuery(jpql).setParameter(1,"%er%").setParameter(2, "%en%");

   List<Employee>  resultList = query.getResultList();

for (Employee obj : resultList) {

System.out.println(obj.getName()+"----"+obj);

}

entityManager.close();

}

1.2.8查询出有员工的部门【distinct】

@Test

public void TestQ9()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select distinct e.department.name from Employee e ";

Query query = entityManager.createQuery(jpql);

   List<Objecct> resultList = query.getResultList();

for (Object obj : resultList) {

System.out.println(obj);

}

entityManager.close();

}

1.3 集合的操作(size)

集合在JPA中经常出现,对集合的操作(size)

sql里面没有size(最终换成sql的count查询)

注意:使用size就是操作集合,那么我们就必需配置员工与部分双向的关系,让部门也可以找到对应的员工

1.3.1查询出有员工的部门【size】

@Test

public void TestQ10()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select d.name from Department d where d.employees.size&gt;0";

Query query = entityManager.createQuery(jpql);

   List<Object> resultList = query.getResultList();

for (Object obj : resultList) {

System.out.println(obj);

}

entityManager.close();

}

1.3.2查询出部门信息,按照部门的员工人数排序

@Test

public void TestQ11()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select d.name,d.employees.size from Department d order by d.employees.size";

Query query = entityManager.createQuery(jpql);

    List<Object> resultList = query.getResultList(); 

for (Object[] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.3.3查询出没有员工参与的项目【对集合使用size】

@Test

public void TestQ12()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select p.name,p.employees.size from Project p where p.employees.size=0";

Query query = entityManager.createQuery(jpql);

    List<Object> resultList = query.getResultList(); 

for (Object[ ] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.4 JOIN

JPA中的JOIN和LEFT JOIN(使用SQL/JPQL对比)

sql:select * 表1 join 表2 on 条件

jpql:

1.不写on子句

2.模型 模型的别名 join 写前面模型别名.出来的对象属性

1.4.1查询出所有员工及部门名称【JOIN/LEFT JOIN】

@Test

public void TestQ13()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e.name,d.name from Employee e join e.department d";

Query query = entityManager.createQuery(jpql);

    List<Objec[]t> resultList = query.getResultList();

for (Object[] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.4.2查询出市场部员工信息及电话

@Test

public void TestQ14()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e.name,p.number,e.department.name from Phone p join p.employee e where e.department.name like ?";

Query query = entityManager.createQuery(jpql).setParameter(1, "%市场部%");

     List<Object> resultList = query.getResultList();

for (Object[] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.5 聚集(组)函数/GROUP/HAVING

1.5.1查询出各个部门员工的平均工资和最高工资【使用聚集函数】

@Test

public void TestQ15()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e.department.name,max(e.salary),avg(e.salary) from Employee e group by e.department.name";

Query query = entityManager.createQuery(jpql);

    List<Object> resultList = query.getResultList();

for (Object[] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.5.2查询出各个项目参与人数报表

@Test

public void TestQ16()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select p.name,p.employees.size from Project p ";

Query query = entityManager.createQuery(jpql);

     List<Object> resultList = query.getResultList();

for (Object[] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.5.3查询出大于平均工资的员工信息

@Test

public void TestQ17()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e where e.salary &gt;(select avg (ee.salary) from Employee ee) order by e.salary asc ";

Query query = entityManager.createQuery(jpql);

    List<Employee > resultList = query.getResultList();

for (Employee obj : resultList) {

System.out.println(obj.getName()+"----"+obj.getSalary());

}

entityManager.close();

}

1.6获取分页数据

@Test

public void TestQ18()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String jpql="select e from Employee e where e.salary &gt;(select avg (ee.salary) from Employee ee) order by e.salary asc ";

Query query = entityManager.createQuery(jpql).setFirstResult(0).setMaxResults(3);

   List<Employee > resultList = query.getResultList();

System.out.println(resultList.size());

for (Employee obj : resultList) {

System.out.println(obj.getName()+"----"+obj.getSalary());

}

entityManager.close();

}

1.7 原生SQL查询

EntityManager API 提供了创建Query实例以执行原生SQL语句中的createNativeQuery()方法

1.7.1原生SQL查询:返回对象数组

@Test

public void TestQ19()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String sql="select \* from Employee e where e.salary &gt;(select avg (ee.salary) from Employee ee) order by e.salary asc ";

Query query = entityManager.createNativeQuery(sql);

List<Object[]> resultList = query.getResultList();

for (Object[] obj : resultList) {

System.out.println(Arrays.toString(obj));

}

entityManager.close();

}

1.7.2 原生SQL查询:返回模型对象

@Test

public void TestQ20()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String sql="select \* from Employee e where e.salary &gt;(select avg (ee.salary) from Employee ee) order by e.salary asc ";

/\*

\* entityManager.createNativeQuery(sql,Employee.class)

\* sql为原生sql语句,Employee.class为生成对象的字节码文件

\*

\* \*/

Query query = entityManager.createNativeQuery(sql,Employee.class);

List<Employee > resultList = query.getResultList();

System.out.println(resultList.size());

for (Employee obj : resultList) {

System.out.println(obj.getName()+"----"+obj.getSalary());

}

entityManager.close();

}

1.7.3 原生SQL查询:添加查询条件

@Test

public void TestQ21()throws Exception{

EntityManager entityManager = JpaUtil.getEntityManager();

String sql="select \* from Employee e where e.salary &gt; ? order by e.salary asc ";

Query query = entityManager.createNativeQuery(sql,Employee.class).setParameter(1, new BigDecimal("5000"));

List<Employee > resultList = query.getResultList();

System.out.println(resultList.size());

for (Employee obj : resultList) {

System.out.println(obj.getName()+"----"+obj.getSalary());

}

entityManager.close();

}

事务并发

2.1事务4个特性ACID回顾

原子性(atomic),事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行

一致性(consistent),事务在完成时,必须使所有的数据都保持一致状态。

隔离性(insulation),由事务并发所作的修改必须与任何其它并发事务所作的修改隔离

持久性(Duration),事务完成之后,它对于系统的影响是永久性的。

2.2 事务并发

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

2.3 隔离机制

当并发的事务访问或修改数据库中相同的数据(同一行同一列)时,通常需要采取必要的隔离机制。

解决并发问题的途径是什么?答案是:采取有效的隔离机制。

怎样实现事务的隔离呢?隔离机制的实现必须使用锁

2.4 事务并发带来的问题

以下事务都是发生在毫秒级的时间差

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

2.4.1第一类丢失更新:(在秒杀一件产品场景会出现问题)

库存是1件

当事务A和事务B同时修改某行的值,

1.事务A将数值改为0并提交,购买了一件

2.事务B将数值改为0并提交,也购买了一件。这时数据的值为0,事务A所做的更新将会丢失。(相当于就卖出去2件商品)

解决办法:对行加锁,只允许并发一个更新事务。(JPA中的悲观锁,乐观锁)

2.4.2 脏读

1.张三的原工资为4000, 财务人员将张三的工资改为了8000(但未提交事务)

2.张三读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!(在缓存中读取)

3.而财务发现操作有误,回滚了事务,张三的工资又变为了4000 像这样,张三记取的工资数8000是一个脏数据。

解决办法:如果在第一个事务提交前,任何其他事务不可读取其修改过的值,则可以避免该问题。

2.4.3虚读(幻读)

目前工资为4000的员工有10人。

1.事务1,读取所有工资为4000的员工。

2.这时事务2向employee表插入了一条员工记录,工资也为4000

3.事务1再次读取所有工资为4000的员工共读取到了11条记录,

解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题。

2.4.4 不可重复读

在一个事务中前后两次读取的结果并不致,导致了不可重复读。

1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成

2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.

3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000

解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。

2.4.5 第二类丢失更新

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

2.5数据库事务隔离级别(使用数据库默认的隔离级别就可以了)

SERIALIZABLE隔离级别最高,效率最低

一般都不修改隔离级别,如果要改,问dba(数据库管理员)

在这里插入图片描述

2.6悲观锁(处理的是同一张表的同一行同一列记录)

如果使用了悲观锁(加了一个行锁),如果事务没有被释放,就会造成其他事务处于等待,所以导致大量时间浪费,因此很少使用。

使用数据库提供的锁机制实现悲观锁。

如果数据库不支持设置的锁机制,JPA会使用该数据库提供的合适的锁机制来完成,而不会报错。

使用entityManager.find(class,id,LockModeType);加悲观锁,相当于发送SELECT … FOR UPDATE(加了一个行锁)

使用entityManager.lock(object,LockModeType);加悲观锁,相当于发送SELECT id FROM … FOR UPDATE(加了一个行锁)

2.7乐观锁(处理的还是同一张表的同一行同一列记录)

在这里插入图片描述

2.7.1 Version方式(整数,存储空间小)

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

@Version

private Integer version;

2.7.2秒杀场景:库存预设1个

在这里插入图片描述

2.7.3实现代码

@Test

public void Test2()throws Exception{

EntityManager entityManager1 = JpaUtil.getEntityManager();

EntityManager entityManager2= JpaUtil.getEntityManager();

try {

//开启两个事务

entityManager1.getTransaction().begin();

entityManager2.getTransaction().begin();

//两个都获取数据库数据

Product product = entityManager1.find(Product.class, 1L);

Product product1 = entityManager2.find(Product.class, 1L);

//两个都对获取的数据操作

product.setPack(product.getPack()-1);

product1.setPack(product1.getPack()-1);

//提交

entityManager1.getTransaction().commit();

entityManager2.getTransaction().commit();

}catch (Exception e){

EntityManager entityManager3 = JpaUtil.getEntityManager();

Product product3 = entityManager3.find(Product.class, 1L);

System.out.println("库存已经改变,最新库存:" + product3.getPack());

entityManager3.close();

}

finally {

entityManager1.close();

entityManager2.close();

}

}

3.如何优化JPA

1.使用双向一对多关联,不使用单向一对多

2.灵活使用单向多对一关联

3.不用一对一,用多对一取代(不要使用共享主键一对一,使用唯一外键一对一)

4.配置对象二级缓存,查询缓存(jpql查询),没有查询条件才使用查询缓存

5.组合关系集合使用list(顺序,重复),多对多集合使用set

6.表字段要少,表关联不要怕多,有二级缓存撑腰,设计表尽量达到第三范式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值