框架学习之Hibernate 第五节 HQL和Criteria查询入门

1.HQL和Criteria简介

HQL: 与SQL语句很相似

面向对象的查询语言,与SQL不同,HQL中的对象名是区分大小写的(除了JAVA类和属性其他部分不区分大小写);

HQL中查的是对象而不是和表,并且支持多态;HQL主要通过Query来操作,Query的创建方式:

Query q = session.createQuery(hql);

hql 可以是类似下面的形式:

from Person

from User user where user.name=?

from User user where user.name=:name and user.birthday < :birthday

 

Criteria:

Criteria是一种比HQL更面向对象的查询方式;Criteria的创建方式:

Criteria crit = session.createCriteria(DomainClass.class);

简单属性条件如:

criteria.add(Restrictions.eq(propertyName, value)),

criteria.add(Restrictions.eqProperty(propertyName,otherPropertyName))

幻灯片13

 

2.实体类或属性名与数据库关键字冲突问题的解决办法

例如,在oracle数据库中user是关键字,表名不能使user

方法一:更改表名

<class name="User" table="t_user">

这样就避免了表名冲突

方法二:添加反引号

<class name="User" table="`user`">

这样user就会当做是字符串处理

 

3.hql的命名参数

setString(int,String)

setString(String,String)

            //方法一:hql中使用?

            String hql = "from User as user where user.name=?";

            Query query = s.createQuery(hql);

            query.setString(0, name);

            

            //方法二:使用替代字符

            String hql = "from User as user where user.name=:name";

            Query query = s.createQuery(hql);

            query.setString("name", name);

还有很多其他的setXxx方法,例如setDate()方法等等,这个要根据domain 类的属性的类型而定

 

4.Query接口的分页方法

分页相关的方法:Projections.rowCount(),criteria.setFirstResult(),criteria.setMaxResults()

//实现分页效果,这个是可移植的,不管使用的那一种数据库,通过配置文件中的 Dialect来完成

            query.setFirstResult(0);

            query.setMaxResults(10);

两个方法分别得到开始的那个和最多得到多少个

 

5.get方法和使用Query方式根据id查用户的区别

get方法可以使用缓存中的数据,但是Query方式不会去缓存中找,所以效率有时会很低

Query方式:
User u1 = null;

Query q = s.createQuery("from User where id="+id);

u1 = (User)q.uniqueResult();

 

get方式:

s = HibernateUtils.getSession();

u = (User)s.get(User.class, id);//默认就是根据id查找

 

6. 离线查询

DetachedCriteria  它的生成和session没有关系,而Criteria和session有关,Criteria是用DetachedCriteria根据session来生成的

DetachedCriteria 一般用于构造不确定的查询条件,它的生成与要查询的对象有关

例如:根据用户的选项:name,age等等信息来查询

dao中代码:

    //根据dc查找

    @Override

    public User findUserByDC(DetachedCriteria dc) {

        User u = null;

        Session s = null;

        try{

            s = HibernateUtils.getSession();

            Criteria cri = dc.getExecutableCriteria(s);

            List<User> users = cri.list();

            for(User user:users){

                System.out.println(user.getName());

            }

        }catch(Exception e){

            e.printStackTrace();

        }finally{

            if(s!=null){

                s.close();

            }

        }

        return u;

    }

测试方法:

    public static void main(String arg[]){

        //插入两条数据

        User u = new User();

        u.setName("yinger");

        u.setBirthday(new Date());

        

        User u0 = new User();

        u0.setName("hibernate");

        u0.setBirthday(new Date());

        

        UserManager um = new UserManager();

        um.saveUser(u);

        um.saveUser(u0);

        

        String name = request.getParameter("name"); // 应用场景这里通常是有 request 的,这里是没有,代码会报错,请注意

        Date birthday = request.getParameter("birthday");

        DetachedCriteria dc = DetachedCriteria.forClass(User.class);

        if(name!=null){

            dc.add(Restrictions.eq("name", name));

        }

        if(birthday!=null){

            dc.add(Restrictions.eq("birthday", birthday));

        }

        um.findUserByDC(dc);

    }

 

