JPA乐观锁&jpql_day04

文章目录

    • 1 jpql
    • 2 jpql练习
      • 2.1 基本查询
      • 2.2 distinct
      • 2.3 集合操作size
      • 2.4 聚集函数
    • 3 分页查询
    • 4 事物并发
      • 4.1 事物的概念
      • 4.2 事物特性
      • 4.3 事务并发
      • 4.4 事务并发带来的问题
        • 4.4.1 第一类丢失更新
        • 4.4.2 脏读
        • 4.4.3 虚读(幻读)
        • 4.4.4 不可重复读
        • 4.4.5 第二类丢失更新
      • 4.5 悲观锁
      • 4.6 乐观锁
        • 4.6.1 Version方式(整数,存储空间小)
      • 4.7 数据库隔离级别
    • 5 如何优化JPA

1 jpql

概念:
jpa 持久化的查询语言,它和sql是类似,但是还是有区别
jpql规范写法:

  1. 在jpql里面, 不能出现* 和表名,只能出现对象别名,或者类名(类的完全限定名)
  2. 关键字和sql里面 一样的 不区分大小写
  3. 类名和属性名 区分大小写
    jpql写法:
select o
from Employee o
where  o.属性名 
group by 
order by
having 

2 jpql练习

2.1 基本查询

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

//查询所有员工的姓名和所属部门名称【查询特定属性】
@Test
public void testQuery1() throws Exception {
	String jpql = "select e.name,e.department.name from Employee e";
	EntityManager entityManager = Junit.getEntityManager();
	Query query = entityManager.createQuery(jpql);
	List<Object[]> list = query.getResultList();
	for (Object[] o : list) {
	   System.out.println(Arrays.toString(o));
	}
}

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

//查询出所有在成都和广州工作的员工【查询结果过滤】
    @Test
    public void testQuery2() throws Exception {
        String jpql = "select e from Employee e where e.department.city like ? or e.department.city like ?";
        EntityManager entityManager = Junit.getEntityManager();
        Query query = entityManager.createQuery(jpql);
        query.setParameter(1, "%成都%").setParameter(2, "%广州%");
        List<Employee> list = query.getResultList();
        for (Employee o : list) {
            System.out.println(o);
        }
    }

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

    //查询出所有员工信息,按照月薪排序【查询排序】
    @Test
    public void testQuery3() throws Exception {
        String jpql = "select e from Employee e order by e.salary desc";
        EntityManager entityManager = Junit.getEntityManager();
        Query query = entityManager.createQuery(jpql);
        List<Employee> list = query.getResultList();
        for (Employee o : list) {
            System.out.println(o);
        }
    }

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

    //查询出所有员工信息,按照部门编号排序【使用关联对象属性排序】
    @Test
    public void testQuery4() throws Exception {
        String jpql = "select e from Employee e order by e.department.sn desc";
        EntityManager entityManager = Junit.getEntityManager();
        Query query = entityManager.createQuery(jpql);
        List<Employee> list = query.getResultList();
        for (Employee o : list) {
            System.out.println(o);
        }
    }

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

    //查询出在恩宁路和八宝街上班的员工信息【使用IN】
    @Test
    public void testQuery5() throws Exception {
        String jpql = "select e from Employee e where e.department.street in(?1,?2)";
        EntityManager entityManager = Junit.getEntityManager();
        Query query = entityManager.createQuery(jpql);
        query.setParameter(1,"恩宁路").setParameter(2, "八宝街");
        List<Employee> list = query.getResultList();
        for (Employee o : list) {
            System.out.println(o);
        }
    }

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

    //查询出工资在5000-6000的员工【使用BETWEEN..AND..】
    @Test
    public void testQuery6() throws Exception {
        String jpql = "select e from Employee e where e.salary between :min and :max ";
        EntityManager entityManager = Junit.getEntityManager();
        Query query = entityManager.createQuery(jpql);
        query.setParameter("min", new BigDecimal("5000")).setParameter("max", new BigDecimal("6000"));
        List<Employee> list = query.getResultList();
        for (Employee o : list) {
            System.out.println(o);
        }
    }

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

    //查询出姓名包含er或者en的员工【使用LIKE】
    @Test
    public void testQuery7() throws Exception {
        String jpql = "select e from Employee e where e.name like ? or e.name like ?";
        EntityManager entityManager = Junit.getEntityManager();
        Query query = entityManager.createQuery(jpql);
        query.setParameter(1,"%er%").setParameter(2, "%en%");
        List<Employee> list = query.getResultList();
        for (Employee o : list) {
            System.out.println(o);
        }
    }

2.2 distinct

//查询出有员工的部门【distinct】
    @Test
    public void testQuery8() throws Exception {
        String jpql = "select distinct e.department from Employee e ";
        EntityManager entityManager = Junit.getEntityManager();
        Query query = entityManager.createQuery(jpql);
        List<Department> list = query.getResultList();
        for (Department o : list) {
            System.out.println(o);
        }
    }

2.3 集合操作size

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

