[学习笔记] 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/

评论