[学习笔记] Hibernate之HQL连接查询和注解
# 学习 # · 2021-03-18
HQL的连接查询
1、HQL提供的常用连接方式:
连接类型 | HQL语法 |
---|---|
内连接 | inner join或join |
迫切内连接 | inner join fetch或join fetch |
左外连接 | left outer join或left join |
迫切左外连接 | left outer join fetch或left join fetch |
右外连接 | right outer join或right join |
(1)适用范围:适用于有关联关系的持久化类,并且在映射文件中对这种关联关系做了映射的。
(2)迫切连接:指不仅指定了连接的查询方式,而且显式地指定了关联级别的查询策略。迫切连接使用 fetch 关键字,fetch关键字表明 “左边” 对象用来与 “右边” 对象关联的属性会立即被初始化。
(3)普通连接:即不是迫切的连接,返回的集合都会被Hibernate封装成 List< Object[ ] >。
2、内连接:
(1)基本语法:
from Entity [inner] join Entity.property
(2)示例操作:查询部门和员工的信息。
String hqlStr = "from Dept as d inner join d.emps";
List<Object[]> list = session.createQuery(hqlStr).list();
for (Object[] obj : list) {
System.out.println(obj[0] + "\t" + obj[1]);
}
(3)执行结果:list集合中的每个元素都是一个Object数组,数组的第一个元素是Dept对象,第二个元素是Emp对象。Dept对象的emps集合元素没有被初始化,即emps集合没有存放关联的Emp对象。
3、迫切内连接:
(1)基本语法:
from Entity [inner] join fetch Entity.property
(2)示例操作:查询部门和员工的信息。
List<Dept> list = HibernateUtil.currentSession().createQuery("from Dept as d inner join fetch d.emps").list();
String hqlStr = "from Dept as d inner join fetch d.emps";
List<Dept> list = session.createQuery(hqlStr).list();
for (Dept dept : list) {
System.out.println(dept.getDeptName() + "\t" + dept.getEmps());
}
(3)执行结果:list集合中的每个元素都是Dept对象,Hibernate使用fetch关键字实现了将Emp对象读出来后立即填充到对应的Dept对象的集合属性中。
4、左外连接:
(1)基本语法:
from Entity left [outer] join Entity.property
(2)示例操作:查询部门和员工的信息。
String hqlStr = "from Dept as d left join d.emps";
List<Object[]> list = session.createQuery(hqlStr).list();
(3)执行结果:
5、迫切左外连接:
(1)基本语法:
from Entity left [outer] join fetch Entity.property
(2)示例操作:查询部门和员工的信息。
String hqlStr = "from Dept as d left join fetch d.emps";
List<Dept> list = session.createQuery(hqlStr).list();
(3)执行结果:
6、右外连接:
(1)基本语法:
from Entity right [outer] join Entity.property
(2)在实际开发中很少使用右外连接。
7、等值连接:适用于两个类之间没有定义任何关联时,在where子句中通过属性作为筛选条件。
(1)基本语法:
from Dept as d, Emp as e where d=e.dept
(2)示例操作:查询部门和员工的信息。
String hqlStr = "from Dept as d, Emp as e where d=e.dept";
List<Object[]> list = session.createQuery(hqlStr).list();
for (Object[] obj : list) {
System.out.println(obj[0] + "\t" + obj[1]);
}
(3)执行结果:
8、隐式内连接:
(1)在HQL查询语句中,对Emp类可以通过dept.deptName的形式访问其关联的dept对象的deptName属性。
(2)示例操作:按部门条件查询员工信息。
String hqlStr = "from Emp as e where e.dept.deptName=?";
List<Emp> list = session.createQuery(hqlStr).setParameter(0,"ACCOUNTING").list();
(3)执行结果:
分组统计数据
1、分组统计数据的基本语法:
[select ...] from ... [where ...] [group by...[having...] ] [order by...]
2、聚合函数:
函数名 | 说明 |
---|---|
count() | 统计记录条数 |
sum() | 求和 |
min() | 求最小值 |
max() | 求最大值 |
avg() | 求平均数 |
Long count = (Long)session.createQuery("select count(id) from Dept").uniqueResult();
Double salarySum = (Double)session.createQuery("select sum(salary) from Emp").uniqueResult();
Object[] salarys = (Object[] salarys)session.createQuery("select min(salary),max(salary),avg(salary) from Emp").uniqueResult();
System.out.println(salarys[0]+","+salarys[1]+","+salarys[2]);
3、分组查询:
(1)按职位统计员工个数:
List<Object[]> list = HibernateUtil.currentSession().createQuery("select e.job,count(e) from Emp e group by e.job").list();
(2)统计各部门的平均工资:
List<Object[]> list = HibernateUtil.currentSession().createQuery("select e.dept.dName,avg(e.sal) from Emp e group by e.dept.dName").list();
(3)统计各个职位的最低工资和最高工资:
List<Object[]> list = HibernateUtil.currentSession().createQuery("select e.job,min(e.sal),max(e.sal) from Emp e group by e.job").list();
(4)统计平均工资>4000元的部门名称:
List<Object[]> list = HibernateUtil.currentSession().createQuery("select e.dept.dName,avg(e.sal)from Emp e group by e.dept.dName having avg(e.sal)>4000").list();
子查询
1、子查询关键字:
关键字 | 说明 |
---|---|
all | 子查询语句返回的所有记录 |
any | 子查询语句返回的任意一条记录 |
some | 与any意识相同 |
in | 与=any意识相同 |
exists | 子查询语句至少返回一条记录 |
2、子查询应用:查询工资高于平均工资的员工。
List<Dept> list = HibernateUtil.currentSession().createQuery("from Emp as e where e.salary > (select avg(salary) from Emp))").list();
3、操作集合的函数或属性:
函数或属性 | 说明 |
---|---|
size()或size | 获取集合中元素的数目 |
minIndex()或minIndex | 对于建立了索引的集合,获得最小的索引 |
maxIndex()或maxIndex | 对于建立了索引的集合,获得最大的索引 |
minElement()或minElment | 对于包含基本类型元素的集合,获得集合中取值最小的元素 |
maxElement()或maxElment | 对于包含基本类型元素的集合,获得集合中取值最大的元素 |
elements() | 获取集合中的所有元素 |
4、操作集合的函数属性应用:
(1)查询指定员工所在部门。
List<Dept> list = HibernateUtil.currentSession().createQuery("from Dept d where ? in elements(d.emps) ").setParameter(0,emp).list();
(2)查询员工人数>5的部门:
List<Dept> list = HibernateUtil.currentSession().createQuery("from Dept d where d.emps.size>5").list();
查询性能优化
1、Hibernate查询优化策略:
(1)使用迫切左外连接或迫切内连接查询策略、配置二级缓存和查询缓存等方式,减少select语句的数目,降低访问访问数据库的频率。
(2)使用延迟加载等方式避免加载多余的不需要访问的数据。
(3)使用Query接口的iterate()方法减少select语句中的字段,减少访问数据库的数据量,并结合缓存等机制减少数据库访问次数,提高查询效率。
2、HQL优化:
(1)避免or操作的使用不当:如果where子句中有多个条件,并且其中某个条件没有索引,使用or将导致全表扫描。
(2)避免使用not:如果where子句的条件包含not关键字,那么执行该字段的索引失效。
(3)避免like的特殊形式:某些情况下,会在where子句中使用like。如果like以一个"%“或”_"开始,则该字段的索引不起作用。
(4)避免使用having子句。:在分组的查询语句中,可在两个位置指定条件,一是在where子句中,二是在having子句中。要尽可能的在where子句中而不是having子句中指定条件。
(5)避免使用distinct:指定distinct会导致在结果中删除重复的行,这会对处理时间造成一定的影响。
(6)索引在以下情况下失效,使用时应注意:
对字段使用函数,该字段的索引将不起作用,如:substring(aa,1,2)=‘XX’
对字段进行计算,该字段的索引将不起作用,如:price+10
注解
1、Hibernate注解:Hibernate提供了注解来进行对象关系映射,它可以代替大量的hbm.xml文件,使得Hibernate程序的文件数量大大精简。使用注解,可以直接将映射信息定义在持久化类中,而无需编写对应的 *.hbm.xml文件。
2、常用的注解配置实体化类:
注解 | 说明 |
---|---|
@Entity | 将一个类声明为一个持久化类 |
@Table | 为持久化类映射指定表 |
@Id | 声明了持久化类的标识属性(相当于主键) |
@GeneratedValue | 定义标识属性值的生成策略 |
@UniqueConstraint | 定义表的唯一约束 |
@Lob | 属性将被持久化为Blob或者Clob类型 |
@Column | 将属性映射到数据库字段 |
@Transient | 指定可以忽略的属性,不用持久化到数据库 |
3、常用的注解配置对象关联关系:
注解 | 说明 |
---|---|
@OneToOne | 一对一关联关系 |
@OneToMany | 一对多关联关系 |
@ManyToOne | 多对一关联关系 |
@ManyToMany | 多对多关联关系 |
4、使用Hibernate注解的步骤:
(1)使用注解配置持久化类及对象关联关系。
// @Entity标识是一个持久化类
@Entity
// @Table的name属性对应数据库表名,省略时默认表名与持久化类名相同。
@Table(name="`EMP`")
public class Emp implements Serializable {
/**
* 员工编号
*/
// @Id标识为注解
@Id
// @GeneratedValue标识主键生成策略为序列
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "seq_emp")
// @SequenceGenerator设置了序列生成器
@SequenceGenerator(name="seq_emp",sequenceName = "seq_emp_id",allocationSize=10,initialValue = 1)
private Integer empNo;
/**
* 员工姓名
*/
//@Column指定属性映射的数据库字段名,若不指定,则默认字段名和属性名相同。
@Column(name="`ENAME`")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="`DEPTNO`")
private String ename;
/**
* 所属部门
*/
// @Transient用于忽略不需要持久化到数据库中的属性
// @Transient
@ManyToOne(fetch = FetchType.LAZY)
private Dept dept;
// 省略其他代码
}
@Entity
@Table(name="`DEPT`")
public class Dept implements Serializable {
/**
* 部门编号
*/
@Id
@Column(name="`DEPTNO`")
private Integer deptNo;
/**
* 部门名称
*/
@Column(name="`DNAME`")
private String deptName;
/**
* 部门地区
*/
@Column(name="`LOC`")
private String loc;
@OneToMany(mappedBy = "dept",cascade = {CascadeType.ALL})
private Set<Emp> emps=new HashSet<Emp>();
// 省略其他代码
}
(2)在Hibernate配置文件(hibernate.cfg.xml)中声明持久化类。
<mapping class="com.aduo.pojo.Dept"/>
<mapping class="com.aduo.pojo.Emp"/>
4、@GeneratedValue注解:指定了OID的生成策略,不使用此注解时,默认OID由程序赋值,相当于在映射文件中指定assigned。JPA提供了4种标准用法。
(1)AUTO:根据不同的数据库选择不同的策略,相当于Hibernate中的native。
(2)TABLE:使用表保存id值。
(3)IDENTITY:使用数据库自动生成主键值,主要是自动增长型,如MySQL、SQLServer。
(4)SEQUENCE:使用序列生成主键值(如Oracle),generator="seq_emp"指定了生成器为seq_emp。
5、@SequenceGenerator注解:标识主键生成策略为序列。
(1)注解属性name:定义了序列生成器为seq_emp。
(2)注解属性sequenceName:指定了序列的名称为seq_emp_id。
(3)注解属性allocationSize:设置了生成器分配id时的增量。
(4)注解属性initialValue:设置了主键起始值。
5、@ManyToOne注解:注解配置了Emp类和Dept类之间的多对一关联。
(1)注解属性fetch=FetchType.LAZY设置关联级别采用延迟加载策略。
(2)若不指定注解属性fetch,则该属性默认值是EAGER,查询Emp时,Hibernate将使用左外连接将相关Dept对象一并查出。
6、@OneToMany注解:配置了Dept类和Emp类之间的一对多关系。
(1)注解属性mappedBy="dept"将关联关系的控制权交给Emp类这一方,相当于Dept.hbm.xml中配置的inverse=“true”。
(2)注解属性mappedBy属性的值是Emp类中与Dept类相关联的属性名。
(3)注解属性cascade={CascadeType.ALL}设置了级联操作的类型。
CascadeType.REMOVE:级联删除
CascadeType.PERSIST:persist()方法级联
CascadeType.MERGE:级联更新
CascadeType.REFRESH:级联刷新
CascadeType.ALL:包含所有级联操作
如若转载,请注明出处:一木林多 - https://www.l5v.cn/archives/270/
评论