关于jpa:Spring-Data-JPA使用看这一篇就够了

41次阅读

共计 7090 个字符,预计需要花费 18 分钟才能阅读完成。

简介

首先理解 Spring Date JPA 是什么?

SpringData:其实 SpringData 就是 Spring 提供了一个操作数据的框架。而 SpringData JPA 只是 SpringData 框架下的一个基于 JPA 规范操作数据的模块。
SpringData JPA:基于 JPA 的规范数据进行操作。简化操作长久层的代码。只须要编写接口就能够。

JPA 是 Spring Data 下的子项目,JPA 是 Java Persistence API 的简称,中文名为 Java 长久层 API,是 JDK 5.0 注解或 XML 形容对象-关系表的映射关系,并将运行期的实体对象长久化到数据库中

你能够了解为 JPA 和 Mybatis 是起雷同作用的, 都是长久层的框架, 然而因为当初 Mybatis 的广泛应用, 当初理解和应用 JPA 的人较少.

但在我应用的过程中, 也发现其一些劣势.

整合

1. 导入 jar 包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2.yml 配置文件

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mytest
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver // 驱动
  jpa:
    hibernate:
      ddl-auto: update // 自动更新
    show-sql: true  // 日志中显示 sql 语句

这里留神:
jpa:hibernate:ddl-auto: update是 hibernate 的配置属性,其次要作用是:主动创立、更新、验证数据库表构造。该参数的几种配置如下:
1.·create:每次加载 hibernate 时都会删除上一次的生成的表,而后依据你的 model 类再从新来生成新表,哪怕两次没有任何扭转也要这样执行,这就是导致数据库表数据失落的一个重要起因。
2.·create-drop:每次加载 hibernate 时依据 model 类生成表,然而 sessionFactory 一敞开, 表就主动删除。
3.·update:最罕用的属性,第一次加载 hibernate 时依据 model 类会主动建设起表的构造(前提是先建设好数据库),当前加载 hibernate 时依据 model 类自动更新表构造,即便表构造扭转了但表中的行依然存在不会删除以前的行。要留神的是当部署到服务器后,表构造是不会被马上建设起来的,是要等利用第一次运行起来后才会。
4.·validate:每次加载 hibernate 时,验证创立数据库表构造,只会和数据库中的表进行比拟,不会创立新表,然而会插入新值。
我在首次创立时会设为 create, 创立好后改为 validate.

3. 实体类

既然上边的参数能够帮忙咱们主动的去通过实体类来创立保护表, 那么实体类该怎么写呢, 又是怎么建设与表的映射

简略的创立一个实体类:get/set 办法由注解实现

@Entity
@Getter
@Setter
@Table(name = "person")
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "name", length = 20)
    private String name;

    @Column(name = "agee", length = 4)
    private int age;
}

创立好实体类并标注好注解后启动主启动类, 应该就会在你配置的数据库中主动生成表.

4.Repository 接口

personRepository 接口如下,

若只是简略的对单表进行 crud 只须要继承 JpaRepository 接口, 传递了两个参数:1. 实体类,2. 实体类中主键类型

public interface PersonRepository extends JpaRepository<Person, Long> {}

然而当然, 咱们在工作应用中, 不可能只是简略的依据字段查一下就能够了, 当你须要传入整个实体类, 在依据其中的所有属性进行动静简单查问, 那仅仅继承这个接口就不能满足咱们的需要了,

就须要咱们再去继承 JpaSpecificationExecutor<T> 该接口, 泛型内传入实体类, 只有简略实现 toPredicate 办法就能够实现简单的查问,

该接口中提供了几个办法:

public interface JpaSpecificationExecutor<T> {T findOne(Specification<T> spec);

    List<T> findAll(Specification<T> spec);

    Page<T> findAll(Specification<T> spec, Pageable pageable);

    List<T> findAll(Specification<T> spec, Sort sort);

    long count(Specification<T> spec);
}

