SpringData和JPA结合

行吧,爬虫学了一点点了。先等等,把这欠着的SpringData和JPA整合篇写了。说真的这个确实是好用,手下有个小项目这次就打算用SSSP这么一套框架整合来写。我们先简单谈谈SpringData和JPA整合的基本用法。

1、创建Maven项目

这次用流行的Maven来构建项目,包管理起来比较的方便。然后把基本的包创建好,最后结构如下。

pom中的包如下,需要注意的是版本一定要对应,用eclipse我并不会看maven依赖关系,用idea可以清晰的看到包之间的依赖关系,因为用了eclipse,就不截图看了

2、编写配置文件

因为这里只测试持久层所以就不去配置SpringMVC了。

  • spring配置
...
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <context:component-scan base-package="cn.lkangle.*"/>
    
    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.pass}"/>
    </bean>
    
    <!-- 配置entityManagerFactory -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
        </property>
        
        <property name="packagesToScan" value="cn.lkangle.entity"/>
        
        <!-- 配置二级缓存开启方式 -->
        <property name="sharedCacheMode" value="ENABLE_SELECTIVE"/>
        <property name="jpaProperties">
            <props>
                <!-- hibernate相关配置 -->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL55Dialect</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                
                <!-- 配置二级缓存项 -->
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
            </props>
        </property>
    </bean>
    
    <!-- 配置JPA事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>
    <!-- 开启注解式事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!-- 引入springdata jpa的支持 -->
    <jpa:repositories base-package="cn.lkangle.repository" entity-manager-factory-ref="entityManagerFactory"/>
...
  • 加入缓存ehcache配置
    直接导入默认配置使用就行,具体怎么找我在上篇文章中已经写了。

3、编写测试类进行测试

直接一张图解决吧,这里使用了spring提供的test包。需要注意junit版本要在4.5已上。好像就4.5记不太清楚了、、、

4、超级简单的Dao层

只需要我们继承SpringDataJpa提供的各种接口就行,甚至可以不需要任何代码就能完成基本的数据库操作。springdata一共提供了四个基础接口来给我们使用

  • org.springframework.data.repository.Repository最基础的接口,没有任何方法
  • org.springframework.data.repository.CrudRepository为我们默认提供了一套CRUD操作的基本方法
  • org.springframework.data.repository.PagingAndSortingRepository在CRUD的基础上提供分页和排序操作方法
  • org.springframework.data.jpa.repository.JpaRepository继承上一个接口,包括已上所有方法。

除了一上四个比较基础的接口外还提供了一个用来动态查询的接口

  • org.springframework.data.jpa.repository.JpaSpecificationExecutor该接口允许我们很方便的构建出支持动态查询的方法

一般我们直接继承JpaRepository这个接口便能完成常用的CRUD操作。

save 保存,delete删除,saveAndFlush修改,findOne查找一个,findAll查找所有等等。

  • 增删改查实例
      @Test // 保存
      public void testSave() {
          Clazz clazz = new Clazz();
          clazz.setDate(new Date());
          clazz.setName("计科一班");
          Clazz clz = cdao.save(clazz);  // 保存后将返回一个被jpa管理的实例,即持久化实例
          
          Student student = new Student();
          student.setDate(new Date());
          student.setName("李某某");
          student.setPasswd("123456");
          student.setClazz(clz);
          
          sdao.save(student);
      }
      /**
       * 删除,这里需要注意的是,class被设置了外键,不能直接进行删除
       * 需要先解除外键关联,或者设置级联删除才能正常删除
       * @date 2018年3月16日
       */
      @Test
      public void testDelete() {
          // 直接根据id删除
          cdao.delete(1);
          
          // 删除持久化对象
          Clazz clz = cdao.findOne(2);
          cdao.delete(clz);
      }
      /**
       * 修改操作 需要开启事务
       * @date 2018年3月16日
       */
      @Test
      public void testChange() {
          // 通过持久化实例的特性直接修改
          ss.change(3, "张三");
      }
      // service层代码,因为涉及到修改,这是一个事务操作,必须要开启事务才行
      @Transactional
      public void change(Integer id, String name) {
          Student stu = dao.findOne(id);
          stu.setName(name);
      }
      @Transactional
      public void change(Integer id) {
          // 使用saveAndFlush 方法修改,需要注意的是必须每个属性都有值,不然默认空  主键是必须的
          Student student = new Student();
          student.setId(id);
          student.setDate(new Date());
          student.setName("李一");
          student.setPasswd("13212313");
          
          dao.saveAndFlush(student);
      }
    
      /**
       * 简单的查询
       * @date 2018年3月16日
       */
      @Test
      public void testFetch() {
          Student stu = sdao.findOne(3);
          System.out.println(stu.getName());
          
          List<Student> stus = sdao.findAll();
          System.out.println(stus.size());
      }
    

