集成Spring Data JPA

Posted 阿_毅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了集成Spring Data JPA相关的知识,希望对你有一定的参考价值。

开心一笑

【上课时老师问同学们:捅和刺,有什么不同?
小明:大的叫捅,小的才叫刺。
老师:滚出去。。。】

新书购买

戳图购买 >>>

3.1 Spring Data JPA介绍

本节主要介绍Spring Data JPA是什么、Spring Data JPA核心接口Repository、核心接口间的继承关系图。

3.1.1 Spring Data JPA介绍

JPA(Java Persistence API)是Sun官方提出的Java持久化规范。所谓规范即只定义标准规则,不提供实现。而JPA的主要实现有Hibernate、EclipseLink、OpenJPA等。JPA是一套规范,不是一套产品。Hibernate是一套产品,如果这些产品实现了JPA规范,那么我们可以叫它们为JPA的实现产品。
Spring Data JPA是Spring Data的一个子项目,它通过提供基于JPA的Respository,极大地减少了JPA作为数据访问方案的代码量。通过Spring Data JPA框架,开发者可以省略实现持久层业务逻辑的工作,唯一要做的,就只是声明持久层的接口,其它都交给 Spring Data JPA 来帮你完成。

3.1.2 核心接口Repository

Spring Data JPA最顶层接口是Repository,该接口是所有Repository类的父类。具体代码如下:

package org.springframework.data.repository;
import java.io.Serializable;
public interface Repository<T, ID extends Serializable> 


Repository类下没有任何的接口,只是一个空类。Repository接口的子类有CrudRepository、PagingAndSortingRepository、JpaRepository等。其中CrudRepository类提供了基本的增删改查等接口,PagingAndSortingRepository类提供了基本的分页和排序等接口,而JpaRepository是CrudRepository和PagingAndSortingRepository的子类,继承了它们的所有接口。所以在真实的项目当中,我们都是通过实现JpaRepository或者其子类进行基本的数据库操作。JpaRepository具体代码如下:

@NoRepositoryBean
public interface JpaRepository extends PagingAndSortingRepository<T, ID> 
    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);

@NoRepositoryBean:使用该注解标明,此接口不是一个Repository Bean。

3.1.3 接口继承关系图

Repository接口间的继承关系如图3-1所示。通过该继承图,我们可以清楚知道接口间的集成关系。在项目中,我们一般都是实现JapRepository类,加上自己定义的业务方法,来完成我们的业务开发。


图3-1 Repository接口间集成关系

3.2 集成Spring Data JPA

本节主要介绍如何在Spring Boot中集成Spring Data JPA,服务层类开发,如何通过Spring Data JPA实现基本增删改查功能,以及自定义查询方法等内容。

3.2.1 引入依赖

在Spring Boot中集成Spring Data JPA,首先需要在pom.xml文件中引入所需的依赖,具体代码如下:

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

在之前的章节中,我们已经在开发工具中安装好Maven Helper插件,所以大家可以通过该插件查看目前引入的所有依赖,具体如图3-2所示。


图3-2 my-spring-boot项目目录 图3-3 Maven Helper查看pom依赖包

3.2.2 继承JpaRepository

在pom.xml文件中引入依赖之后,我们在目录/src/main/java/com.example.demo.repository下开发一个AyUserRepository类,如图3-3所示,具体代码如下:

/**
 * 描述:用户Repository
 * @author 阿毅
 * @date   2017/10/14.
 */
public interface AyUserRepository extends JpaRepository<AyUser,String>


与此同时,我们需要AyUser实体类下添加@Entity和@Id注解,具体代码如下:

/**
 * 描述:用户表
 * @Author 阿毅
 * @date   2017/10/8.
 */
@Entity
@Table(name = "ay_user")
public class AyUser 
    //主键
    @Id
    private String id;
    //用户名
    private String name;
    //密码
    private String password;

@Entity:每个持久化POJO类都是一个实体Bean, 通过在类的定义中使用 @Entity 注解来进行声明。
@Table:声明此对象映射到数据库的数据表。该注释不是必须的,如果没有则系统使用默认值(实体的短类名)。
@Id:指定表的主键。

3.2.3 服务层类实现

我们在my-spring-boot项目下继续开发服务层接口类和实现类:AyUserService和AyUserServiceImpl类。它们分别存放在目录/src/main/java/com.example.demo.service 和/src/main/java/com.example.demo.service.impl下。具体代码如下所示:

/**
 * 描述:用户服务层接口
 * @author 阿毅
 * @date   2017/10/14
 */
public interface AyUserService 
AyUser findById(String id);
    List<AyUser> findAll();
    AyUser save(AyUser ayUser);
    void delete(String id);

