前几篇博客对JPA做了宏观性质的总结,也搭建成功了JSF + CDI + EJB + JPA(eclipselink)的程序。在通过JPA进行对实体查询的时候,写JPQL语句困惑到了我,于是重新总结一下。
Part1: JPQL基本用法
一、Query查询API
EM提供的创建Query方法:
createNamedQuery(String name)
createNativeQuery(String sqlString)
createNativeQuery(String sqlString, Class resultClass)
createNativeQuery(String sqlString, String resultSetMapping)
createQuery(String jpqlString)
二、参数设置
List getResultList()
Object getSingleResult(); //上述用于select语句
int executeUpdate(); //该句用于update,delete语句
Query setFirstResult(int startPosition)
Query setMaxResult(int maxResult)
三、使用JPQL查询步骤:
(1)EntityManager创建Query对象
(2)如果包含参数,setParameter()
(3)如果需要分页,调用Query的setFirstResult()或者setMaxResult()
(4)如果是select语句,使用getResultList()或者getSingleResult();
四、执行查询
P460一个demo就ok,注意返回的结果List,以及两层for循环,最终拿到值,以及内层循环中的条件,select的条件个数。
//至此,讲解基本用法
Part2: JPQL语法
一、from子句的使用
from后接实体名称,建议as 别名, from后不会跟多个实体,一般通过隐式连接或者显式连接来写实现笛卡尔积或者跨表连接。
二、select子句的使用
select p.name, p from Person as p
(1)select查询返回结果一般是List集合,如上,返回[String, Person]结果的数组形式集合(需要两层循环来解析),比如:
for(int i = 0; i < result.size(); i++)
{
Object[] values = (Object[])result.get(i);
System.out.println(values[0] + "-->"
+ values[1]);
}
或者:
for(int i = 0; i < result.size(); i++)
{
Object[] row = (Object[])result.get(i);
for(int j = 0; j < row.length; j++)
{
System.out.println(row[j]);
}
}
(2)特殊情况,select后只有一项(包含实体对象或属性),则查询得到的集合元素就是该实体对象或属性。
三、查询中使用构造器
select new ClassTest(p.name, p.address) from Person as p
如上,返回一个List集合,集合元素是ClassTest.对象(前提:ClassTest是支持以p.name, p.address作为参数的构造器),如果p.name类型为:String; p.address类型为String,则ClassTest有如下构造器
ClassTest(String s1, String s2)
四、使用distince排除相同的记录
select之后跟distinct,在对实体查询时,通过distinct修饰,就可以去除重复的查询结果,语句不予展示。
五、where子句和条件表达式
(1)常用运算符:数学、二进制比较、逻辑、in\not in\ between\ is null \ is empty \ is not empty \ member of \ mot member of 。
(2)like操作符:
select name from Person where name like 'tom%'
上面的运算符的功能大致上和SQL语句中类似的运算符功能相同,可以将SQL中的经验直接用到JPQL当中。
六、使用JPQL函数
对于JPQL的掌握,一定要向SQL语句那样, 同时掌握丰富的函数,来帮助我们处理问题。
常用函数:
(1)字符串函数
concat(str1,str2)\ substring(str,pos,len)\ trim([leading|trailing|both] sub from str)
(2)数学函数
abs(math_expression)
sqrt(math_express)
mod(number,div)
size(collection_expression)
(3)日期、时间函数
current_date\current_time\current_timestamp
七、多态查询
例如如下语句:
select p.name from Person p
该查询不仅会查询Person全部实例的name属性,还会将Person的子类实例的name属性全部查询出来。
当有继承关系的实体,查询父实体的属性时,会把子实体的name属性查出来。
八、关联和连接
参考Part3.
九、使用orderby进行结果排序
select p from Person as p order by p.name, p.age
JPQL支持在选出的属性上使用聚集函数,有如下5个:
avg:属性平均值。
count:统计选择对象的数量。
max:统计属性值的最大值。
min:统计属性值的最小值。
sum:计算属性值的总和。
十一、使用子查询
通常用在where子句中过滤查询结果集,如果底层数据库支持子查询,则可以在JPQL语句中使用子查询,通过英文()括起来。如下:
select fatcat from Cat as fatcat
where fatcat.weight > (select avg(cat.weight) from DomesticCat cat)
如果JPQL子查询返回多行结果集,则需要使用in、exists、any、all等关键字,其中any、all或者some可以和>、<、=等比较运算符结合使用。
十二、命名查询
JPA支持使用Annotation来定义JPQL查询语句,在Annotation中定义JPQL查询语句的方式就是命名查询。JPA提供了@NamedQuery、@NamedQueries两个Annotation来定义命名查询。比如,最近做的duke-bookstore项目中就有这样的使用方法:
@Entity
@Table(name = "WEB_BOOKSTORE_BOOKS")
@NamedQuery(
name = "findBooks",
query = "SELECT b FROM Book b ORDER BY b.bookId")
public class Book implements Serializable {
private static final long serialVersionUID = -4146681491856848089L;
@Id
@NotNull
private String bookId;
private String surname;
private String firstname;
private String title;
private Double price;
private Boolean onsale;
private Integer calendarYear;
private String description;
private Integer inventory;
public Book() {
}
public Book(String bookId, String surname, String firstname, String title,
Double price, Boolean onsale, Integer calendarYear,
String description, Integer inventory) {
this.bookId = bookId;
this.surname = surname;
this.firstname = firstname;
this.title = title;
this.price = price;
this.onsale = onsale;
this.calendarYear = calendarYear;
this.description = description;
this.inventory = inventory;
}
}
在Entity层直接使用@NameQuery定义一个查询语句,之后在ServiceImpl层的调用如下:
public List getBooks() throws BooksNotFoundException {
try {
return (List) em.createNamedQuery("findBooks").getResultList();
} catch (Exception ex) {
throw new BooksNotFoundException(
"Could not get books: " + ex.getMessage());
}
}
通过em.createNameQuery("
findBooks").getResultList(); 即可实现对@NameQuery的调用。
十三、批量更新和批量删除
(1)批量更新:
update set ... [Where where_conditions]
如上所示,即实现了批量更新的写法格式。
(2)批量删除:
delete from [where where_conditions]
实现批量删除的基本语法,和SQL语法很像, 不做过多介绍。
概念:
(1)关系维护端:负责对关系做CRUD操作;负责外键纪录的更新,关系被维护端没有权力更新外键纪录。 (one to many多的一方,many to many则为中间关联表)
(2)关系被维护端:拥有MappedBy注解,不进行CRUD操作,不维护关系。
(3)分类: one to one、one to many、many to many || 单向、双向
细致分析:
请参考我的下一篇博客关于JPQL实体映射详细分析。
总结:
针对JPQL,从上面三个角度做了最初的总结,后面遇到问题的解答以及详细介绍仍在继续。