JPQL&乐观锁

匿名 (未验证) 提交于 2019-12-03 00:11:01

JPQL(JPA的查询语句)

最基本的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并且封装执行的。

JPQL书写规则

JPA的查询语言,类似于sql
1.里面不能出现表名,列名,只能出现java的类名,属性名,区分大小写
2.出现的sql关键字是一样的意思,不区分大小写
3.不能写select * 要写select 别名

集合的操作(size)

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

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

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

查询出有员工的部门【size】//必须配置双向一对多:部门和员工

JOIN

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

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

jpql:

 1.不写on子句   2.模型 模型的别名 join 写前面模型别名.出来的对象属性  Employee e

join e.department d

聚集(组)函数/GROUP/HAVING

  1.1.1.   查询出各个部门员工的平均工资和最高工资【使用聚集函数】    @Test  public void test15() throws Exception {    EntityManager entityManager = JPAUtils.getEntityManager();    String jpql = "select avg(o.salary),max(o.salary) from Employee o group by o.department.name";    Query query = entityManager.createQuery(jpql);    List<Object[]> list = query.getResultList();    for (Object[] objects : list) {      System.out.println(Arrays.toString(objects));    }    System.out.println("size:" + list.size());    entityManager.close();  }    1.1.2.   查询出各个项目参与人数报表    // 项目1 3  // 项目2 2  @Test  public void test16() throws Exception {    EntityManager entityManager = JPAUtils.getEntityManager();    //查询出各个项目参与人数(不使用聚合)    String jpql = "select o.name,o.employees.size from Project o where o.employees.size>0";    //查询出各个项目参与人数(使用聚合)    jpql = "select o.name,count(e) from Project o join o.employees e group by o.name";    Query query = entityManager.createQuery(jpql);    List<Object[]> list = query.getResultList();    for (Object[] objects : list) {      System.out.println(Arrays.toString(objects));    }    System.out.println("size:" + list.size());    entityManager.close();  }    1.2.  子查询:查询出大于平均工资的员工信息    @Test  public void test17() throws Exception {    EntityManager entityManager = JPAUtils.getEntityManager();    String jpql = "select o from Employee o where salary>(select avg(salary) from Employee)";    Query query = entityManager.createQuery(jpql);    List<Employee> list = query.getResultList();    for (Employee employee : list) {      System.out.println(employee);    }    System.out.println("size:" + list.size());    entityManager.close();  }  

分页查询

  1.1.  获取分页数据    @Test  public void limit() throws Exception {    // 当前页码    int currentPage = 2;    // 一页显示条数    int pageSize = 5;       EntityManager entityManager = JPAUtils.getEntityManager();    String jpql = "select o from Employee o";    Query query = entityManager.createQuery(jpql);    // 从那里开始取数据,索引从0开始    int firstResult = (currentPage - 1) * pageSize;    // 取多少条    int maxResults = pageSize;    query.setFirstResult(firstResult).setMaxResults(maxResults);    List<Employee> list = query.getResultList();    for (Employee employee : list) {      System.out.println(employee);    }    System.out.println("size:" + list.size());    entityManager.close();  } 

分页原理

  1.1.1.   MySQLDialect核心代码    @Override  public String getLimitString(String sql, boolean hasOffset) {    return sql + (hasOffset ? " limit ?, ?" : " limit ?");  }    1.1.2.   Oracle10gDialect核心代码    public String getLimitString(String sql, boolean hasOffset) {              sql = sql.trim();              boolean isForUpdate = false;              if ( sql.toLowerCase().endsWith( " for update" ) ) {                          sql = sql.substring( 0, sql.length()-11 );                          isForUpdate = true;              }                 final StringBuilder pagingSelect = new StringBuilder( sql.length()+100 );              if (hasOffset) {                          pagingSelect.append( "select * from ( select row_.*, rownum rownum_ from ( " );              }              else {                          pagingSelect.append( "select * from ( " );              }              pagingSelect.append( sql );              if (hasOffset) {                          pagingSelect.append( " ) row_ ) where rownum_ <= ? and rownum_ > ?" );              }              else {                          pagingSelect.append( " ) where rownum <= ?" );              }                 if ( isForUpdate ) {                          pagingSelect.append( " for update" );              }                 return pagingSelect.toString();  }

原生SQL查询

  1.1.  返回对象数组    @Test    public void test3() throws Exception {      EntityManager entityManager = JPAUtils.getEntityManager();      String sql = "select * from employee";      Query query = entityManager.createNativeQuery(sql);      List<Object[]> list = query.getResultList();      for (Object[] objects : list) {        System.out.println(Arrays.toString(objects));      }      System.out.println("size:" + list.size());      entityManager.close();    }    1.2.  返回模型对象      @Test    public void test31() throws Exception {      EntityManager entityManager = JPAUtils.getEntityManager();      String sql = "select * from employee";      // 告诉hibernate把employee表转换为Employee对象      Query query = entityManager.createNativeQuery(sql, Employee.class);      List<Employee> list = query.getResultList();      query.setFirstResult(3).setMaxResults(3);      for (Employee employee : list) {        System.out.println(employee);      }      System.out.println("size:" + list.size());      entityManager.close();    }    1.3.  添加查询条件      @Test    public void test4() throws Exception {      EntityManager entityManager = JPAUtils.getEntityManager();      String sql = "select * from employee where name like ? order by salary desc";      Query query = entityManager.createNativeQuery(sql, Employee.class);      query.setParameter(1, "%en%");      List<Employee> list = query.getResultList();      for (Employee employee : list) {        System.out.println(employee);      }      System.out.println("size:" + list.size());      entityManager.close();      }

事务并发(乐观锁)

事务4个特性ACID回顾

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

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

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

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

隔离机制

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

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

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

事务并发带来的问题

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

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

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

库存是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会使用该数据库提供的合适的锁机制来完成,而不会报错。

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

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

乐观锁(处理的还是同一张表的同一行同一列记录)
Version方式(整数,存储空间小)

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

@Version

private Integer version;

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!