jpql是JPA操作数据库的语言,它的底层还是sql语句,就是我们的jpql语言最终还是会转化为sql语句到数据库去做相应的操作。所以JPAQL与sql语句很相识,它们的关键字是一样的,最大的一个区别就是JPQL是面向对象的,我们可以在语句中使用一些面向对象的语法(new 对象,调属性)。建议初学者,可以先学习sql语言。
1.JPQL语句的使用规范
JPA的查询语言,类似于sql
1.里面不能出现表名,列名,只能出现java的类名,属性名,区分大小写
2.出现的sql关键字是一样的意思,不区分大小写
3.不能写select * 要写select 别名
2.JPQL基本查询语句(我这里没有使用框架,以后想Spring等框架使用起来会更加方便)
1.查询所有
Select 别名 from 类 别名
Query query = entityManager.createQuery(jpql); 关联sql语句
List<Employee> resultList = query.getResultList(); 返回查询结果
/**
* 查询所有员工【查询实体类型】
* resultList.forEach(e-> System.out.println(e));foreach
foreach的另一种写法
*/
@Test
public void testQueryAll(){
String jpql="select o from Employee o";
EntityManager entityManager = JPAUtils.getEntityManager();//JPAUtils是我抽取的一个获取
EntityManager对象的工具类
Query query = entityManager.createQuery(jpql);
List<Employee> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(e));
entityManager.close();
}
2.查询一个字段(某一行/列)
查询所有员工的姓名和所属部门名称【查询特定属性】
jpql:是面向对象的的语言,可以使用Java语言
返回为一个Object的数组
Arrays.asList(e) 转化,将e转化为字符
@Test
public void testQuery1(){
String jpql="select o.name,o.department.name from Employee o";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Object[]> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(Arrays.asList(e)));
entityManager.close();
}
/**
* 2.2.2.查询所有员工的姓名和所属部门名称【查询特定属性】
* 直接new
*/
@Test
public void testQuery2(){
String jpql="select new Employee(o.name,o.department.name) from Employee o";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Employee> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(e));
entityManager.close();
}
3.条件查询 where
/**
* 查询出所有在成都和广州工作的员工【查询结果过滤】
* ? 占位符 从1开始
给占位符复值
query.setParameter(1, "成都");
query.setParameter(2, "广州");
List<Employee> resultList = query.getResultList();
*/
@Test
public void testQuery3(){
String jpql="select o from Employee o where o.department.city=?1 or o.department.city=?2";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
//给?设置值
query.setParameter(1, "成都");
query.setParameter(2, "广州");
List<Employee> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(e.getName()+" "+ e.getDepartment().getCity()));
entityManager.close();
}
条件集合查询 in(条件1,条件2....)
/**
* 查询出在恩宁路和八宝街上班的员工信息【使用IN】
* in(条件1,条件2...)
*/
@Test
public void testQuery7(){
String jpql="select o from Employee o where o.department.street in(?1,?2)";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
query.setParameter(1,"恩宁路" ).setParameter(2, "八宝街" );
List<Employee> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(e));
entityManager.close();
}
4.范围查询
按照范围查找(BigDecimal 类型:赋值是new 它的对象)
/**
* 查询出工资在5000-6000的员工【使用BETWEEN..AND..】
*
* salary 是BigDecimal 类型,所以我们不能直接拿值,需要new BigDecimal(值),这个就是值
*/
@Test
public void testQuery8(){
String jpql="select o from Employee o where o.salary between ?1 and ?2";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
query.setParameter(1,new BigDecimal(5000)).setParameter(2,new BigDecimal(6000) );
List<Employee> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(e.getName()+" "+e.getSalary()));
entityManager.close();
5.模糊查询
/**
*
* 查询出姓名包含er或者en的员工【使用LIKE】
*
* like:where o.name like ?1 or o.name like ?2
* 不能在语句中直接写“%?%”
*
*/
@Test
public void testQuery9(){
String jpql="select o from Employee o where o.name like ?1 or o.name like ?2";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
query.setParameter(1,"%er%").setParameter(2,"%en%");
List<Employee> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(e.getName()));
entityManager.close();
}
6.查询去重
/**
* 查询出有员工的部门【distinct】
* 使用distinct:只查询不为空的
*/
@Test
public void testQuery10(){
String jpql="select distinct o.department from Employee o ";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Department> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(e));
entityManager.close();
}
3.集合的操作
集合的操作(size) 集合都有size
集合在JPA中经常出现,对集合的操作(size)
sql里面没有size(最终换成sql的count查询)
注意:使用size就是操作集合,那么我们就必需配置员工与部分双向的关系,让部门也可以找到对应的员
/**
* 集合的操作(size) 集合都有size
* 集合在JPA中经常出现,对集合的操作(size)
* sql里面没有size(最终换成sql的count查询)
* 注意:使用size就是操作集合,那么我们就必需配置员工与部分双向的关系,让部门也可以找到对应的员工
*/
/**
* 2.4.1.查询出有员工的部门【size】//必须配置双向一对多:部门和员工
*
*/
@Test
public void testJPA11(){
String jpql="select o from Department o where o.employees.size>0";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Department> resultList = query.getResultList();
resultList.forEach(e -> System.out.println(e.getName()));
entityManager.close();
}
4.JOIN/left JOIN
JPA中的JOIN和LEFT JOIN(使用SQL/JPQL对比)
sql:
select * 表1 join 表2 on 条件
jpql:
1.不写on语句
2.模型 模型的别名 join 写前面模型别名.出来的对象属性
PQL中是支持关连(JOIN/LEFT JOIN)
两个法则:1.自己消除笛卡尔积 2.关连 对象的别名.属性
/**
* JPA中的JOIN和LEFT JOIN(使用SQL/JPQL对比)
* sql:
* select * 表1 join 表2 on 条件
jpql:
1.不写on子句
2.模型 模型的别名 join 写前面模型别名.出来的对象属性
PQL中是支持关连(JOIN/LEFT JOIN)
两个法则:1.自己消除笛卡尔积 2.关连 对象的别名.属性
*/
/**
* 查询出所有员工及部门名称【JOIN/LEFT JOIN】
*
*/
@Test
public void testJPA14(){
//language=JPAQL
String jpql="select o.name,d.name from Employee o left join o.department d";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Object[]> resultList = query.getResultList();
resultList.forEach(e -> System.out.println(Arrays.asList(e)));
entityManager.close();
}
/**
*2.5.2.查询出市场部员工信息及电话
*/
@Test
public void testJPA15(){
//language=JPAQL
String jpql = "select e,p from Phone p join p.employee e join e.department d where d.name='市场部'";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Object[]> resultList = query.getResultList();
resultList.forEach(e -> System.out.println(Arrays.asList(e)));
entityManager.close();
}
5.聚合函数
/**
* 查询出各个项目参与人数报表
*/
@Test
public void testJPA17(){
String jpql="select p.name,count(e) from Project p left join p.employees e group by p.name";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Object[]> resultList = query.getResultList();
resultList.forEach(e-> System.out.println(Arrays.asList(e)));
entityManager.close();
}
6.子查询
/**
* 子查询:查询出大于平均工资的员工信息
* 一个查询的结果作为另一个查询的条件与参数
*/
@Test
public void testJPA18(){
String jpql="select o from Employee o where o.salary > (select avg(e.salary) from Employee e )";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
List<Employee> resultList = query.getResultList();
resultList.forEach(e -> System.out.println(e));
entityManager.close();
}
7.分页
1.真分页:在数据库一次查询只查当前页的值 每次查询时间一样
2.假分页:在数据库一次全部查出来,然后放到缓冲中,每次查询只到缓存中拿,第一次特别慢
Jpql的分页:
setFirstResult:从第几条开始
setMaxResults:第几页查询多少条数据
Long singleResult = (Long)query.getSingleResult();//返回非Long
//假分页
@Test
public void testPage(){
String jpql="select o from Employee o";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
//开始设置分页
/**
* setFirstResult:从第几条开始
* setMaxResults:第几页查询多少条数据
* limit 0,5
* 从第几条开始 : (currentPage-1)*pageSize
*/
query.setFirstResult(5).setMaxResults(5);
List<Employee> resultList = query.getResultList();
resultList.forEach(employee -> System.out.println(employee) );
entityManager.close();
}
//获取总数据
@Test
public void testConut(){
String jpql="select count(o) from Employee o";
EntityManager entityManager = JPAUtils.getEntityManager();
Query query = entityManager.createQuery(jpql);
Long singleResult = (Long)query.getSingleResult();//返回非Long
System.out.println(singleResult);
entityManager.close();
}
8.原生SQL
我们可以在语句中直接写sql
如何使用sql:
entityManager.createNativeQuery(sql);//运行原生SQL
createNativeQuery(sql);//运行原生sql,拿到的是集合
createNativeQuery(sql, Employee.class); 拿到的是集合
@Test
public void testSQL(){
String sql="select * from Employee ";
EntityManager entityManager = JPAUtils.getEntityManager();
Query nativeQuery = entityManager.createNativeQuery(sql);
List<Object[]> resultList = nativeQuery.getResultList();
resultList.forEach(employee -> System.out.println(Arrays.asList(employee)));
entityManager.close();
}
//返回一个对象
@Test
public void testSQL2(){
String sql="select * from Employee ";
EntityManager entityManager = JPAUtils.getEntityManager();
Query nativeQuery = entityManager.createNativeQuery(sql, Employee.class);
List<Employee> resultList = nativeQuery.getResultList();
resultList.forEach(employee -> System.out.println(employee));
entityManager.close();
}
9.事务:同生共死
1.原子性(atomic):
最小单位
对于其数据修改,要么全都执行,要么全都不执行
2.一致性(consistent):
事务在完成时,必须使所有的数据都保持一致状态。
3.持久性(Duration):
事务完成之后,它对于系统的影响是永久性的。
4.隔离性(insulation):
当并发的事务访问或修改数据库中相同的数据(同一行同一列)时,通常需要采取必要的隔离机制。
由事务并发所作的修改必须与任何其它并发事务所作的修改隔离(使用锁来实现隔离机制)
10.事务并发
通常为了获得更好的运行性能,各种数据库都允许多个事务同时运行,这就是事务并发。
事务并发的问题
- 第一类丢失更新:
- 第二类丢失更新
- 脏读
- 不可重复读
- 泛读
脏读,泛读,与不可重复读:考虑比较小,影响也比较小,在开发中如果要求不高可以不考虑
解决第一,二类丢失更新(使用锁)
悲观锁:
是将我们需要的数据全部加锁,他不释放事务,别人是不能访问。性能极差,不推荐使用
如果使用了悲观锁(加了一个行锁),如果事务没有被释放,就会造成其他事务处于等待
使用entityManager.find(class,id,LockModeType);加悲观锁,相当于发送SELECT ... FOR UPDATE(加了一个行锁)
使用entityManager.lock(object,LockModeType);加悲观锁,相当于发送SELECT id FROM ... FOR UPDATE(加了一个行锁)
乐观锁:
大家都可以来,但是只有满足条件的才能获取,提交成功。比较常用
使用version:添加一个私有字段Integer version,不由程序员维护,由JPA自己维护
11.JPQL的代码优化
- 使用双向一对多关联,不使用单向一对多
- 灵活使用单向多对一关联
- 不用一对一,用多对一取代(不要使用共享主键一对一,使用唯一外键一对一)
- 配置对象二级缓存,不使用集合二级缓存,如果使用了集合二级缓存,集合里面的对象也必须二级缓存;查询缓存(jpql查询),没有查询条件才使用查询缓存
- 组合关系集合使用list(顺序,重复),多对多集合使用set(性能更好)
- 表字段要少,表关联不要怕多,有二级缓存撑腰,设计表尽量达到第三范式