SpringBoot之Data JPA介绍、开发

来源:互联网 发布:巨人网络最新招聘信息 编辑:程序博客网 时间:2024/06/06 03:20

一、Spring Data JPA
1.什么是Spring Data JPA
Spring Data JPA是Spring Data的一个子项目,它通过提供基于JPA的Repository极大地减少了JPA作为数据访问方案的代码量。

2.Spring Data JPA定义数据访问层方法
使用Spring Data JPA建立数据访问层十分简单,只需定义一个继承JpaRepository的接口即可,定义如下:

public interface DemoRepository extends JpaRepository<Demo, Long> {    //定义数据访问操作的方法}

而继承JpaRepository接口意味着我们默认已经有了下面的数据访问操作方法:

@NoRepositoryBeanpublic 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);}

3.在springboot中使用
在springboot中已经为jpa做了自动配置,如下图:
这里写图片描述
这里写图片描述
通过上面的截图,从HibernateJpaAutoConfiguration可以看出,SpringBoot默认JPA的实现者是Hibernate;HibernateJpaAutoConfiguration依赖于DataSourceAutoConfiguration。

4.定义查询方法

a. 根据属性名查询:支持通过定义在Repository接口中的方法名来定义查询,而方法名是根据实体类的属性名来确定的。

public interface DemoRepository extends JpaRepository<Demo, Long> {    /**    *    * 通过名字相等查询,参数为name    * 相当于JPQL:select d from Demo d where d.name=?1    */    List<Demo> findByName(String name);    /**    *    * 通过名字like查询,参数为name    * 相当于JPQL:    select d from Demo d where d.name like ?1    */    List<Demo> findByNameLike(String name);    /**    *    * 通过名字和地址查询,参数为name和address    * 相当于JPQL:select d from Demo d where d.name=?    1 and p.address=?2    */    List<Demo> findByNameAndAddress(String name,String address);    /**    * 获得符合查询条件的前10条数据    */    List<Demo> findFirst10ByName(String name);    /**    * 获得符合查询条件的前30条数据    */    List<Demo> findTop30ByName(String name);}

从代码可以看出,这里使用了findBy、Like、And这样的关键字。其中findBy可以用find、read、readBy、query、queryBy、get、getBy来代替。
还有更多的查询关键字,如下图所示:
这里写图片描述

b. 使用JPA的NamedQuery查询
支持用JPA的NameQuery来定义查询方法,即一个名称映射一个查询语句。定义如下:

@Entity@NamedQuery(name = "Demo.findByName",query = "select d from Demo d where d.name=?1")public class Demo{}//使用如下语句:public interface DemoRepository extends JpaRepository<Demo, Long> {/** 1. 这时我们使用的是NamedQuery里定义的查询语句,而不是根据方法名称查询*/List<Demo> findByName(String name);}

c. 使用@Query查询
支持用
@Query注解在接口的方法上实现查询,例如:

//1.使用参数索引。public interface DemoRepository extends JpaRepository<Demo, Long> {    @Query("select d from Demo d where d.address=?1")    List<Demo> findByAddress(String address);}//2.使用命名参数。public interface DemoRepository extends JpaRepository<Demo, Long> {    @Query("select d from Demo d where d.address= :address")    List<Demo> findByAddress(@Param("address") String address);}//3.更新查询,支持@Modifying和@Query注解组合来事件更新查询,例如:public interface DemoRepository extends JpaRepository<Demo, Long> {    @Modifying    @Transactional    @Query("update Demo d set d.name=?1")    int setName(String name);}//4.Specificationpublic class CustomerSpecs {    public static Specification<Demo> demoFromHefei() {        return new Specification<Demo>() {            @Override            public Predicate toPredicate(Root<Demo> root, CriteriaQuery<?> query, CriteriaBuilder cb) {                return cb.equal(root.get("address"), "云南");            }        };    }}//5.排序与分页public interface DemoRepository extends JpaRepository<Demo, Long> {    List<Demo> findByName(String name,Sort sort);    Page<Demo> findByName(String name,Pageable pageable);}//使用List<Demo> demo = demoRepository.findByName("xx", new sort(Direction.ASC,"age"));Page<Demo> demo2 = demoRepository.findByName("xx", new PageRequest(0, 10));

二、实例(SpringBoot开发)
1.pom.xml中添加jpa和mysql的依赖

        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-jpa</artifactId>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <scope>runtime</scope>        </dependency>

2.实体类,Teacher