7. N+1次查询和懒加载

N+1次查询:本来查出来的数据只有N条,但是执行了N+1条select语句进行查询

造成N+1查询的情况:

1.用Query.iterate可能会有N+1次查询。

2.懒加载时获取关联对象。

3.如果打开对查询的缓存即使用list也可能有N+1次查询。

测试代码:

    public static void main(String[] args) {

        add();

        add();

        System.out.println("-------------");

//        ncpp();

        iterate();

    }



    private static void ncpp() {

        Session s = HibernateUtils.getSession();

        Query q = s.createQuery("from IdCard");

        List<IdCard> ics = q.list();

        for (IdCard ic : ics) {

            System.out.println(ic.getPerson().getName());

            s.clear();

        }

        s.close();

    }

    

    private static void iterate() {

        Session s = HibernateUtils.getSession();

        Query q = s.createQuery("from IdCard");

        Iterator<IdCard> it = q.iterate();

        while(it.hasNext()) {

            System.out.println(it.next().getPerson().getName());

        }

        s.close();

    }



    private static void add() {

        Person p = new Person();

        p.setName("person name");



        IdCard card = new IdCard();

        card.setLife(new Date());



        // p.setIdCard(card); // 外键一对一映射的这种情况下 id_card表中的person_id字段没有值

        card.setPerson(p);



        Session s = null;

        Transaction tr = null;

        try {

            s = HibernateUtils.getSession();

            tr = s.beginTransaction();

            s.save(p);

            s.save(card);



            tr.commit();

        } catch (Exception e) {

            if (tr != null)

                tr.rollback();

        } finally {

            if (s != null)

                s.close();

        }

    }

 

测试一:使用 iterate 方法

Query.iterate 可以从缓存中拿到数据

它查询的方式:首先它会从数据库中查询出所有的id,然后根据id在一级缓存和二级缓存中找,如果缓存中有数据,那么就不用访问数据库了

但是如果缓存中没有数据的话,就要根据id在数据库中一个一个的查,这个时候的效率就会很低

测试结果:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).

log4j:WARN Please initialize the log4j system properly.

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

-------------

Hibernate: select idcard0_.id as col_0_0_ from id_card idcard0_

Hibernate: select idcard0_.id as id1_, idcard0_.life as life4_1_, person1_.id as id0_, person1_.name as name3_0_ from id_card idcard0_ inner join Person person1_ on idcard0_.id=person1_.id where idcard0_.id=?

person name

Hibernate: select idcard0_.id as id1_, idcard0_.life as life4_1_, person1_.id as id0_, person1_.name as name3_0_ from id_card idcard0_ inner join Person person1_ on idcard0_.id=person1_.id where idcard0_.id=?

person name

 

测试二:使用 ncpp 方法

懒加载也可能造成N+1次查询

例如:one-to-one中查询从对象,然后访问从对象对应的主对象的属性值(因为查询从对象时默认是不会查询主对象的,这就是懒加载,但是查询主对象就不会懒加载)

测试结果:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).

log4j:WARN Please initialize the log4j system properly.

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

-------------

Hibernate: select idcard0_.id as col_0_0_ from id_card idcard0_

Hibernate: select idcard0_.id as id1_, idcard0_.life as life4_1_, person1_.id as id0_, person1_.name as name3_0_ from id_card idcard0_ inner join Person person1_ on idcard0_.id=person1_.id where idcard0_.id=?

person name

Hibernate: select idcard0_.id as id1_, idcard0_.life as life4_1_, person1_.id as id0_, person1_.name as name3_0_ from id_card idcard0_ inner join Person person1_ on idcard0_.id=person1_.id where idcard0_.id=?