//查询出有员工的部门【size】
@Test
public void testQuery9() throws Exception {
    String jpql = "select d from Department d where d.employees.size>0";
    EntityManager entityManager = Junit.getEntityManager();
    Query query = entityManager.createQuery(jpql);
    List<Department> list = query.getResultList();
    for (Department o : list) {
        System.out.println(o);
    }
}

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

//查询出部门信息,按照部门的员工人数排序
@Test
public void testQuery10() throws Exception {
    String jpql = "select d from Department d order by d.employees.size desc";
    EntityManager entityManager = Junit.getEntityManager();
    Query query = entityManager.createQuery(jpql);
    List<Department> list = query.getResultList();
    for (Department o : list) {
        System.out.println(o);
    }
}

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

//查询出没有员工参与的项目【对集合使用size】
@Test
public void testQuery11() throws Exception {
    String jpql = "select p from Project p where p.employees.size=0";
    EntityManager entityManager = Junit.getEntityManager();
    Query query = entityManager.createQuery(jpql);
    List<Project> list = query.getResultList();
    for (Project o : list) {
        System.out.println(o);
    }
}

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

//查询出所有员工及部门名称【JOIN/LEFT JOIN】
@Test
public void testQuery12() throws Exception {
    String jpql = "select e.name,d.name from Employee e left join e.department d";
    EntityManager entityManager = Junit.getEntityManager();
    Query query = entityManager.createQuery(jpql);
    List<Object[]> list = query.getResultList();
    for (Object[] o : list) {
        System.out.println(Arrays.toString(o));
    }
}

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

//查询出市场部员工信息及电话
@Test
public void testQuery13() throws Exception {
    String jpql = "select e,p from Phone p join p.employee e where e.department.name like ?";
    EntityManager entityManager = Junit.getEntityManager();
    Query query = entityManager.createQuery(jpql);
    query.setParameter(1, "%市场部%");
    List<Object[]> list = query.getResultList();
    for (Object[] o : list) {
        System.out.println(Arrays.toString(o));
    }
}

2.4 聚集函数

//查询出各个部门员工的平均工资和最高工资【使用聚集函数】
@Test
 public void testQuery14() throws Exception {
     String jpql = "select e.department.name,avg(e.salary),max(e.salary) from Employee e group by e.department.name ";
     EntityManager entityManager = Junit.getEntityManager();
     Query query = entityManager.createQuery(jpql);
     List<Object[]> list = query.getResultList();
     for (Object[] o : list) {
         System.out.println(Arrays.toString(o));
     }
 }

3 分页查询

query.setFirstResult(5).setMaxResults(5);

//分页查询
//query.setFirstResult(5).setMaxResults(5);
 @Test
 public void testQuery17() throws Exception {
     String jpql = "select e from Employee e";
     EntityManager entityManager = Junit.getEntityManager();
     Query query = entityManager.createQuery(jpql);
     query.setFirstResult(5).setMaxResults(5);
     List<Employee> list = query.getResultList();
     for (Employee o : list) {
         System.out.println(o);
     }
 }

4 事物并发

4.1 事物的概念

一组操作 要么都成功 要么都失败

4.2 事物特性

  1. 原子性(atomic),事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行
  2. 一致性(consistent),事务在完成时,必须使所有的数据都保持一致状态。
    3.隔离性(insulation),由事务并发所作的修改必须与任何其它并发事务所作的修改隔离。
  3. 持久性(Duration),事务完成之后,它对于系统的影响是永久性的

4.3 事务并发

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

4.4 事务并发带来的问题

4.4.1 第一类丢失更新

库存是1件
当事务A和事务B同时修改某行的值,

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

4.4.2 脏读

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

4.4.3 虚读(幻读)

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

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

4.4.4 不可重复读

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

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

4.4.5 第二类丢失更新

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

4.5 悲观锁

相当于同步效果, 如果一个人操作数据的时候,先把数据锁住,另外一个人必须等待我操作完,释放锁之后,才能进行操作
弊端:如果效率很低
底层: 可以锁行

 transaction;
begin:
 select * from t_employee where id=1 for update;
.......................
 update t_employee set money =money+1000;
commit;

4.6 乐观锁

JPA乐观锁&jpql_day04_第1张图片

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

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

4.7 数据库隔离级别

READ UNCOMMITTED(读未提交) 幻想读(虚读)、不可重复读和脏读都允许
READ COMMITTED 允许幻想读,不可重复读,不允许脏读
REPEATABLE READ 允许幻想读,不允许不可重复读和脏读
SERIALIZABLE :幻想读、不可重复读和脏读都不允许

MySql默认隔离级别为:Repeatable Read(可能会出现虚读)
Oracle 支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别
默认隔离级别为READ COMMITTED
JPA乐观锁&jpql_day04_第2张图片

5 如何优化JPA

  1. 使用双向一对多关联,不使用单向一对多
  2. 灵活使用单向多对一关联
  3. 不用一对一,多对一取代(不要使用共享主键一对一,使用唯一外键一对一)
  4. 配置对象二级缓存,查询缓存(jpql查询),没有查询条件才使用查询缓存
    5.组合关系集合使用list(顺序,重复),多对多集合使用set
  5. 表字段要少,表关联不要怕多,有二级缓存撑腰,设计表尽量达到第三范式

你可能感兴趣的:(学习)