办法中的 Specification 就是须要咱们传进去的参数,它是一个接口, 也是咱们实现简单查问的要害, 其中只有一个办法toPredicate


public interface Specification<T> {Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

其 api 编写能够参考:
http://blog.csdn.net/dracotianlong/article/details/28445725
http://developer.51cto.com/art/200911/162722.htm

5.Controller

而后咱们能够间接在 controller 中编写代码即可(如果业务简单, 当然如果 service 层也是最好).

简略 crud:

@RestController
@RequestMapping(value = "person")
public class PerconController {

    @Autowired
    private PersonRepository personRepository;

    @PostMapping(path = "addPerson")
    public void addPerson(Person person) {personRepository.save(person);
    }

    @DeleteMapping(path = "deletePerson")
    public void deletePerson(Long id) {personRepository.delete(id);
    }
}

简略的 crud 甚至不须要在 Repository 中写代码,JpaRepository 中已有封装好的间接应用即可.

那么咱们怎么本人去编写一些简略的代码呢?

咱们以依据 name 查问 person 为例:
在 repository 接口中增加如下查询方法:
1. 留神办法名肯定是 findBy+ 属性名

Person findByName(String name);

还须要留神依据 ID 查找的 findById 是不必本人增加办法的, 由接口曾经封装, 然而源码中返回的是 Optional 类型。那么这个时候该如何取得 T 实体类类型呢, 只须要 get()即可, 就是findById(Id).get() 即返回 T 类型

2. 除了增加 findBy 这种不必写 sql 的办法外, 还有一种能够本人编写 sql 的办法:

能够在所增加的办法上通过 @Query 注解, 在 value 属性上写 sql 语句来实现对数据库的操作,

带参查问:(1、依据参数地位 2、依据 Param 注解)

   /**
     * 查问依据参数地位
     * @param name
     * @return
     */
    @Query(value = "select * from person  where name = ?1",nativeQuery = true)
    Person findPersonByName(String Name);
 
    /**
     * 查问依据 Param 注解
     * @param name
     * @return
     */
    @Query(value = "select p from person p where p.uname = :name")
    Person findPersonByNameTwo(@Param("name") String name);

置信大家也留神到, 在 @Query 中传入了一个属性 nativeQuery,

  • @Query 有 nativeQuery=true,示意可执行的原生 sql,原生 sql 指能够间接复制 sql 语句给参数赋值就能运行
  • @Query 无 nativeQuery=true,示意不是原生 sql,查问语句中的表名则是对应的我的项目中实体类的类名

这里去理解了一下其生成 sql 的原理:

其实 JPA 在这里遵循 Convention over configuration(约定大概配置)的准则,遵循 spring 以及 JPQL 定义的办法命名。Spring 提供了一套能够通过命名规定进行查问构建的机制。这套机制会把办法名首先过滤一些关键字,比方 find…By, read…By, query…By, count…By 和 get…By。零碎会依据关键字将命名解析成 2 个子语句,第一个 By 是辨别这两个子语句的关键词。这个 By 之前的子语句是查问子语句(指明返回要查问的对象),前面的局部是条件子语句。如果间接就是 findBy… 返回的就是定义 Respository 时指定的畛域对象汇合,同时 JPQL 中也定义了丰盛的关键字:and、or、Between 等等,上面咱们来看一下 JPQL 中有哪些关键字:

Keyword Sample JPQL snippet

