JPA支持两种方式:JPQL和条件API,条件API不推荐看。
Java持久化查询语言JPQL,了解下
JPQL 是 实体模型上进行查询的,而且查询的结果只能是实体对象或者实体的一个字段,查询的是实体对象。
JPQL后面都会去转化为本地的sql执行。。。
1. 查询实体的所有数据 /单个字段
select e from Employee e
select e.name from Employee e
2. 筛选结果
JPQL支持where子句,在SQL中通常可用的操作符号在JPQL中也可用,包括,基本比较操作符,in,like ,between,
函数表达式和子查询等。
3. 实体之间的连接
一个SELECT查询的结果类型不能是集合,必须是单值对象,如实体的实例或者字段。支持内连接和外连接
select m from Employee e,Phone p where p.id ='1'
4. 聚合查询
5. 查询参数
位置表示法
命名参数表示法
SQL查询
JPQL是实体上进行查询的首选方法,使用JPQL更容易在多个数据库之间移植,并且映射关系更容易维护。sql查询更方便。
JPQL和SQL查询定义之间的关键区别在于查询引擎不应解析和解释特定于供应商的SQL
JPA提供了Query和TypedQuery接口来配置和执行查询,Query接口用于当结果类型是Object的情况,而TypedQuery接口则是
偏好指定类型的结果的情况。
为了动态地定义一个返回实体结果的SQL查询,一种是使用EntityManager接口的creatNativeQuery()方法。
String sql = " select * from student_info ";
entityManager.createNativeQuery(sql, Student.class).getResultList();
使用@NamedNativeQuery注解来定义命名的SQL查询,
这个注解在使用的时候具有唯一性,要求名称唯一性,在实体上定义多个会报错,解决方法是使用@NamedNativeQuerys里面定义多个查询
改为@NamedNativeQuerys定义多个查询
@Entity
@NamedNativeQueries(value={
@NamedNativeQuery(
name = "studentInfoById",
query = " select * from student_info where stu_id = ? ",
resultClass = Student.class
),
@NamedNativeQuery(
name= "studentJoinCourse",
query = " select * from student_info,course",
resultSetMapping = "studentAndCourse"
)
})
@Table(name="student_info")
public class Student {
如果结果类型是一个实体,那么resutlClass元素可用指示实体类,如果结果需要一个SQL映射,那么resutlSetMapping元素可用
用来指定映射的名称。
执行命名SQL查询
下面用到的表,学生和课程的关系,按照一对多的关系测试。
student_info
course_info
执行命名SQL查询可以使用EntityManager接口的createNamedQuery()方法创建和执行这个查询,并且这个方法能够返回一个指定了类型的query,TypedQuery。而不是createNativeQuery()返回一个非类型的Query(Object的实体类型)
public class StudentDao {
public Student queryStudentbyId(EntityManager entitymanager,Integer id){
return entitymanager.createNamedQuery("studentInfoById", Student.class).setParameter(1, 1).getSingleResult();
}
关于返回的实体的SQL查询,需要注意的是,由于实体的实例在持久化上下文中是托管状态,实体的内容/和修改后的实体的内容都将在事务提交后写入到数据库中。
通常的情况是,如果查询中忽略映射一个字段/或者默认为某个值,此时假设每个映射字段,这个值为null,然后修改结果提交了事务,数据库的默认值就会改变,变成了null,这就存在问题,这点需要注意。
JPA支持SQL数据操作语句(INSERT,UPDATE,DELETE)
public class StudentDao {
public static final String deleteStudentId = "delete from student_info where stu_id = ? ";
public static final String updateStudentId = " update student_inf set stu_name = ? where stu_id = ? ";
public void deleteStudentById(EntityManager entitymanager,Integer id ){
entitymanager.createNativeQuery(deleteStudentId).setParameter(1, id).executeUpdate();
}
public void updateStudentById(EntityManager entitymanager,Integer id ){
entitymanager.createNativeQuery(updateStudentId).setParameter(1, id).executeUpdate();
}
通常不鼓励执行SQL语句来更改实体所映射的表中数据,这会导致缓存的实体和数据库表不一致,遇到这种情况,不要使用查询出来的缓存对象,最好再次发起一次查询方法。
SQL结果集映射
使用注解@SqlResultSetMapping注解定义SQL结果集映射,可以放在一个实体类上,有一个名称(在持久性单元中唯一)和一个或者多个实体映射组成。
直接在实体上使用多个@SqlResultSetMapping会报错,解决方法是使用@SqlResultSetMappings注解,在这个注解中定义多个@SqlResultSetMapping
@SqlResultSetMappings({
@SqlResultSetMapping(name="studentInfo",entities=@EntityResult(entityClass=Student.class)),
})
元素name表示名称必须唯一,entites表示映射的实体对象,可以是多个
使用实体类进行映射的时候,必须查询出所有实体类的字段,包括外键。如果缺少字段,报错或者部分构建实体,取决于JPA的供应商。
这个定义一个单一的结果映射,由@EntityResult注解指定,其引用了Employee实体类,该查询必须为实体映射的列提供值,包括外键。
如何使用结果集映射呢,resultSetMapping属性为使用的结果集名称。
* 映射外键
假设学生和课程的关系是一对多,课程表中外键列存放学生表的主键,定义一个本地查询
@Entity
@NamedNativeQuery(name="queryCourseInfoByid",
query = " select course_id,course_name,stu_id from course_info where course_id = ? ",
resultSetMapping = "courseInfo"
)
@SqlResultSetMapping(name="courseInfo",entities=@EntityResult(entityClass=Course.class))
@Table(name="course_info")
public class Course {
public class CourseDao {
public Course queryCouserInfoById(EntityManager entityManager,Integer id ){
return entityManager.createNamedQuery("queryCourseInfoByid", Course.class).setParameter(1, id).getSingleResult();
}
}
那么在执行本地查询的时候,查询的时候会根据外键列,查询关联实体,这样会发送两次查询
最好设置实体中关联对象的字段为懒加载。需要注意缓存的是外键列的值。
CourseDao dao = new CourseDao();
Course course = dao.queryCouserInfoById(entityManager, 1);
打印SQL:
Hibernate:
select
course_id,
course_name,
stu_id
from
course_info
where
course_id = ?
Hibernate:
select
student0_.stu_id as stu_id1_19_0_,
student0_.stu_sex as stu_sex2_19_0_,
student0_.stu_age as stu_age3_19_0_,
student0_.stu_name as stu_name4_19_0_
from
student_info student0_
where
student0_.stu_id=?
* 映射列别名
在查询的结果不同于列映射名称的情况下,@FieldResult注解将列的别名映射到实体的字段上。
@SqlResultSetMapping(name="studentInfo",
entities={
@EntityResult(entityClass=Student.class,fields={
@FieldResult(name="stuId",column="student_id"),
@FieldResult(name="sex",column="stu_sex"),
@FieldResult(name="stuName",column="stu_name"),
@FieldResult(name="stuAge",column="stu_age")
}
@NamedNativeQuery(
name = "studentInfoById",
query = " select stu_id as student_id, stu_sex ,stu_name,stu_age from student_info where stu_id = ? ",
resultSetMapping = "studentInfo"
)
测试中发现,实体映射的一个列使用别名,其他的列也需要手动使用@FieldResult注解映射,奇怪。
* 查询返回的字段不是实体字段(标量结果列)映射方式
映射的字段不是实体结果类型的字段,称为标量结果类型,借助@ColumnResult注解定义映射字段,很多时候不需要实体的
全部字段,只需要部分字段,可以使用@ColumnResult进行字段映射。
并且注解@ColumnResult的name元素的值为列名
@NamedNativeQuery(name="stuCousrseInfos",
query = "select m.stu_id,m.stu_name,n.course_name from student_info m join course_info n on m.stu_id = n.stu_id"
+"and m.stu_id = ? ",
resultSetMapping = "stuCousrseInfosMapping"
)
结果集映射定义
@SqlResultSetMapping(name="stuCousrseInfosMapping",
columns={
@ColumnResult(name="stu_id"),
@ColumnResult(name="stu_name"),
@ColumnResult(name="course_name")
}
Dao如何执行查询,取出映射字段,无法确定类型,使用Object[]数组接收
public List<Object[]> queryjoinInfo(EntityManager entityManager,Integer stuId){
List<Object[]> list = entityManager.createNamedQuery("stuCousrseInfos").setParameter(1, 1).getResultList();
return list;
}
Object[]数组中映射字段的值的顺序和在结果集中定义的顺序一致,通过上面的方式可以用于多表join的字段映射上,不过
得到的结果是Object[]数组形式,需要去遍历获取。
* 返回的字段是多个实体的字段的映射反射(多个结果映射)
查询可能返回的的字段是属于多个实体的字段,当查询的实体之间存在一对一关系时,比较合适,如果不是一对一方法,
,比如是一对多的情况,就是导致一的一方,重复的实例生成。
@NamedNativeQuery(
name= "studentJoinCourse",
query = " select * from student_info m ,course_info n where m.stu_id = ? and n.course_id = ? ",
resultSetMapping = "studentAndCourse"
)
@SqlResultSetMapping(name="studentAndCourse",
entities ={@EntityResult(entityClass=Student.class),@EntityResult(entityClass=Course.class)})
@SuppressWarnings("unchecked")
public List<Object[]> querystudentJoinCourse(EntityManager entitymanager,Integer stuId,Integer courseId){
List<Object[]> list = entitymanager.createNamedQuery("studentJoinCourse").setParameter(1, stuId).setParameter(2, courseId).getResultList();
return list;
}
从结果看出,字段映射的顺序按照实体类的顺序进行映射。
参数绑定
定义本地查询的时候使用?作为占位符号,setParameter方法按照下标设值,下标从1开始,如果是日期类型需要特殊处理
setParameter(1, new Date(),TemporalType.DATE)
public enum TemporalType
{
DATE, TIME, TIMESTAMP;
private TemporalType() {}
}
根据需要设置为DATE,TIME,TIMESTAMP