文章目录
1 jpql
概念:
jpa 持久化的查询语言,它和sql是类似,但是还是有区别
jpql规范写法:
- 在jpql里面, 不能出现* 和表名,只能出现对象别名,或者类名(类的完全限定名)
- 关键字和sql里面 一样的 不区分大小写
- 类名和属性名 区分大小写
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 事物特性
- 原子性(atomic),事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行
- 一致性(consistent),事务在完成时,必须使所有的数据都保持一致状态。
3.隔离性
(insulation),由事务并发所作的修改必须与任何其它并发事务所作的修改隔离。 - 持久性(Duration),事务完成之后,它对于系统的影响是永久性的
4.3 事务并发
通常为了获得更好的运行性能,各种数据库都允许多个事务同时运行,这就是事务并发
4.4 事务并发带来的问题
4.4.1 第一类丢失更新
库存是1件
当事务A和事务B同时修改某行的值,
- 事务A将数值改为0并提交,购买了一件
- 事务B将数值改为0并提交,也购买了一件。这时数据的值为0,事务A所做的更新将会丢失。(相当于就卖出去2件商品)
解决办法:对行加锁,只允许并发一个更新事务。(JPA中的悲观锁,乐观锁)
4.4.2 脏读
- 张三的原工资为4000, 财务人员将张三的工资改为了8000(但未提交事务)
- 张三读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!(在缓存中读取)
- 而财务发现操作有误,回滚了事务,张三的工资又变为了4000 像这样,张三记取的工资数8000是一个脏数据。
解决办法:如果在第一个事务提交前,任何其他事务不可读取其修改过的值,则可以避免该问题
4.4.3 虚读(幻读)
目前工资为4000的员工有10人。
- 事务1,读取所有工资为4000的员工。
- 这时事务2向employee表插入了一条员工记录,工资也为4000
- 事务1
再次
读取所有工资为4000的员工共读取到了11条记录,
解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题
4.4.4 不可重复读
在一个事务中前后两次读取的结果并不致,导致了不可重复读。
- 在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
- 在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
- 在事务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 乐观锁
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
5 如何优化JPA
使用
双向一对多关联,不使用
单向一对多- 灵活使用
单向多对一
关联 不用
一对一,用
多对一取代(不要使用共享主键一对一,使用唯一外键一对一)- 配置对象二级缓存,查询缓存(jpql查询),没有查询条件才使用查询缓存
5.组合关系集合使用list
(顺序,重复),多对多集合使用set
- 表字段要少,表关联不要怕多,有二级缓存撑腰,设计表
尽量达到第三范式