接口类AyUserService定义了4个接口,findById和findAll用来查询单个和所有数据,delete用来删除数据,save同时具备保存和更新数据的功能。接口实现类AyUserServiceImpl代码如下:

/**
 * 描述:用户服务层实现类
 * @author 阿毅
 * @date   2017/10/14
 */
@Service
public class AyUserServiceImpl implements AyUserService

    @Resource
    private AyUserRepository ayUserRepository;

	    @Override
    public AyUser findById(String id)
        return ayUserRepository.findById(id).get();
    

    @Override
    public List<AyUser> findAll() 
        return ayUserRepository.findAll();
    

    @Override
    public AyUser save(AyUser ayUser) 
        return ayUserRepository.save(ayUser);
    

    @Override
    public void delete(String id) 
        ayUserRepository.deleteById(id);
    

@ Service:Spring Boot会自动扫描到@Component注解的类,并把这些类纳入进Spring容器中管理。也可以用@Component注解,只是@Service注解更能表明该类是服务层类。
@Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Repository:持久层组件,用于标注数据访问组件,即DAO组件 。
@Resource:这个注解属于J2EE的,默认安照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行查找。如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。具体代码如下:

@Resource(name = "ayUserRepository")
private AyUserRepository ayUserRepository;

@Autowired:这个注解是属于Spring的,默认按类型装配。默认情况下要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用。具体代码如下:

@Autowired
@Qualifier("ayUserRepository")
private AyUserRepository ayUserRepository;

3.2.4 增删改查分页简单实现

上一节,我们已经在服务层类AyUserService开发完增删改查方法。这一节,我们将继续在类中添加分页接口,具体代码如下:

/**
 * 描述:用户服务层接口
 * @author 阿毅
 * @date   2017/10/14
 */
public interface AyUserService 
AyUser findById(String id);
    List<AyUser> findAll();
    AyUser save(AyUser ayUser);
    void delete(String id);
    //分页
	    Page<AyUser> findAll(Pageable pageable);

Pageable:这是一个分页接口,查询时候我们只需要传入一个 Pageable接口的实现类,指定pageNumber和pageSize即可。pageNumber为第几页,而pageSize为每页大小。
Page:分页查询结果会封装在该类中,Page接口实现Slice接口,通过查看其源码可知。我们通过调用getTotalPages和 getContent等方法,可以方便获得总页数和查询的记录。Page接口和Slice接口源码如下:

public interface Page<T> extends Slice<T> 
    int getTotalPages();
    long getTotalElements();
    <S> Page<S> map(Converter<? super T, ? extends S> var1);


public interface Slice<T> extends Iterable<T> 
    int getNumber();
    int getSize();
    int getNumberOfElements();
    List<T> getContent();
    boolean hasContent();
    Sort getSort();
    boolean isFirst();
    boolean isLast();
    boolean hasNext();
    boolean hasPrevious();
    Pageable nextPageable();
    Pageable previousPageable();
    <S> Slice<S> map(Converter<? super T, ? extends S> var1);

分页方法定义好之后,我们在类AyUserServiceImpl中实现该方法,具体代码如下:

@Override
public Page<AyUser> findAll(Pageable pageable) 
    return ayUserRepository.findAll(pageable);

3.2.5 自定义查询方法

我们除了使用JpaRepository接口提供的增删改查分页等方法之外,还可以自定义查询方法。我们在AyUserRepository类中添加几个自定义查询方法,具体代码如下:

/**
 * 描述:用户Repository
 * @author 阿毅
 * @date   2017/10/14.
 */
public interface AyUserRepository extends JpaRepository<AyUser,String>

/**
     * 描述:通过名字相等查询,参数为 name
     * 相当于:select u from ay_user u where u.name = ?1
     */
    List<AyUser> findByName(String name);

    /**
     * 描述:通过名字like查询,参数为 name
     * 相当于:select u from ay_user u where u.name like ?1
     */
    List<AyUser> findByNameLike(String name);

    /**
     * 描述:通过主键id集合查询,参数为 id集合
     * 相当于:select u from ay_user u where id in(?,?,?)
     * @param ids
     */
    List<AyUser> findByIdIn(Collection<String> ids); 

