hibernate5关联查询和条件查询以及统计查询

基本的增删改查

class CURDTest {
    //实例化配置对象,Configuration()方法加载hbm.properties文件的,configure()方法加载配置文件hibernate.cfg.xml
    Configuration configuration=new Configuration().configure();
    //,读取连接参数(数据库方言,hbm加载,)sessionFactory内置连接池,创建会话连接工厂
    SessionFactory sessionFactory=configuration.buildSessionFactory();
    //创建会话(从连接池随机选择一个连接,构建session对象【会话】,一会话--一连接【一级缓存】)
    Session session=sessionFactory.openSession();
    //开启事务,等价于connect.setAutoCommit(false)
    Transaction transaction= session.beginTransaction();

    /**
     *对上述代码封装成HibernateUitl工具类:如下
     *
    public class HibernateUtil {
        private static Configuration configuration;
        private static SessionFactory sessionFactory;
        static {
            //只对sessionFactory初始化一次
            configuration=new Configuration().configure();
            sessionFactory=configuration.buildSessionFactory();
        }

         //获得hibernate操作对象

        public static Session openSession(){
            return sessionFactory.openSession();
        }
        //服务层就不用再sessionFactory.close,关闭sessionFactory
    }
    */
    @Test
    void add() {
            GoodsEntity goodsEntity = new GoodsEntity();
            goodsEntity.setCreateTime(new Date());
            goodsEntity.setGoodsDesc("鼠标纯黑");
            goodsEntity.setGoodsName("鼠标");
            session.persist(goodsEntity);
            transaction.commit();
            session.close();
    }

    @Test
    void delete() {
        GoodsEntity goodsEntity=new GoodsEntity();
        goodsEntity.setGoodsId(3);
        session.delete(goodsEntity);
        transaction.commit();
        session.close();
    }

    @Test
    void update() {
        GoodsEntity goodsEntity=new GoodsEntity();
        goodsEntity.setGoodsId(3);
        goodsEntity.setGoodsDesc("鼠标超酷型");
        goodsEntity.setGoodsName("打鼠标");
        session.update(goodsEntity);
        transaction.commit();
        session.close();
    }

    @Test
    void select() {
       //session.createCriteria()方法过时的原因
       // Hibernate-specific标准特性将移植作为JPA javax.persistence.criteria.CriteriaQuery扩展
       //替换后的解决方案是:criteriaQuery 对象可以添加各种查询条件和关联条件等等
        CriteriaQuery<GoodsEntity> criteriaQuery = session.getCriteriaBuilder().createQuery(GoodsEntity.class);
        criteriaQuery.from(GoodsEntity.class);
        List<GoodsEntity> listUser = session.createQuery(criteriaQuery).getResultList();
        for (GoodsEntity goodsEntity:listUser){
            System.out.println(goodsEntity);
        }
        session.close();
    }
}

统计查询+条件查询之count()和sum()

原生sql:

select  count(id)  from user where id > 2

使用CriteriaBuilder动态构造上述语句

   @Test
    public void whereAndCount() {
/**
 * 使用Spring JPA提供的方法只能进行简单的CRUD,如果遇到复杂的情况就需要我们使用CriteriaBuilder来动态来构建查询条件了
 * CriteriaBuilder对象提供了----统计函数(sum,count),表达式函数(like,ge,equal,and,between and,asc/desc,diff,in)
 * Predicate  这是条件表达式 相当于where语句
 */
        EntityManager entityManager = session.getEntityManagerFactory().createEntityManager();
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);
        
        //query.from类似于sql中的from语句,from user
        Root<User> root = query.from(User.class);
       
        // query.select(criteriaBuilder.count(root.get(“id”)))等价于select count(id)。如果执行query.select(root)则等价于select *。
        query.select(criteriaBuilder.count(root.get("id")));

        //该predicate也就是在where后面的条件子句
        Predicate predicate = criteriaBuilder.gt(root.get("id"), 2);
        query.where(predicate);
        
        Long singleResult = entityManager.createQuery(query).getSingleResult();
        System.out.println(singleResult);
    }