  1. And—-findByLastnameAndFirstname—-where x.lastname = ?1 and
  2. Or—-findByLastnameOrFirstname—-where x.lastname = ?1 or x.firstname = ?2
  3. Is,Equals—-findByFirstnameIs,findByFirstnameEquals—-where x.firstname = ?1
  4. Between—-findByStartDateBetween—-where x.startDate between ?1 and ?2
  5. LessThan—-findByAgeLessThan—-where x.age < ?1
  6. LessThanEqual—-findByAgeLessThanEqual—-where x.age ⇐ ?1
  7. GreaterThan—-findByAgeGreaterThan—-where x.age > ?1
  8. GreaterThanEqual—-findByAgeGreaterThanEqual—-where x.age >= ?1
  9. After—-findByStartDateAfter—-where x.startDate > ?1
  10. Before—-findByStartDateBefore—-where x.startDate < ?1
  11. IsNull—-findByAgeIsNull—-where x.age is null
  12. IsNotNull,NotNull—-findByAge(Is)NotNull—-where x.age not null
  13. Like—-findByFirstnameLike—-where x.firstname like ?1
  14. NotLike—-findByFirstnameNotLike—-where x.firstname not like ?1
  15. StartingWith—-findByFirstnameStartingWith—-where x.firstname like ?1 (parameter bound with appended %)
  16. EndingWith—-findByFirstnameEndingWith—-where x.firstname like ?1 (parameter bound with prepended %)
  17. Containing—-findByFirstnameContaining—-where x.firstname like ?1 (parameter bound wrapped in %)
  18. OrderBy—-findByAgeOrderByLastnameDesc—-where x.age = ?1 order by x.lastname desc
  19. Not—-findByLastnameNot—-where x.lastname <> ?1
  20. In—-findByAgeIn(Collection ages)—-where x.age in ?1
  21. NotIn—-findByAgeNotIn(Collection age)—-where x.age not in ?1
  22. TRUE—-findByActiveTrue()—-where x.active = true
  23. FALSE—-findByActiveFalse()—-where x.active = false
  24. IgnoreCase—-findByFirstnameIgnoreCase—-where UPPER(x.firstame) = UPPER(?1)

简单 crud:
简单 crud 的查问是依附 JpaSpecificationExecutor<T> 接口, 以及 specification 的 toPredicate 办法来增加条件, 上文中也根本介绍过, 所以在这里就简略贴一下代码, 大家依据例子, 应该就能够本人写了:


    public List<Flow> queryFlows(int pageNo, int pageSize, String status, String userName, Date createTimeStart, Date createTimeEnd) {
        List<Flow> result = null;

        // 结构自定义查问条件
        Specification<Flow> queryCondition = new Specification<Flow>() {
            @Override
            public Predicate toPredicate(Root<Flow> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {List<Predicate> predicateList = new ArrayList<>();
                if (userName != null) {predicateList.add(criteriaBuilder.equal(root.get("currentOperator"), userName));
                }
                if (status != null) {predicateList.add(criteriaBuilder.equal(root.get("status"), status));
                }
                if (createTimeStart != null && createTimeEnd != null) {predicateList.add(criteriaBuilder.between(root.get("createTime"), createTimeStart, createTimeEnd));
                }
                if (orderId!= null) {predicateList.add(criteriaBuilder.like(root.get("orderId"), "%" + orderId+ "%"));}
                return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
            }
        };

        // 分页和不分页,这里按起始页和每页展现条数为 0 时默认为不分页,分页的话按创立工夫降序
       
        if (pageNo == 0 && pageSize == 0) {result = flowRepository.findAll(queryCondition);
        } else {result = flowRepository.findAll(queryCondition, PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "createTime"))).getContent();}
       
        return result;
    }

了解了之后其实很简略, 上边次要就是两部:1. 先将你所须要的条件加到 predicate 汇合中去, 例子中也有 equal/between/like 相等 / 区间 / 含糊, 根本也是平时应用的几个, 增加好条件后 2. 进行了分页, 判断有没有传入分页的参数, 所有传了就分页, 没传就查全副, 分页中有一个 getContent(), 能够不加, 不加的话还会返回页数 / 总条数等一些分页的参数, 加这个办法就只返回 list 汇合.

总结

我也是这两天忽然在工作中须要用到 JPA, 平时也都是应用 Mybatis, 然而找了很多文章材料, 根本也是应用起来曾经没有问题了, 也实现了业务, 所以根底的应用看着一篇就够了, 大家有问题的话也欢送多多斧正!

正文完
 0