在AyUserRepository中,我们自定义了3个查询的方法。从代码可以看出,Spring Data JPA为我们约定了一系列的规范,只要我们按照规范编写代码,Spring Data JPA就会根据代码翻译成相关的SQL语句,进行数据库查询。比如我们可以使用findBy、Like、In等关键字。其中findBy可以用read、readBy、query、queryBy、get、getBy来代替。关于查询关键字的更多内容,大家可以到官网(https://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/)查看,里面有详细的内容介绍,这里就不一一列举了。
AyUserRepository类中自定义查询方法开发完成之后,我们分别在类AyUserService和类AyUserServiceImpl中调用它们。
AyUserService继续添加这3个方法,具体代码如下:

List<AyUser> findByName(String name);
List<AyUser> findByNameLike(String name);
List<AyUser> findByIdIn(Collection<String> ids);

    AyUserServiceImpl类添加这3个方法,具体代码如下:
@Override
public List<AyUser> findByName(String name)
   return ayUserRepository.findByName(name);

@Override
public List<AyUser> findByNameLike(String name)
  return ayUserRepository.findByNameLike(name);

@Override
public List<AyUser> findByIdIn(Collection<String> ids)
   return ayUserRepository.findByIdIn(ids);

@Override注解不可去掉哦,它帮助我们校验接口方法是否被误改。

3.3 集成测试

3.3.1 测试用例开发

我们在测试类MySpringBootApplicationTests中添加如下代码:

@Resource
	private AyUserService ayUserService;

	@Test
	public void testRepository()
		//查询所有数据
		List<AyUser> userList =  ayUserService.findAll();
		System.out.println("findAll() :" + userList.size());
		//通过name查询数据
		List<AyUser> userList2 = ayUserService.findByName("阿毅");
		System.out.println("findByName() :" + userList2.size());
		Assert.isTrue(userList2.get(0).getName().equals("阿毅"),"data error!");
		//通过name模糊查询数据
		List<AyUser> userList3 = ayUserService.findByNameLike("%毅%");
		System.out.println("findByNameLike() :" + userList3.size());
		Assert.isTrue(userList3.get(0).getName().equals("阿毅"),"data error!");
		//通过id列表查询数据
		List<String> ids = new ArrayList<String>();
		ids.add("1");
		ids.add("2");
		List<AyUser> userList4 = ayUserService.findByIdIn(ids);
		System.out.println("findByIdIn() :" + userList4.size());
		//分页查询数据
		PageRequest pageRequest = new PageRequest(0,10);
		Page<AyUser> userList5 =  ayUserService.findAll(pageRequest);
	System.out.println("page findAll():" + userList5.getTotalPages() + "/" + userList5.getSize());
		//新增数据
		AyUser ayUser = new AyUser();
		ayUser.setId("3");
		ayUser.setName("test");
		ayUser.setPassword("123");
		ayUserService.save(ayUser);
		//删除数据
		ayUserService.delete("3");
	

Assert:添加Assert断言,在软件开发中是一种常用的调试方式。从理论上来说,通过Assert断言方式可以证明程序的正确性。在现在项目中被广泛使用,这是大家需要掌握的基本知识。Assert提供了很多好用的方法,比如isNull,isTrue等。

3.3.2 测试

通过运行3.3.1节中的单元测试用例,我们可以在控制台看到如下的打印信息:

findAll() :2
findByName() :1
findByNameLike() :1
findByIdIn() :2
page findAll():1/10

通过上面的打印信息,可以看出Spring Boot集成Spring Data JPA已经成功,同时代码中的所有Assert断言都全部通过,说明增删改查分页以及自定义查询方法都可以正常运行。


读书感悟

来自《疯狂的进化》

  • 自然界创造了生存难题,又亲手交给了生物们解决方法,那就是进化。

经典故事

一个小男孩抓到了一只非常漂亮的小乌龟。但小乌龟在陌生的环境当中,就把头缩进了壳里,无论你是用棍子敲打它还是捅它,就是不肯伸出来。这时,爷爷看到他的举动,就说:“这种方法是行不通的,我告诉你一个办法。”他叫小男孩把小乌龟放到暖和的壁炉旁。几分钟后,小乌龟觉得环境变得温暖了,便伸出了头和脚,主动向小男孩爬了过来。

【生活中,温暖的关爱是打开心灵最好的钥匙。不要用强硬的手段去逼迫他人做什么,只要以善意、亲切、诚挚和热情的方式使他觉得温暖,他会情愿去做你想要他做的事情。】


大神文章


其他

如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎鼓励,点赞、顶、欢迎留下宝贵的意见、多谢支持!

以上是关于集成Spring Data JPA的主要内容,如果未能解决你的问题,请参考以下文章

spring-boot与spring-data-JPA的简单集成使用

SpringBoot集成Spring-data-jpa访问数据库

集成 Spring JPA Data 和 Spring Cache 时的奇怪行为

PostgreSQL springboot spring data jpa 集成

Spring boot集成spring-boot-starter-data-jpa环境搭建

集成Spring Data JPA