person name

 

可以看到,两种方法的测试结果是一样的。

横线下面,共有三条select语句,但是数据库中只有两条idCard数据,这个就是 N+1 次查询

第一条语句查出所有的id,后面的两条根据查出来的id来查person表

 

解决方法:

1.可以利用二级缓存,也就是说,确定数据已经存入到了二级缓存中时才这么查询,否则效率是很低的

2.改变配置方式,例如改变关联对象的fetch的方式,如果改成 join 就不会有 N+1 次查询了

测试实例:IdCard.hbm.xml中配置

<one-to-one name="person" class="Person" constrained="true" fetch="select"/>

测试代码:

    public static void main(String[] args) {

        add();

        add();

        System.out.println("-------------");

        IdCard card = queryIdCardById(1);

        System.out.println(card.getPerson().getName());

    }



    // 添加信息到数据库中

    private static void add() {

        Person p = new Person();

        p.setName("person name");



        IdCard card = new IdCard();

        card.setLife(new Date());



        // p.setIdCard(card); // 外键一对一映射的这种情况下 id_card表中的person_id字段没有值

        card.setPerson(p);



        Session s = null;

        Transaction tr = null;

        try {

            s = HibernateUtils.getSession();

            tr = s.beginTransaction();

            s.save(p);

            s.save(card);



            tr.commit();

        } catch (Exception e) {

            if (tr != null)

                tr.rollback();

        } finally {

            if (s != null)

                s.close();

        }

    }



    // 根据id查询IdCard

    public static IdCard queryIdCardById(int id) {

        IdCard card = null;

        Session s = null;

        try {

            s = HibernateUtils.getSession();

            card = (IdCard) s.get(IdCard.class, id);

//            Hibernate.initialize(card); // 可以使用这个方法来初始化代理对象

            System.out.println(card.getPerson().getName());

            return card;

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            if (s != null) {

                s.close();

            }

        }

        return card;

    }

输出结果:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).

log4j:WARN Please initialize the log4j system properly.

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

-------------

Hibernate: select idcard0_.id as id0_, idcard0_.life as life4_0_ from id_card idcard0_ where idcard0_.id=?

Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=?

person name

person name

查询了 两次,第一次查idcard,第二次查person

如果配置改为:

<one-to-one name="person" class="Person" constrained="true" fetch="join"/>

重新测试:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).

log4j:WARN Please initialize the log4j system properly.

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

Hibernate: insert into Person (name) values (?)

Hibernate: insert into id_card (life) values (?)

-------------

Hibernate: select idcard0_.id as id1_, idcard0_.life as life4_1_, person1_.id as id0_, person1_.name as name3_0_ from id_card idcard0_ inner join Person person1_ on idcard0_.id=person1_.id where idcard0_.id=?

person name

person name

查询了一次,不过使用的是内联接  inner join

其他问题:

HQL

1查询多个对象select art, user from Article art, User user where art.author.id=user.id and art.id=:id

这种方式返回的是Object[],Object[0]:article,Object[1]:user。

2分页query.setFirstResult,query.setMaxResults.

查询记录总数 query.iterate(“select count(*) from Person”).next()

3批量更新query.executeUpdate(),可能造成二级缓存有实效数据。

 

Criteria

1排序Criteria.addOrder(Order.desc(propertyName));

2关联查询criteria.setFetchMode(“propertyName”, FetchMode.SELECT)与映射文件中关联关系的fetch作用一致。

3投影Projections.rowCount(),max(propertyName), avg, groupProperty…

4分页Projections.rowCount(),criteria.setFirstResult(),criteria.setMaxResults()

5DetachedCriteria可在session外创建(在其他层创建比如在Service中创建)然后用getExecutableCriteria(session)方法创建Criteria对象来完成查询。

6Example查询,Example.create(obj);criteria.add(example)。

463

你可能感兴趣的:(Hibernate)