查询出来的结果是3

使用原生sql进行关联查询并完成参数传递,结果封装,结果装json输出

 @Test
    public void mutiQuery() throws JsonProcessingException {

       EntityManager entityManager = session.getEntityManagerFactory().createEntityManager();
        String selectString = "select s.title stitle,m.title mtitle from sub_forum s" +
                " inner join main_forum m on s.main_forum = m.id" +
                " where s.id>?0 limit 0,10";
        Query query=entityManager.createNativeQuery(selectString);
        query.setParameter(0,1);
        List<Map<String,Object>>listMap=new ArrayList<>();
        List<Object[]> resultList = query.getResultList();
       
        for (Object[]obj:resultList){
            Map<String,Object>map=new HashMap<String,Object>();
        
            map.put("mtitle",obj[0]);
            map.put("stitle",obj[1]);
            listMap.add(map);
        }
        System.out.println(listMap);
        /**

         ObjectMapper是JSON操作的核心,Jackson的所有JSON操作都是在ObjectMapper中实现。
         ObjectMapper有多个JSON序列化的方法,可以把JSON字符串保存File、OutputStream等不同的介质中。
         writeValue(File arg0, Object arg1)把arg1转成json序列,并保存到arg0文件中。
         writeValue(OutputStream arg0, Object arg1)把arg1转成json序列,并保存到arg0输出流中。
         writeValueAsBytes(Object arg0)把arg0转成json序列,并把结果输出成字节数组。
         writeValueAsString(Object arg0)把arg0转成json序列,并把结果输出成字符串。
         */
        ObjectMapper mapper = new ObjectMapper();
        String jsonList = mapper.writeValueAsString(listMap);
        System.out.println(jsonList);
    }