//Teacher@Entity@Table(name = "t_teacher_info")@NamedQuery(name = "Teacher.withNameAndAddressNamedQuery", query = "select t from Teacher t where t.name=?1 and t.address=?2")public class Teacher {    @Id    @GeneratedValue    private Long id;    private String name;    private int age;    private String address;    public Teacher() {        super();    }    public Teacher(Long id, String name, int age, String address) {        super();        this.id = id;        this.name = name;        this.age = age;        this.address = address;    }    //略get、set方法。。。}

3.定义数据访问接口

public interface TeacherRepository extends JpaRepository<Teacher, Long> {    //使用方法名查询,接受一个name参数,返回值为列表。    List<Teacher> findByAddress(String address);    //使用方法名查询,接受name和address,返回值为单个对象。    Teacher findByNameAndAddress(String name, String address);    //使用@Query查询,参数按照名称绑定。    @Query("select t from Teacher t where t.name= :name and t.address= :address")    Teacher withNameAndAddressQuery(@Param("name") String name, @Param("address") String address);    //使用@NamedQuery查询,请注意我们在实体类中做的@NamedQuery的定义。    Teacher withNameAndAddressNamedQuery(String name, String address);}

4.控制器-本地作为演示,省略掉service,实际项目中不可省略

@RestController@RequestMapping("/teacher")public class DemoController {    @Autowired    TeacherRepository teacherRepository;//1 Spring Data JPA已自动为你注册bean,所以可自动注入    /**     * 保存     *     * @param name     * @param address     * @param age     * @return     */    @RequestMapping("/save")    public Teacher save(String name, String address, int age) {        Teacher t = teacherRepository.save(new Teacher(null, name, age, address));        return t;    }    /**     * 执行findByAddress     *     * @param address     * @return     */    @RequestMapping("/q1")    public List<Teacher> q1(String address) {        List<Teacher> teachers = teacherRepository.findByAddress(address);        return teachers;    }    /**     * 执行findByNameAndAddress     */    @RequestMapping("/q2")    public Teacher q2(String name, String address) {        Teacher teacher = teacherRepository.findByNameAndAddress(name, address);        return teacher;    }    /**     * 执行withNameAndAddressQuery     */    @RequestMapping("/q3")    public Teacher q3(String name, String address) {        Teacher teacher = teacherRepository.withNameAndAddressQuery(name, address);        return teacher;    }    /**     * 执行withNameAndAddressNamedQuery     */    @RequestMapping("/q4")    public Teacher q4(String name, String address) {        Teacher teacher = teacherRepository.withNameAndAddressNamedQuery(name, address);        return teacher;    }    /**     * 执行排序     */    @RequestMapping("/sort")    public List<Teacher> sort() {        List<Teacher> teachers = teacherRepository.findAll(new Sort(Direction.ASC, "age"));        return teachers;    }    /**     * 执行分页     */    @RequestMapping("/page")    public Page<Teacher> page() {        Page<Teacher> pageTeacher = teacherRepository.findAll(new PageRequest(1, 2));        return pageTeacher;    }}

5、测试(测试工具:postman介绍)

1. save功能测试
这里写图片描述

2. q1功能测试
这里写图片描述

3. q2功能测试
这里写图片描述

4.q3功能测试
这里写图片描述

5.q4功能测试
这里写图片描述

6.sort功能测试 (代码中是按照age升序排序【Sort(Direction.ASC, “age”)】)
这里写图片描述

7.page功能测试(代码中取了2条【new PageRequest(1, 2)】)
这里写图片描述

三、利用Spring Data Jpa做自定义Repository
1.pom.xml中添加依赖

        <dependency>            <groupId>com.google.guava</groupId>            <artifactId>guava</artifactId>            <version>23.4-jre</version>        </dependency>

2.定义Specification:

public class CustomerSpecs {    //1定义一个返回值为Specification的方法byAuto,这里使用的是泛型T,所以这个Specification是可以用于任意的实体类的。它接受的参数是entityManager和当前的包含值作为查询条件的实体类对象。    public static <T> Specification<T> byAuto(final EntityManager entityManager, final T example) {        final Class<T> type = (Class<T>) example.getClass();//2获得当前实体类对象类的类型。        return new Specification<T>() {            @Override            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {                List<Predicate> predicates = new ArrayList<>(); //3新建Predicate列表存储构造的查询条件。                EntityType<T> entity = entityManager.getMetamodel().entity(type);//4获得实体类的EntityType,我们可以从EntityType获得实体类的属性。                for (Attribute<T, ?> attr : entity.getDeclaredAttributes()) {//5对实体类的所有属性做循环。                    Object attrValue = getValue(example, attr); //6获得实体类对象某一个属性的值。                    if (attrValue != null) {                        if (attr.getJavaType() == String.class) { //7当前属性值为字符类型的时候。                            if (!StringUtils.isEmpty(attrValue)) { //8若当前字符不为空的情况下。                                predicates.add(cb.like(root.get(attribute(entity, attr.getName(), String.class)), pattern((String) attrValue))); //9构造当前属性like(前后%)属性值查询条件,并添加到条件列表中。                            }                        } else {                            //这里注释掉是因为如果字段不为String类型会自动分配值,如(int=0),导致自动查询结果有误                            //predicates.add(cb.equal(root.get(attribute(entity, attr.getName(), attrValue.getClass())), attrValue)); //10其余情况下,构造属性和属性值equal查询条件,并添加到条件列表中。                        }                    }                }                return predicates.isEmpty() ? cb.conjunction() : cb.and(toArray(predicates, Predicate.class));//11将条件列表转换成Predicate。            }            /**             * 12通过反射获得实体类对象对应属性的属性值。             */            private <T> Object getValue(T example, Attribute<T, ?> attr) {                return ReflectionUtils.getField((Field) attr.getJavaMember(), example);            }            /**             * 13获得实体类的当前属性的SingularAttribute,             SingularAttribute包含的是实体类的某个单独属性。             */            private <E, T> SingularAttribute<T, E> attribute(EntityType<T> entity, String fieldName, Class<E> fieldClass) {                return entity.getDeclaredSingularAttribute(fieldName, fieldClass);            }        };    }    /**     * 14构造like的查询模式,即前后加%。     */    static private String pattern(String str) {        return "%" + str + "%";    }}

3.定义接口:

/** * 此例中的接口继承了JpaRepository,让我们具备了JpaRepository所提供的方法;继承了JpaSpecificationExecutor,让我们具备使用Specification的能力。 * @param <T> * @param <ID> */@NoRepositoryBeanpublic interface CustomRepository<T, ID extends Serializable>extends JpaRepository<T, ID> ,JpaSpecificationExecutor<T>{    Page<T> findByAuto(T example,Pageable pageable);}

4.定义实现:

/** * 此类继承JpaRepository的实现类SimpleJpaRepository,让我们可以使用SimpleJpaRepository的方法;此类当然还要实现我们自定义的接口CustomRepository。findByAuto方法使用byAuto Specification构造的条件查询,并提供分页功能。 * @param <T> * @param <ID> */public class CustomRepositoryImpl <T, ID extends Serializable>        extends SimpleJpaRepository<T, ID> implements CustomRepository<T,ID> {    private final EntityManager entityManager;    public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {        super(domainClass, entityManager);        this.entityManager = entityManager;    }    @Override    public Page<T> findByAuto(T example, Pageable pageable) {        return findAll(CustomerSpecs.byAuto(entityManager, example),pageable);    }}

5.定义RepositoryFactoryBean:

/** * 只需让实体类Repository继承我们自定义的Repository接口,即可使用我们在自定义Respository中实现的功能。 * @param <T> * @param <S> * @param <ID> */public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable>        extends JpaRepositoryFactoryBean<T, S, ID> {    public CustomRepositoryFactoryBean(Class<? extends T> repositoryInterface) {        super(repositoryInterface);    }    @Override    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {        return new CustomRepositoryFactory(entityManager);    }    private static class CustomRepositoryFactory extends JpaRepositoryFactory {        public CustomRepositoryFactory(EntityManager entityManager) {            super(entityManager);        }        @Override        @SuppressWarnings({"unchecked"})        protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(                RepositoryInformation information, EntityManager entityManager) {            return new CustomRepositoryImpl<T, ID>                    ((Class<T>) information.getDomainType(), entityManager);        }        @Override        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {            return CustomRepositoryImpl.class;        }    }}

6.使用:
DemoController中:

/**     * 执行auto     * @param teacher     * @return     */    @RequestMapping("/auto")    public Page<Teacher> auto(Teacher teacher){        Page<Teacher> pageTeachers = teacherRepository.findByAuto(teacher, new PageRequest(0, 10));        return pageTeachers;    }

Springboot启动bean中(添加@EnableJpaRepositories):

@SpringBootApplication@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)public class SpringbootcacheApplication {    public static void main(String[] args) {        SpringApplication.run(SpringbootcacheApplication.class, args);    }}

6.执行效果:
执行/auto
这里写图片描述

执行/auto?name=徐
这里写图片描述

执行/auto?address=99
这里写图片描述

参考资料《JavaEE开发的颠覆者 Spring Boot》

**

新手一枚,欢迎拍砖~ ~ ~

**