Spring boot中spring data jpa的使用

Spring Data JPA

JPA即Java Persistence API,是一个基于O/R映射的标准规范,该规范只负责定义规则的标准(注解或接口),而不需要提供具体实现,具体的实现交由软件提供商来实现,目前主要的JPA提供商为Hibernate,EclipseLink和OperJPA。

Spring Data JPA是Spring Data的一个子项目,通过提供基于JPA的Repository来简化代码量。

其提供了一个org.springframework.data.jpa.repository.JpaRepository,我们的Repository只要继承该JpaRepository,即可享受到JPA带来的好处。

Spring Boot通过spring-boot-starter-data-jpa来提供对JPA的支持,Spring Boot默认的JPA实现者是Hibernate。

Spring Boot项目中使用JPA

创建项目时选择JPA依赖,或者手工将spring-boot-starter-data-jpa添加到pom中。

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

此时项目会自动开启如下两个自动配置类:

  • JpaRepositoriesAutoConfiguration
  • HibernateJpaAutoConfiguration

application.properties中增加jpa相关配置

#datasource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot1?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=newpwd
#spring_jpa
#启动时会根据实体类生成数据表,或者更新表结构,不清空数据,开发阶段使用;validate:表结构稳定后使用,可用于正式环境;
spring.jpa.hibernate.ddl-auto=update
#控制台打印sql
spring.jpa.show-sql=true
#让控制器输出的json格式更美观
spring.jackson.serialization.indent-output=true

在项目中使用JPA时,只需要创建一个继承于JpaRepository的Repository接口,即可拥有JpaRepository及其父类中提供的全部数据访问方法。如果提供的方法不满足业务需要,可以按如下规则扩展数据方法。

package org.springframework.data.jpa.repository;
import java.io.Serializable;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();
    List<T> findAll(Sort var1);
    List<T> findAll(Iterable<ID> var1);
    <S extends T> List<S> save(Iterable<S> var1);
    void flush();
    <S extends T> S saveAndFlush(S var1);
    void deleteInBatch(Iterable<T> var1);
    void deleteAllInBatch();
    T getOne(ID var1);
    <S extends T> List<S> findAll(Example<S> var1);
    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

自定义Repository:PersonRepository,并扩展数据访问方法,具体扩展方法参看示例代码

package com.example.dao;
import com.example.model.Person;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface PersonRepository extends JpaRepository<Person, Integer> {
    //1.以下方法基于属性名称和查询关键字,所以方法名称必须遵循命名规则,并且参数类型要与实体的参数类型一致。
    // 只用于查询方法,以下给出常用的示例
    //等于
    List<Person> findByPName(String PName);
    //And --- 等价于 SQL 中的 and 关键字;  
    List<Person> findByPNameAndPAge(String PName, Integer PAge);
    // Or --- 等价于 SQL 中的 or 关键字;  
    List<Person> findByPNameOrPAge(String PName, Integer PAge);
    //Between --- 等价于 SQL 中的 between 关键字;  
    List<Person> findByPAgeBetween(Integer min, Integer max);
    //LessThan --- 等价于 SQL 中的 "<";  日期类型也可以使用Before关键字
    List<Person> findByPAgeLessThan(Integer max);
    //LessThanEqual --- 等价于 SQL 中的 "<=";
    List<Person> findByPAgeLessThanEqual(Integer max);
    //GreaterThan --- 等价于 SQL 中的">";日期类型也可以使用After关键字
    List<Person> findByPAgeGreaterThan(Integer min);
    //GreaterThanEqual --- 等价于 SQL 中的">=";
    List<Person> findByPAgeGreaterThanEqual(Integer min);
    //IsNull --- 等价于 SQL 中的 "is null";
    List<Person> findByPNameIsNull();
    //IsNotNull --- 等价于 SQL 中的 "is not null";
    List<Person> findByPNameIsNotNull();
    //NotNull --- 与 IsNotNull 等价;  
    List<Person> findByPNameNotNull();
    //Like --- 等价于 SQL 中的 "like";
    List<Person> findByPNameLike(String PName);
    //NotLike --- 等价于 SQL 中的 "not like";
    List<Person> findByPNameNotLike(String PName);
    //OrderBy --- 等价于 SQL 中的 "order by";
    List<Person> findByPNameNotNullOrderByPAgeAsc();
    //Not --- 等价于 SQL 中的 "! =";
    List<Person> findByPNameNot(String PName);
    //In --- 等价于 SQL 中的 "in";
    List<Person> findByPNameIn(String PName);
    //NotIn --- 等价于 SQL 中的 "not in";
    List<Person> findByPNameNotIn(String PName);
    //Top --- 查询符合条件的前两条记录,等价与First关键字
    List<Person> findTop2ByPName(String PName);
    //2.以下方法基于@Query注解,方法名称可以随意,可用于查询和更新方法,更新方法要设置@Modifying注解
    //使用命名参数
    @Query("select p from Person p where p.pName = :name and p.pAge = :age")
    List<Person> withNameAndAgeQuery(@Param("name") String name, @Param("age") Integer age);
    //使用参数索引
    @Query("select p from Person p where p.pName = ?1 and p.pAge = ?2")
    List<Person> withNameAndAgeQuery2(String name, Integer age);
    //删除操作,使用hql,如果要使用sql,需要增加nativeQuery = true
    @Query(value = "delete from Person where pId=?1")
    @Modifying
    int deletePersonById(Integer id);
    //修改操作
    @Query(value = "update Person set pName=?1 where pId=?2 ")
    @Modifying
    int updatePersonName(String name, Integer id);
    //插入操作,使用sql操作
    @Query(value = "insert into person(p_name,p_age) value(?1,?2)",nativeQuery = true)
    @Modifying
    int insertPersonByParam(String name, Integer age);
    //3.以下方法实现分页查询功能,只需要在方法中增加Pageable pageable参数即可,返回结果为Page集合
    Page<Person> findByPNameNot(String name, Pageable pageable);
    //使用命名参数
    @Query("select p from Person p where p.pName = :name ")
    Page<Person> withNameQueryPage(@Param("name") String name, Pageable pageable);
}