json转换需要添加jackjson的jar包(参见文章:https://blog.csdn.net/weixin_33971130/article/details/87218028)

  <!--引入jackjson所需基本jar包-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.1</version>
    </dependency>

使用jpql/hql 语句完成关联查询

@Test
    public void add2(){
      Query query=session.createQuery("select m.title ,s.title" +
               " FROM MainForumEntity as m left join SubForumEntity as s on m.id=s.mainForum where s.id>?1");
       query.setParameter(1,2);
       List<Object>list=query.getResultList();
       System.out.println(list);
   }

对两个实体进行关联映射 OneToMany、ManyToOne完成关联查询

这种关联查询方式应该可以说是hibernate的一大特色了。它还提供了延迟加载机制。(默认是开启改机制的,如果我只想查一级板块的标题,则它就不会关联查询出二级板块的内容,只会单表查询一级板块。如果关闭了该机制,不管有没有用到二级板块的内容,它都会去做关联查询)

比如:
我有一级板块MainForumEntity和二级板块SubForumEntity,现在我想关联查出一级,二级板块的标题。
通过配置他们二者的多对一和一对多来实现

MainForumEntity

@Entity
@Table(name = "main_forum", schema = "bbs", catalog = "")
public class MainForumEntity {
  /*配置一对多的关系,使用无序去重的集合set<>*/
    private Set<SubForumEntity>sfeSet=new HashSet<SubForumEntity>();
    
    public Set<SubForumEntity> getSfeSet() {
        return sfeSet;
    }
    public void setSfeSet(Set<SubForumEntity> sfeSet) {
        this.sfeSet = sfeSet;
    }
}

MainForumEntity.hbm.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
   <class name="com.bbs.pojo.MainForumEntity" table="main_forum" schema="bbs">
        <set name="sfeSet">
           <!-- hibernate是双向维护外键,也是双向维护表的关系,一方是一对多,另一方就必然是多对一了-->
            <key column="main_forum"></key>
            <one-to-many class="com.bbs.pojo.SubForumEntity"></one-to-many>
         </set>
    </class>
</hibernate-mapping>

SubForumEntity

@Entity
@Table(name = "sub_forum", schema = "bbs", catalog = "")
public class SubForumEntity {
    private MainForumEntity mainForumEntity;

    public MainForumEntity getMainForumEntity() { return mainForumEntity;}
    public void setMainForumEntity(MainForumEntity mainForumEntity) {this.mainForumEntity = mainForumEntity;}
}

SubForumEntity.hbm.xml

<class....>
     <many-to-one name="mainForumEntity" class="com.bbs.pojo.MainForumEntity" column="main_forum"/>
</class....>

测试如下:

@Test
    public void query3(){
       EntityManager entityManager=session.getEntityManagerFactory().createEntityManager();
        SubForumEntity sb=entityManager.find(SubForumEntity.class,5);
        System.out.println("语句1查询结果: "+sb.getTitle());                      //语句1
        System.out.println("语句2查询结果: "+sb.getMainForumEntity().getTitle());  //语句2
    }

生成的sql如下:
在这里插入图片描述
如果注释掉语句2,则就不会查询一级板块的表,程序只会按需查询(注释语句2后的结果)
在这里插入图片描述

使用CriteriaBuilder构造多表关联查询

hibernate中配置关系映射 才能支持外连接,否则只支持内连接 如 from a,b where …或者a inner join b on …
jpql/hql 对于写sql而言,它是半面向对象的,
而 CriteriaBuilder 对于sql是完全面向对象的----通过它可以做到程序的jdbc操作可通过完全无sql来实现。-------------------------------Criteria【中文意思:标准–可理解为查询对象】Criteria是一种比hql更面向对象的查询方式.Criteria可以使用Criterion和Projection设置查询条件。可以设置FetchMode(联合查询抓取的模式),设置排序方式,Criteria还可以设置FlushModel(冲刷会话的方式)和LockMode


我们有三种方式关闭懒加载

  1. 将该类的lazy设置为false,这种方式我是不推荐的,因为有时需要懒加载,有时不需要。
    SubForumEntity.hbm.xml
 <many-to-one lazy="false" name="mainForumEntity" class="com.bbs.pojo.MainForumEntity" column="main_forum"/>

MainForumEntity.hbm.xml

 <set name="sfeSet" lazy="false">
        .......
   </set>
  1. 通过 Hibernate.initialize();这个方法强制去加载.

  2. 通过opensessioninview的方式(延长了session的生命周期),其实就是一个过滤器,就是每次在发送请求的时候,打开session.那么在view层就能获取关联类了,然后请求结束,关闭session,这个方式应该算是目前主流的解决懒加载的方式,唯一的缺点就是延长了session的生命周期。

这里采用hibernate自带的OpenSessionInViewFilter代码如下

 <!-- opsessioninview -->
  <filter>
    <filter-name>openSessionInview</filter-name>
    <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter </filter-class>
    <init-param>
      <param-name>sessionFactoryBeanName</param-name>
      <param-value> sessionFactory</param-value>
    </init-param>
    <init-param>
       <param-name>singleSession</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>openSessionInview</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

QBC做统计,关联,条件,分页查询

  1. QBC操作的是实体类和属性
  2. 使用hql查询需要写sql,可以用QBC通过调方法来实现无sql效果
    新版中使用的CriteriaBuilder类来实现

select * from A,B where a.id=b.aid 这是属于内连接,还有inner join

验证hibernate的一级缓存

数据库是一个文件系统(使用流方式操作文件,效率并不高----解决方式:将数据直接存到内存中,就可以不用流方式,直接从内存中去就ok了-----把数据存到内存中,从而提高读取效率,这就叫缓存----例如:第一次访问网站很慢,第二次很快,以为在第一次访问后,就 发生了缓存),数据存在数据库。读取文件是通过流来实现。

hibernate默认打开的是一级缓存,作用范围是session的范围,创建session,创建session就有一级缓存Session session=SessionFactory.openSession(); 关闭session,【session.close】一级缓存就没有了,只有持久化的对象才会存入到缓存

二级缓存已经被redis替换了—hibernate没有三级,只有一级,二级缓存

 @Test
    public void query4(){
       UserEntity userEntity1=session.get(UserEntity.class,1);
       System.out.println(userEntity1);
        UserEntity userEntity2=session.get(UserEntity.class,1);
        System.out.println(userEntity2);
   }

我在程序中做了两次请求,但程序之打印了一条sql,说明第一次userEntity1是从数据库中查询取出,第二次userEntity2是从一级缓存中查询取出
在这里插入图片描述

执行过程:先查询一级缓存,一级缓存没有,再去查数据库,得到持久化的对象,并将其(里面的值)放入到一级缓存。存入缓存的不是对象,而是将里面的值(以键值对)存入到某个内存空间(并打上标签:标签名是id值,即查询条件),第二次如果查询(条件id相同)相同,则直接从内存中取出这些键值对的值放入(调用对象的setter方法)到新的对象里返回,即user2

一级缓存的特性:缓存中的持久化对象会自动更新数据库

  @Test
    public void AutoUpdate(){
        /**
         * 第一次查出来,会将其同时放入两个地方,一级缓存和快照区(一级缓存的副本)
         */
        UserEntity userEntity1=session.get(UserEntity.class,1);
        System.out.println(userEntity1);
        /**
         * 更新持久化对象的值,同时也会更新一级缓存里面的值,但不会更新快照区里的值
         */
        userEntity1.setUsername("我是更名后的火星人");
        /**
         * 事务:操作中的基本单元,都成功,都失败(的操作)----原子性,一致性,隔离性,持久性
         * 无隔离性的问题:脏读,虚读,不可重复读
         *
         * 设置事务隔离级别
         * mysql默认级别是repeatable read可重复读---在Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题,幻读就是读出了不存在的数据
         *
         * 事务一提交,一级缓存就会发出updaate语句自动去更新数据库的值
         * 查询操作不存在事务,所以查询不需要transaction.commit();
         * update,insert,delete需要事务的支持,所以执行更新,插入,删除操作后需要提交事务,上述操作才会生效
         * 事务提交,会进行一级缓存和快照区的值对比,如果相同,则不会发update语句,不同才会发update自动更新数据库的值
         */
        transaction.commit();
        System.out.println(userEntity1);
        session.close();
    }

在这里插入图片描述

事务的规范代码的写法

不规范的写法

 Configuration configuration=new Configuration().configure();
        SessionFactory sessionFactory=configuration.buildSessionFactory();
        Session session=sessionFactory.openSession();
        Transaction transaction= session.beginTransaction();
        userEntity1.setUsername("我是更名后的火星人");
        int i=1/0;      //?????????????????????????????????????????????????????????异常代码,下面的代码不会被执行
        transaction.commit();
        System.out.println(userEntity1);
        session.close();
        sessionFactory.close();
    }

规范的写法:

  @Test
    public void standardWrite(){
       SessionFactory sessionFactory=null;
       Transaction transaction=null;
       Session session=null;
       try{
           sessionFactory=configuration.buildSessionFactory();
           session=sessionFactory.getCurrentSession();
           //开启事务
           transaction=session.beginTransaction();
           UserEntity userEntity=new UserEntity();
           userEntity.setUsername("标准的事务代码规范者");
           session.save(userEntity);
           //提交事务
           transaction.commit();
       }catch (Exception e){
           //回滚事务
           transaction.rollback();
       }finally {
           //关闭
           session.close();
           sessionFactory.close();
       }
    }

参考文章:
Spring JPA使用CriteriaBuilder动态构造查询
https://blog.csdn.net/zhaoruda/article/details/80157975

扼杀性能的 10 个常见 Hibernate 错误
http://www.codeceo.com/article/10-hibernate-mistake-performance.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值