zoukankan      html  css  js  c++  java
  • Hibernate.Criteria查询

    1.序言

    Hibernate框架是目前JavaEE软件开发的企业主流框架,学习Hibernate必然要掌握ORM(对象关系映射Object/Relation Mapping)的概念思想, Hibernate拥有完全的ORM理念,我们在操作数据库时,可以通过面向对象的方式就可以完成数据库的CRUD(创建(Create)、更新(Update)、读取(Read)和删除(Delete))操作。
    企业在使用Hibernate进行软件开发进行数据查询时,主要基于HQL(Hibernate 面向对象的查询语言,语法类似SQL)、 Criteria(面向对象的条件查询对象)、SQL(原生态SQL语句)几种方式,本文重点讲解Criteria 这种完全面向对象编程查询方式,详细分析Crieria各种使用与SQL生成关系。

    2.Criteria
    Criteria 是一个完全面向对象,可扩展的条件查询API,通过它完全不需要考虑数据库底层如何实现、SQL语句如何编写,是Hibernate框架的核心查询对象。
    Hibernate 定义了CriteriaSpecification接口规范用来完成面向对象的条件查询,Criteria 就是CriteriaSpecification的子接口。

     2.1数据库环境
    开发条件:
    数据库 oracleXE 10g
    用户名 jsd1606
    密码 jsd1606
     
    我们以部门(Department)和员工(Employee) 案例,来讲解Criteria的详细使用。
    (1).建表:
    (a).部门表 department 

    create table department (
    id number(10,0) not null,
    name varchar2(255 char),
    primary key (id)
    )

    (b).员工表 employee 

    create table employee (
    id number(10,0) not null,
    age number(10,0),
    birthday date,
    name varchar2(255 char),
    department_id number(10,0),
    primary key (id)
    )

    (2).插入数据
    (a).部门表 department 

    insert into department values(1,'人力资源部');
    insert into department values(2,'财务部');
    insert into department values(3,'行政部');
    insert into department values(4,'市场部');
    SQL>select * from department;
    id name
    1 人力资源部
    2 财务部
    3 行政部
    4 市场部
     

    (b).员工表 employee 
    insert into employee values(1,21,TO_DATE('1995-06-06', 'yyyy-MM-dd'),'张三',1);
    insert into employee values(2,22,TO_DATE('1994-09-26', 'yyyy-MM-dd'),'李四',1);
    insert into employee values(3,21,TO_DATE('1995-03-06', 'yyyy-MM-dd'),'王五',2);
    insert into employee values(4,22,TO_DATE('1994-08-21', 'yyyy-MM-dd'),'赵六',3);
    mysql>select * from employee;
    id name age birthday department_id
    1 张三 21 1995-06-06 1
    2 李四 22 1994-09-26 1
    3 王五 21 1995-03-06 2
    4 赵六 22 1994-08-21 3
     

    2.2.使用JPA注解配置实体类

    部门类:
    package com.axhu.entity;
    import java.util.Set;
    import javax.persistence.*;
     
    @Entity
    @Table(name="department")
    public class Department {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
     
    @Column
    private String name;
     
    @OneToMany
    @JoinColumn(name="department_id")
    private Set<Employee> employees;
    }

    员工类:
    package com.axhu.entity;
    import javax.persistence.*;
    import java.util.Date;
     
    @Entity
    @Table(name="employee ")
    public class Employee {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
     
    @Column
    private String name;
     
    @Column
    private int age;
     
    @Temporal(TemporalType.DATE)
    private Date birthday;
     
    @ManyToOne(fetch=FetchType.LAZY)
    private Department department;
    }

    2.3使用Crteria查询所有部门和所有员工

    查询所有部门
    java代码:
    Criteria criteria = ses.createCriteria(Department.class);
    List<Department> lists= criteria.list();
    产生的SQL:
    select id,name from department
    查询结果:
    人力资源部,财务部,行政部,市场部(以toString方法打印对象,此处简写)

    查询所有员工
    java代码:
    Criteria criteria= ses.createCriteria(Employee.class);
    List<Employee> lists= criteria.list();
    产生的SQL:
    select id,age,birthday,department_id,name from employee
    查询结果:
    张三,李四,王五,赵六(以toString方法打印对象,此处简写)

    总结:
    当session.createCriteria(实体类.class) 就会产生一条select所有列from 表;
    SQL语句,查询实体类对应数据表的所有记录,然后我们就可以在这个Criteria对象上进行条件查询、分页查询、多表关联查询、投影查询、子查询等一系列操作……

    3.Criteria SQL定制详解

    3.1对查询添加条件对象Criterion
    org.hibernate.criterion.Criterion是Hibernate提供的一个面向对象查询条件接口,一个单独的查询就是Criterion接口的一个实例,用于限制Criteria对象的查询,在Hibernate中Criterion对象的创建通常是通过Restrictions 工厂类完成的。
    Restrictions 提供条件查询方法.

    查询姓“张”的所有员工信息
    java代码:
    Criteria criteria= ses.createCriteria(Employee.class);
    criteria.add(Restrictions.like("name", "张%"));
    List<Employee> lists= criteria.list();
    产生的SQL:
    (1).select id,age,birthday,department_id,name
    from employee where name like ?
    (2).select id,name from department where id=?
    查询结果:
    [Employee [id=1, name=张三, age=21, birthday=1995-06-06,
    department=Department [id=1, name=人力资源部]]]

    查询年龄大于21的所有员工
    java代码:
    Criteria criteria= ses.createCriteria(Employee.class);
    criteria.add(Restrictions.gt("age", 21));
    List<Employee> lists= criteria.list();
    产生的主要SQL:
    select id.age,birthday,department_id,name
    from employee where age>?
    查询结果:
    李四,赵六(以toString方法打印对象,此处简写)

    查询年龄小于28的姓“王”的员工
    java代码:
    Criteria criteria= ses.createCriteria(Employee.class);
    criteria.add(Restrictions.lt("age", 28));
    criteria.add(Restrictions.like("name", "王%"));
    List<Employee> lists= criteria.list();
    产生的主要SQL:
    select id,age,birthday,department_id,name
    from employee where age<? and name like ?
    查询结果:
    王五(以toString方法打印对象,此处简写)

    总结:
    对于多个查询条件,Restrictions提供了逻辑组合查询方法。
    and(Criterion lhs, Criterion rhs) 用于生成多个条件and关系SQL语句; 
    or(Criterion lhs, Criterion rhs) 用于生成多个条件or关系SQL语句;
    not(Criterion expression) 用于查询与条件相反的数据,生成not取反查询语句。

    3.2分页操作 firstResult和maxResults 
    Criteria接口提供用于分页查询的方法,实现数据库SQL物理级别的分页操作。
    setFirstResult(int firstResult)设置记录的起始位置0代表第一条记录。
    setMaxResults(int maxResults)设置查询记录的长度。

    查询第1-3条员工记录(共4条记录)
    java代码:
    Criteria criteria = ses.createCriteria(Employee.class);
    criteria.setFirstResult(0);
    criteria.setMaxResults(3);
    List<Employee> lists= criteria.list();
    产生的主要SQL:
    select * from
    ( select id,age,birthday,department_id,name
    from employee)
    where rownum <= ?
    查询结果:
    张三,李四,王五(以toString方法打印对象,此处简写)

    查询第2-4条员工记录(共3条记录)
    java代码:
    Criteria criteria= ses.createCriteria(Employee.class);
    criteria.setFirstResult(1);
    criteria.setMaxResults(3);
    List<Employee> lists = criteria.list();
    产生的主要SQL:
    select * from
    ( select e.*,rownum rum
    from (
    select id,age,birthday,department_id,name
    from employee
    ) e
    where rownum <= ?
    ) where rum > ?
    查询结果:
    李四,王五,赵六(以toString方法打印对象,此处简写)

    3.3排序操作 Order
    Hibernate提供org.hibernate.criterion.Order用于排序操作,Criteria接口提供addOrder(Order order)用于生成排序SQL.

    查询所有员工信息,按照年龄升序(asc)/降序(desc)排列。
    java代码:
    Criteria criteria= ses.createCriteria(Employee.class);
    criteria.addOrder(Order.asc("age"));//Order.desc("age")
    List<Employee> lists = criteria.list();
    产生的主要SQL:
    select id,age,birthday,department_id,name
    from employee order by age asc/desc
    查询结果:(以toString方法打印对象,此处简写)
    asc:王五(21),张三(21),赵六(22),李四(22)
    desc:李四(22),赵六(22),王五(21),张三(21)

    3.4多表关联操作createAlias和createCriteria
    Criteria接口提供createAlias和createCriteria两组方法用于完成多表关联查询.
    createAlias(String associationPath, String alias) 采用内连接关联.
    createAlias(String associationPath, String alias, int joinType)
    可以通过joinType指定连接类型.
    createCriteria(String associationPath) 采用内连接关联(返回新的Criteria对象)。
    createCriteria(String associationPath, int joinType)
    可以通过joinType指定关联类型(返回新的Criteria对象).

    查询部门为“人力资源部”的所有员工(使用createCriteria方法)
    java代码:
    Criteria criteria1 = ses.createCriteria(Employee.class);
    Criteria criteria2 = criteria1.createCriteria("department");
    criteria2.add(Restrictions.eq("name", "人力资源部"));
    List<Employee> lists= criteria.list();
    产生的主要SQL:
    select e.id,e.age,e.birthday,e.department_id,e.name,d.id,d.name
    from employee e
    inner join department d on e.department_id=d.id
    where d.name=?
    查询结果:
    张三,李四(以toString方法打印对象,此处简写)
    注意事项:
    Criteria criteria1和Criteria criteria2一定要是两个对象,不能在一个对象中直接获取.

    总结:
    代码中的criteria对象,是针对employee表,criteria.createCriteria(”department”) 就是建立employee表和department表的内连接.返回的是针对department表新的criteria2对象,这时再对criteria2 添加条件,就是查询department部门表的属性,而不是employee的属性了.

    查询部门为“人力资源部”的所有员工(使用createAlias方法)
    java代码:
    Criteria criteria = ses.createCriteria(Employee.class);
    criteria .createAlias("department","d");
    criteria .add(Restrictions.eq("d.name", "人力资源部"));
    List<Employee> lists = criteria .list();
    产生的主要SQL:
    select e.id,e.age,e.birthday,e.department_id,e.name,d.id,d.name
    from employee e
    inner join department d
    on e.department_id=d.id
    where d.name=?
    查询结果:
    张三,李四(以toString方法打印对象,此处简写)

    3.5.投影、分组查询 Projection
    在实际开发中,进行查询是:可能只需要返回表中的指定列信息(投影)或者进行统计查询(count,avg,sum,min,max).
    Criteria接口提供setProjection(Projection projection)方法用于实现投影查询操作.
    org.hibernate.criterion.Projections工厂类用于返回Projection投影查询对象.

    查询员工表的name和age属性
    java代码:
    Criteria criteria = ses.createCriteria(Employee.class);
    criteria .setProjection(
    Projections.projectionList()
    .add(Projections.property("name"))
    .add(Projections.property("age")));
    List<Object> objs= criteria .list();
    或:
    Criteria criteria = ses.createCriteria(Employee.class);
    criteria .setProjection(
    Projections.projectionList()
    .add(Property.forName("name"))
    .add(Property.forName("age")));
    List<Object> objs= criteria .list();
    产生的主要SQL:
    select name,age from employee
    查询结果:
    Object,Object,Object,Object

    查询员工的总数量
    java代码:
    Criteria criteria = ses.createCriteria(Employee.class);
    criteria.setProjection(Projections.rowCount());
    Long row = (Long) criteria.uniqueResult();
    产生的主要SQL:
    select count(*) from employee
    查询结果:
    4

    总结:
    rowCount() 查询记录总数量;
    count(String propertyName) 统计某列数量;
    countDistinct(String propertyName) 统计某列数(排除重复);
    avg(String propertyName) 统计某列平均值;
    sum(String propertyName) 对某列值求和;
    max(String propertyName) 求某列最大值;
    min(String propertyName) 求某列最小值;

    查询每个部门的员工数量(输出部门的编号和数量)
    java代码:
    Criteria criteria = ses.createCriteria(Employee.class);
    criteria.setProjection(
    Projections.projectionList()
    .add(Projections.groupProperty("department"))
    .add(Projections.count("id")));
    List<Object> lists = criteria.list();
    产生的主要SQL:
    select e.department_id,count(e.id)
    from employee e group by e.department_id
    查询结果:
    Object,Object,Object

    3.6. 设置结果集封装策略 ResultTransformer
    刚刚说了投影操作的使用,其实在Hibernate内部投影查询是会影响到结果集的封装策略的.先用HQL举例说明:
    session.createQuery(”from Employee”).list();//返回 List
    session.createQuery(”select count(e) from Employee e”).list();//返回List
    session.createQuery(”select name,age from Employee”).list();//返回List
    从这几个例子我们不难发现,如果没有指定select语句(没有投影),那么将返回表中的所有字段,返回结果会被封装到Entity实体对象Employee中,一但提供select语句(投影)后,返回的结果类型,将不再封装到Employee对象,而是根据投影的实际类型返回,这就是投影对结果封装策略的影响.
    我们再来看之前写过的Criteria 查询案例:
    session.createCriteria(Employee.class).list();//返回 List
    session.createCriteria(Employee.class).setProjection(
    Projections.projectionList()
    .add(Property.forName(”name”))
    .add(Property.forName(”age”)));//返回 List
    投影之后,返回的结果将不再被封装到Employee对象中,这是为什么呢?
    我们一起来看看 Criteria的接口定义,不难发现在Criteria接口中提供了一个setResultTransformer(ResultTransformer resultTransformer),这个ResultTransformer就是结果集转换策略接口,在Criteria的父接口中CriteriaSpecification定义了几个ResultTransformer的常用实现.
    ALIAS_TO_ENTITY_MAP 将结果集封装到Map对象;
    ROOT_ENTITY 将结果集封装到根实体对象;
    DISTINCT_ROOT_ENTITY 将结果集封装到不重复的根实体对象;
    PROJECTION 根据投影的结果类型自动封装;
    当进行投影查询时,结果的封装策略由ROOT_ENTITY 变为了PROJECTION,所以是根据查询数据进行封装,而不是封装到根对象.
    了解上面原理后,即使只查询name和age属性,也可以封装成List集合.

    使用AliasToBeanResultTransformer 修改结果封装策略,AliasToBeanResultTransformer 会根据返回列自动匹配类中属性名,完成封装
    java代码:
    Criteria criteria = ses.createCriteria(Employee.class);
    criteria.setProjection(
    Projections.projectionList()
    .add(Projections.property("name").as("name"))
    .add(Projections.property("age").as("age")));
    criteria.setResultTransformer(
    new AliasToBeanResultTransformer(Employee.class));
    List<Employee[]> lists = criteria.list();
    产生的主要SQL:
    select e.name,e.age from employee
    查询结果:
    [Employee [id=null, name=张三, age=21, birthday=null, department=null],
    Employee [id=null, name=李四, age=22, birthday=null, department=null],
    Employee [id=null, name=王五, age=21, birthday=null, department=null],
    Employee [id=null, name=赵六, age=22, birthday=null, department=null]]

    4.附录

    测试代码:
    @Test
    public void test() throws HibernateException{
    Session ses = null;
    Transaction tx = null;
    try {
    ses = SessionUtil.getSession();
    tx = ses.beginTransaction();
    Criteria criteria = ses.createCriteria(Department.class);
    List<Department> lists= criteria.list();
    System.out.println(lists);
    tx.commit();
    } catch (HibernateException e) {
    // TODO Auto-generated catch block
    if(null!=tx){
    tx.rollback();
    }
    e.printStackTrace();
    }finally{
    SessionUtil.closeSession(ses);
    }
    }

    工具类:
    public class SessionUtil {
    private static SessionFactory sf = null;
    /**
    * 通过静态代码块来创建SessionFactory
    */
    static{
    try {
    //1.启动hibernate.cfg.xml配置文件,通过Configuration类
    Configuration cfg = new Configuration();
    //2.调用configure()方法来去加载src根目录下面的hibernate.cfg.xml文件
    //注意:configure(String path);//重载方法,用来加载指定目录下的配置文件.
    cfg.configure();
    //3.创建SessionFactory(充当数据源的一个代理,重量级的对象)
    //通常一个数据库只需要配置一个SesisonFactory对象.通过这个
    //对象,可以获取Session(Connection+Cache)
    //3-1.hibernate3.x创建方式(已经过时了)
    //SessionFactory sf = cfg.buildSessionFactory();
    //3-2.hibernate4.x创建方式
    ServiceRegistry sr = new StandardServiceRegistryBuilder()
    .applySettings(cfg.getProperties()).build();
    sf = cfg.buildSessionFactory(sr);
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    throw new RuntimeException(e);
    }
    }
     
    /**
    * 获取Session
    */
    public static Session getSession() throws HibernateException{
    return sf==null?null:sf.openSession();
    }
    /**
    * 关闭session
    */
    public static void closeSession(Session ses){
    if(null!=ses){
    ses.close();
    }
    }
    }

     说明:该文系参考网上,现找不到该原文(我没找到).特此说明,向原博主致敬

    author : ily
    address : anhui hefei
     
     




    出自博客 http://www.cnblogs.com/yunlei0821/,欢迎留言补充指正,仅供互相交流学习.转载请务必保留此出处..

    本文为作者原创 ,欢迎转载和收藏,转载请保留作者及出处,谢谢您的配合,如有侵权,请第一时间联系 yunlei0821@vip.qq.com,以便及时删除...

    晨露&无尘
  • 相关阅读:
    迭代器
    逻辑量词
    How to distinguish between strings in heap or literals?
    Is the “*apply” family really not vectorized?
    power of the test
    The Most Simple Introduction to Hypothesis Testing
    析构函数允许重载吗?
    C++中析构函数的作用
    在C#中的构造函数和解析函数
    c++构造函数与析构函数
  • 原文地址:https://www.cnblogs.com/yunlei0821/p/6601776.html
Copyright © 2011-2022 走看看