POJO实体对象:Person

package com.example.model;
import javax.persistence.*;
import static javax.persistence.GenerationType.IDENTITY;
@Entity   //lombook注解,简化开发
@Table(name = "person")  
public class Person implements java.io.Serializable {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "p_id", unique = true, nullable = false)
    private Integer pId;
    @Column(name = "p_name", length = 45)
    private String pName;
    @Column(name = "p_age")
    private Integer pAge;
    //setter and getter
    @Override
    public String toString() {
        return "Person{" +
                "pId=" + pId +
                ", pName='" + pName + '\'' +
                ", pAge=" + pAge +
                '}';
    }
}

测试演示

package com.example;
import com.example.dao.PersonRepository;
import com.example.model.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.Iterator;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class JpaSingleDatasourceApplicationTests {
    @Autowired
    private PersonRepository personRepository;
    @Test
    public void findByPName() {
        String name = "王五";
        List<Person> list = personRepository.findByPName(name);
        System.out.println(list.size());
        for(Person person : list){
            System.out.println(person);
        }
    }
    @Test
    public void findByPNameAndPAge() {
        String name = "王五";
        int age = 18;
        List<Person> list = personRepository.findByPNameAndPAge(name,age);
        System.out.println(list.size());
        for(Person person : list){
            System.out.println(person);
        }
    }
    @Test
    public void findByPNameOrPAge() {
        String name = "王五";
        int age = 25;
        List<Person> list = personRepository.findByPNameOrPAge(name,age);
        System.out.println(list.size());
        for(Person person : list){
            System.out.println(person);
        }
    }
    @Test
    public void findTop2ByPName() {
        String name = "王五";
        List<Person> list = personRepository.findTop2ByPName(name);
        System.out.println(list.size());
        for(Person person : list){
            System.out.println(person);
        }
    }
    @Test
    public void withNameAndAgeQuery() {
        String name = "王五";
        int age = 18;
        List<Person> list = personRepository.withNameAndAgeQuery(name,age);
        System.out.println(list.size());
        for(Person person : list){
            System.out.println(person);
        }
    }
    @Test
    public void withNameAndAgeQuery2() {
        String name = "王五";
        int age = 18;
        List<Person> list = personRepository.withNameAndAgeQuery2(name,age);
        System.out.println(list.size());
        for(Person person : list){
            System.out.println(person);
        }
    }
    @Test
    public void deletePersonById(){
        int id = 1;
        int result = personRepository.deletePersonById(id);
        System.out.println("result = " + result);
    }
    @Test
    public void updatePersonName(){
        int id = 1;
        String name = "哈哈";
        int result = personRepository.updatePersonName(name,id);
        System.out.println("result = " + result);
    }
    @Test
    public void insertPersonByParam(){
        int age = 10;
        String name = "哈哈";
        int result = personRepository.insertPersonByParam(name,age);
        System.out.println("result = " + result);
    }
    @Test
    public void findByPNameNot(){
        String name = "哈哈";
        //排序
        Sort sort = new Sort(Sort.Direction.DESC, "pId");
        //查询第一页,按一页三行分页
        Pageable pageable = new PageRequest(0, 3, sort);
        Page<Person> pages = personRepository.findByPNameNot(name,pageable);
        System.out.println("pages.getTotalElements()" + pages.getTotalElements());
        System.out.println("pages.getTotalPages()" + pages.getTotalPages());
        Iterator<Person> it=pages.iterator();
        while(it.hasNext()){
            System.out.println("value:"+((Person)it.next()));
        }
    }
    @Test
    public void withNameQueryPage(){
        String name = "王五";
        //排序
        Sort sort = new Sort(Sort.Direction.DESC, "pId");
        //查询第二页,按一页三行分页
        Pageable pageable = new PageRequest(1, 3, sort);
        Page<Person> pages = personRepository.withNameQueryPage(name,pageable);
        System.out.println("pages.getTotalElements()" + pages.getTotalElements());
        System.out.println("pages.getTotalPages()" + pages.getTotalPages());
        Iterator<Person> it=pages.iterator();
        while(it.hasNext()){
            System.out.println("value:"+((Person)it.next()));
        }
    }
}

