• 框架学习之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

  • 相关阅读:
    Flink 状态生命周期
    jpa使用@CollectionTable创建表
    Java的四种引用---强软弱虚
    ThreadLocal与内存泄露
    Flink 1.11 Table API 实现kafka到mysql
    FLIink 1.11 SQL 构建一个端到端的流式应用
    Flink1.11编译
    Flink运行yarn-session报错 java.lang.NoClassDefFoundError: org/apache/hadoop/yarn/exceptions/YarnException
    欢迎订阅AI科技导读微信公众号,获取人工智能的最新技术解读教程!
    深度学习深刻理解和应用--人工智能从业人员必看知识
  • 原文地址:https://www.cnblogs.com/yinger/p/2137861.html
Copyright © 2020-2023  润新知