已上是接口提供的crud基本方法,我们还能通过@Query注解和@Modifying注解配合使用通过JPQL进行crud操作。

  /**
   * 删除
   * @date 2018年3月16日
   */
  @Modifying
  @Query("delete from Student where id=?1")
  Integer delete_(Integer id);
  
  /**
   * 修改 
   * @date 2018年3月16日
   */
  @Modifying
  @Query("update from Student set name=?1 where id=?2")
  Integer change_(String name, Integer id);
  
  /**
   * 查询 
   * @date 2018年3月16日
   */
  @Query("select s from Student s where id=?1")
  Student find_(Integer id);

不仅如此SpringData JPA还提供了根据方法名自动构建查询的功能。具体查看官方文档这里就不做示例了。

  • 动态查询
    对于分页查询和动态查询,SpringData JPA也提供了全面的支持,通过继承JpaSpecificationExecutor接口就能轻松实现。
  @Transactional(readOnly = true) 
  public Page<Student> pageQuery(Map<String, Object> map, Integer pageNo) {
      Specification<Student> spec = new Specification<Student>() {
          @Override
          public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
              List<Predicate> ps = new ArrayList<>();
              map.forEach((key, val)->{
                  try {
                      ps.add(cb.equal(root.get(key), val));
                  } catch (Exception e) {
                      log.error("不存在的属性--> {}", e);
                  }
              });
              query.where(ps.toArray(new Predicate[ps.size()]));
              return null;
          }
      };
      /**
       * 这里我们按照id降序,每页5条数据进行分页查询
       * 需要注意这里的pageNo是从0开始的
       */
      Pageable sort = new PageRequest(pageNo - 1, 5, new Sort(new Sort.Order(Direction.DESC, "id")));
      Page<Student> stus = dao.findAll(spec, sort);
      return stus;
  }

这样就简单的构建了一个动态分页查询,更多的查询条件可以在文档中查看,直接查看CriteriaQueryCriteriaBuilder接口源码也能看到。

5、缓存使用也变得简单

  • 根据配置,这里我们需要在被缓存的实体类上添加@Cacheable注解,注意要是JPA的注解

  • 修改dao层,支持缓存
    还记得上篇文章写的,在使用EntityManager来查询时只需要在实体类上加上注解就行,这里也是这样,直接使用接口中提供的方法也是直接加上注解就行。只是那四个标准接口,分页查询动态查询的就不行了,这里要怎么配置我还不太会、、、
    先写一下自定义方法的缓存配置

  /**
   * 查询 
   * @date 2018年3月16日
   * @QueryHints是org.springframework.data.jpa.repository.QueryHints
   * @QueryHint是javax.persistence.QueryHint
   */
  @Query("select s from Student s where id=?1")
  @QueryHints(@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_CACHEABLE, value = "true"))
  Student find_(Integer id);

6、总结

使用SpringData JPA可以简化dao层的开发,使开发者只写少量的代码就能完成复杂的数据库操作,大大加快了开发效率。官方文档链接前面加了,这里在添加一次。官方文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。