测试数据库

CREATE SCHEMA `springboot1` DEFAULT CHARACTER SET utf8 ;
CREATE TABLE `springboot1`.`person` (
  `p_id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
  `p_name` VARCHAR(45) NULL COMMENT '姓名',
  `p_age` INT NULL COMMENT '年龄',
  PRIMARY KEY (`p_id`))
ENGINE = InnoDB
COMMENT = '人员信息表';
INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('1', '张三', '20');
INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('2', '李四', '25');
INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('3', '王五', '18');
INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('4', '王五', '18');

JpaSpecificationExecutor查询接口 (动态条件查询)

  • 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法  
  • Specification:封装 JPA Criteria 查询条件。通常使用匿名内部类的方式来创建该接口的对象

Spring Data Jpa 支持 Criteria 查询方式,使用这种方式需要继承 JpaSpecificationExecutor 接口,该接口提供了如下一些方法

T findOne(Specification<T> var1);
List<T> findAll(Specification<T> var1);
Page<T> findAll(Specification<T> var1, Pageable var2);
List<T> findAll(Specification<T> var1, Sort var2);
long count(Specification<T> var1);

调用 JpaSpecificationExecutor 的 Page<T> findAll(Specification<T> var1, Pageable pageable);

Specification: 封装了 JPA Criteria 查询的查询条件

Pageable: 封装了请求分页的信息: 例如 pageNo, pageSize, Sort

通过实现 Specification 中的 toPredicate 方法来定义动态查询,通过 CriteriaBuilder 来创建查询条件

Page<ComicDailyDetail> allInfo = dailyDetailRepo.findAll(new Specification<ComicDailyDetail>() {
                @Override
                public Predicate toPredicate(Root<ComicDailyDetail> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                   /**
                    * @param *root: 代表查询的实体类.
                    * @param query: 可以从中可到 Root 对象, 即告知 JPA Criteria 查询要查询哪一个实体类. 还可以
                    * 来添加查询条件, 还可以结合 EntityManager 对象得到最终查询的 TypedQuery 对象.
                    * @param *cb: CriteriaBuilder 对象. 用于创建 Criteria 相关对象的工厂. 当然可以从中获取到 Predicate 对象
                     * @return: *Predicate 类型, 代表一个查询条件.
                     */ 
                    List<Predicate> list = new ArrayList();
                    Expression<String> dayExp = root.get("day");
                    list.add(cb.equal(dayExp, finalDate));
                    Expression<String> platformIdExp = root.get("platformId");
                    list.add(cb.equal(platformIdExp, platformId));
// add到一起表示且关系,or就是或关系
                    Predicate[] p = new Predicate[list.size()];
                    query.where(list.toArray(p));
                    query.orderBy(cb.desc(root.get(orderTitle)));
                    return null;
                }
            }, pageRequest);


List<Student> stus = studentSpecificationRepository.findAll(new Specification<Student>() {
    @Override
    public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
    //root.get("address")表示获取address这个字段名称,like表示执行like查询,%zt%表示值
    Predicate p1 = criteriaBuilder.like(root.get("address"), "%zt%");
    Predicate p2 = criteriaBuilder.greaterThan(root.get("id"),3);
    //将两个查询条件联合起来之后返回Predicate对象
    return criteriaBuilder.and(p1,p2);
    }
});

可以定义多个 Specification,然后通过 Specifications 对象将其连接起来

//第一个Specification定义了两个or的组合
Specification<Student> s1 = new Specification<Student>() {
    @Override
    public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        Predicate p1 = criteriaBuilder.equal(root.get("id"),"2");
        Predicate p2 = criteriaBuilder.equal(root.get("id"),"3");
        return criteriaBuilder.or(p1,p2);
    }
};
//第二个Specification定义了两个or的组合
Specification<Student> s2 = new Specification<Student>() {
    @Override
    public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        Predicate p1 = criteriaBuilder.like(root.get("address"),"zt%");
        Predicate p2 = criteriaBuilder.like(root.get("name"),"foo%");
        return criteriaBuilder.or(p1,p2);
    }
};
//通过Specifications将两个Specification连接起来,第一个条件加where,第二个是and
List<Student> stus = studentSpecificationRepository.findAll(Specifications.where(s1).and(s2));

这个代码对应的SQL语句是

select * from t_student where (id=2 or id=3) and (address like 'zt%' and name like 'foo%')

CriteriaBuilder 中各个方法的对应

equle : filed = value gt / greaterThan : filed > value lt / lessThan : filed < value ge / greaterThanOrEqualTo : filed >= value le / lessThanOrEqualTo: filed <= value notEqule : filed != value like : filed like value notLike : filed not like value
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值