玩转 SpringBoot 2.x 之使用 SpringDataJpa 篇

前言

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。

–以上内容摘抄自百度百科

JPA Hibernate SpringDatAJpa 三者的关系

JPA 是一种规范而 Hibernate 和 SpringDataJpa 是 JPA 的具体实现, 另外Spring Data JPA 是 Spring 在 Hibernate 的基础上构建的 JPA 使用解决方案。值得一提的是在早期的时候 Hibernate是不支持JPA,因为Sun在提出 JPA 的时候就已经有 Hibernate 了。

SpirngBoot JPA 操作实战

内置查询接口

第一步:引入 SpirngBoot JPA 的 starter 依赖,同时需要引入mysql 驱动依赖。具体代码如下:

<dependency>

	<groupId>org.springframework.bootgroupId>

	<artifactId>spring-boot-starter-data-jpaartifactId>

dependency>



<dependency>

    <groupId>mysqlgroupId>

    <artifactId>mysql-connector-javaartifactId>

dependency>

第二步:添加Mysql 数据源的配置

在 SpringBoot 的配置文件 application.yml 中添加数据库配置信息。

spring:

  datasource:

    url: jdbc:mysql://127.0.0.1:3306/learn?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true

    driver-class-name: com.mysql.jdbc.Driver

    username: root

    password: root

# Ddl-auto :  Create: 自动创建表 Update:自动修改表  Create-drop:应用停下来会把表删除掉 None:什么都不做  Validate:类和表明是否一致

  jpa:

    show-sql: true

    hibernate:

      ddl-auto: update

    properties:

      hibernate:

        format_sql: true

        dialect: org.hibernate.dialect.MySQL5InnoDBDialect

在启动的时候报如下错误

java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.

at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.13.jar:8.0.13]

at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.13.jar:8.0.13]

at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) ~[mysql-connector-java-8.0.13.jar:8.0.13]

at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63) ~[mysql-connector-java-8.0.13.jar:8.0.13]

图片

原来的url内容是: url: jdbc:mysql://127.0.0.1:3306/learn

将url 内容修改为

url: jdbc:mysql://127.0.0.1:3306/learn?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true 问题解决。

第三步:根据 JPA 规范的注解配置映射实体

添加映射实体通过JPA规范的注解,具体代码如下:

package cn.lijunkui.springbootlearn.test.model;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

@Entity

public class User {

    @Id

    @GeneratedValue

    private Long id;

    private String name;

    private Integer age;

    public Long getId() {

        return id;

    }

    public void setId(Long id) {

        this.id = id;

    }

    private String sex;

    private String address;

    public  User(){

    }

    public User(Long id,String name,Integer age,String sex,String address){

        this.id = id;

        this.name = name;

        this.age = age;

        this.address = address;

        this.sex = sex;

    }

   // 此处省略get 和set 方法

}

第四步:使用Spring Data Jpa 内置查询接口 CrudRepository 充当DAO,具体操作方式是定义接口然后继承CrudRepository即可。具体代码如下:

package cn.lijunkui.springbootlearn.test.dao;

import cn.lijunkui.springbootlearn.test.model.User;

import org.springframework.data.repository.CrudRepository;

import org.springframework.transaction.annotation.Transactional;

import java.util.List;

public interface UserCrudRepository extends CrudRepository<User,Long>{

}

你肯定会惊讶 这就完成啦,我可以确定的告诉我们的Dao开发完毕 基本的增删改查搞定。

查看CrudRepository源码我们发现他有的方法如下:

<S extends T> S save(S entity);//新增

<S extends T> Iterable<S> saveAll(Iterable<S> entities);//批量新增

Optional<T> findById(ID id);//查询通过id

boolean existsById(ID id);//id是否存在

Iterable<T> findAll();//查询所有

Iterable<T> findAllById(Iterable<ID> ids);//查询多个id的数据

long count();//数据的总数

void deleteById(ID id);//根据id进行删除

void delete(T entity);//根据实例进行删除

void deleteAll(Iterable<? extends T> entities);//批量删除

void deleteAll();//删除所有

第五步:编写测试用例。

SpringBoot 的单元测试 需要我们声明 @SpringBootTest 和 @RunWith 注解

这里只是简单写啦几个测试。

package cn.lijunkui.springbootlearn.test.dao;


@SpringBootTest

@RunWith(SpringRunner.class)

public class UserCrudRepositoryTest {

    @Autowired

    private UserCrudRepository userCrudRepository;

    /**

     *  添加用户 测试

     */

    @Test

    public void add(){

        User user = new User();

        user.setName("ljk2");

        user.setSex("1");

        user.setAge(18);

        user.setAddress("beijing");

        User result = userCrudRepository.save(user);

        Assert.assertNotNull(result);

    }

    /**

     *  修改用户

     */

    @Test

    public void edit(){

        User user = new User();

        user.setId(1l);

        user.setName("ljk2edit");

        user.setSex("1");

        user.setAge(18);

        user.setAddress("beijing");

        User result = userCrudRepository.save(user);

        Assert.assertNotNull(result);

    }

    /**

     * 通过id 进行查找

     */

    @Test

    public void findById(){

        Optional<User> userOptional = userCrudRepository.findById(1l);

        User result = userOptional.get();

        Assert.assertNotNull(result);

    }

    /**

     *  查询所有

     */

    @Test

    public void findAll(){

        List<User> userList = (List<User>)userCrudRepository.findAll();

        Assert.assertTrue(userList.size()>0);

    }

    @Test

    public void count(){

        long count = userCrudRepository.count();

        System.out.println(count);

    }

}

到这里最简单SpringDataJAP 介绍完毕。接下来让我们继续深入SpringDataJAP其他内置接口

PagingAndSortRepository 使用介绍

CrudRepository 只是具有增删改查的一些基本功能,接下来 PagingAndSortingRepository是具有分页和排序的功能 同时他继承啦 CrudRepository。

编写测试用例:


package cn.lijunkui.springbootlearn.test.dao;

import cn.lijunkui.springbootlearn.test.model.User;

import org.hibernate.criterion.Order;

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.Sort;

import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.junit.Assert.*;

@SpringBootTest

@RunWith(SpringRunner.class)

public class UserPageRepositoryTest {

    @Autowired

    private UserPageRepository userPageRepository;

    @Test

    public void findAllBySort(){

        List<User> userList = (List<User>)userPageRepository.findAll(new Sort(Sort.Direction.ASC,"age"));

        System.out.println(userList.size());

    }

    @Test

    public void findAllByPageable(){

        Page<User> userPage =  userPageRepository.findAll(new PageRequest(0, 20));

        userPage.getNumber();//页数

        userPage.getContent();//分页的数据

        userPage.getTotalPages();//总共的页数

        System.out.println("number:"+userPage.getNumber()

                +"Countet"+userPage.getContent().size()

                +"TotalPages"+userPage.getTotalPages());

    }

}

JpaRepository 使用介绍

JpaRepository 不仅继承啦 PagingAndSortingRepository 同时继承啦 QueryByExampleExecutor(示例匹配器)

通过我们的测试用例查询期详细的用法


package cn.lijunkui.springbootlearn.test.dao;

import cn.lijunkui.springbootlearn.test.model.ResultDTO;

import cn.lijunkui.springbootlearn.test.model.User;

import org.junit.Assert;

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.*;

import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import static org.junit.Assert.*;

@SpringBootTest

@RunWith(SpringRunner.class)

public class UserJpaRepositoryTest {

    @Autowired

    private UserJpaRepository userJpaRepository;

    /**

     * 执行秒数:49422 49145

     * 批量保存数据

     */

    @Test

    public void BatchSave(){

        long startTime = System.currentTimeMillis();

        List<User> list = new ArrayList<User>();

        for (int i = 0; i < 60000; i++) {

            User user = new User();

            user.setName("ljk"+i);

            user.setAge(i);

            user.setAddress("address"+i);

            list.add(user);

            if(i%100 == 0){

                userJpaRepository.saveAll(list);

                list.clear();

            }

        }

        long endTime = System.currentTimeMillis();

        System.out.println("执行秒数:"+ (endTime - startTime));

    }

    /**

     * 执行秒数:48053 48394 执行速度比BatchSave 要快

     * 批量保存数据 (高效处理方式)减少大事物的提交

     */

    @Test

    public void BatchSaveBest(){

        long startTime = System.currentTimeMillis();

        List<User> list = new ArrayList<User>();

        for (int i = 0; i < 60000; i++) {

            User user = new User();

            user.setName("ljk"+i);

            list.add(user);

            if(i%100 == 0){

                userJpaRepository.saveAll(list);

                userJpaRepository.flush();

                list.clear();

            }

        }

        long endTime = System.currentTimeMillis();

        System.out.println("执行秒数:"+ (endTime - startTime));

    }

    /**

     * 查询所有数据

     */

    @Test

    public void findALL(){

        List<User> userlists = userJpaRepository.findAll();

        Assert.assertTrue(userlists.size() > 0);

    }

    /**

     * 根据 age 排序查询

     */

    @Test

    public void findALLSortAge(){

        List<User> lists = userJpaRepository.findAll(Sort.by(Sort.Direction.ASC ,"age"));

        for (User list : lists) {

            System.out.println(list);

        }

    }

    /**

     * 分页查询

     */

    @Test

    public void  findAllByPage(){

        PageRequest pageRequest = new PageRequest(0,1);

        Page<User> userPage = userJpaRepository.findAll(pageRequest);

        Assert.assertTrue(userPage.getContent().size() == 1);

    }

    /**

     *  分页排序查询

     */

    @Test

    public void findAllByPageAndSort(){

        PageRequest pageRequest = new PageRequest(0,3,Sort.by(Sort.Direction.ASC ,"age"));

        Page<User> userPage =  userJpaRepository.findAll(pageRequest);

        List<User> userList= userPage.getContent();

        for (User user : userList) {

            System.out.println(user);

        }

    }

    /**

     * 根据id 的集合获取所有数据

     */

    @Test

    public void findAllByIds(){

        List<Long> ids = new ArrayList<Long>();

        ids.add(1l);

        ids.add(2l);

        ids.add(3l);

        ids.add(4l);

        List<User> userList = userJpaRepository.findAllById(ids);

        Assert.assertTrue(userList.size()>0);

    }



    /**

     * 批量删除所有数据

     */

    @Test

    public void  deleteAllInBatch(){

        userJpaRepository.deleteAllInBatch();

    }

    /**

     *  保存数据并刷新缓存

     */

    @Test

    public void saveAndFlush(){

        User user = new User();

        user.setName("ljk");

        user.setAge(18);

        user.setAddress("beijing");

        user.setSex("1");

        User result = userJpaRepository.saveAndFlush(user);

        Assert.assertNotNull(result);

    }

    /**

     * 批量删除

     */

    @Test

    public void deleteInBatch(){

        List<User> userList = new ArrayList<User>();

        User user = new User();

        user.setId(1l);

        userList.add(user);

        User user2 = new User();

        user2.setId(2l);

        userList.add(user2);

        User user3 = new User();

        user3.setId(3l);

        userList.add(user3);

        User user4 = new User();

        user4.setId(4l);

        userList.add(user4);

        userJpaRepository.deleteInBatch(userList);

    }

    /**

     * 根据id 获取数据 延迟加载

     */

    @Test

    public void getOne(){

        User result = userJpaRepository.getOne(1l);

        Long id = result.getId();

        String name = result.getName();

        System.out.println(id);

        System.out.println(name);

        Assert.assertNotNull(result);

    }

    /**

     * 示例匹配器 ExampleMatcher

     */

    @Test

    public void findUserByExam(){

        User user = new User();

        user.setName("ljk");

        List<User> list = userJpaRepository.findAll(Example.of(user));

        System.out.println(list.size());

    }

    @Test

    public void findUserByExamQuery(){

        User user = new User();

        user.setName("ljk");

        user.setAddress("address8");

        user.setAge(8);

        ExampleMatcher matcher = ExampleMatcher.matching()

                .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.startsWith())//模糊查询匹配开头,即{username}%

                .withMatcher("address" ,ExampleMatcher.GenericPropertyMatchers.contains())//全部模糊查询,即%{address}%

                .withIgnorePaths("id");//忽略字段,即不管id是什么值都不加入查询条件

        Example<User> example = Example.of(user ,matcher);

        List<User> userList = userJpaRepository.findAll(example);

        Assert.assertTrue(userList.size() > 0);

    }

}

方法名称创建查询

虽然 SpringDataJPA 提供内置查询接口,自定义查询方式它提供了方法名称的方式进行查询,只需要定义个接口方法你就可以进行查询你想要的数据。


@SpringBootTest

@RunWith(SpringRunner.class)

public class UserJpaRepositoryTest {

    @Autowired

    private UserJpaRepository userJpaRepository;

    @Test

    public void findByNameAndAge(){

        List<User> userList = userJpaRepository.findByNameAndAge("ljk",18);

        Assert.assertTrue( userList.size()>0 );

    }

    public void findByNameOrAge(){

        List<User> userList = userJpaRepository.findByNameOrAge("ljk",18);

        Assert.assertTrue( userList.size()>0 );

    }

}

快速自定以查询方法:示例如下

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1(parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1(parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1(parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

计数查询

SpringDataJap 提供针对某个字段数量统计使用 countBy+数据库映射实体字段名称即可完成,代码如下所示:

	long countByName(String name);

计数删除

也可以通过 removeBy+数据库映射实体字段名称进行计数删除。

@Transactional
	List<User> reomveByName(String name);

List 用于接收删除数据的信息

同时也可以通过 deleteBy+数据库映射实体字段名称进行数据的删除。

	@Transactional
	List<User> deleteByName(String name);

@Query注解查询

Spring Data JPA 不仅提供内置接口和方法名称查询方式同时还提供了通过 @Query 注解的方式拼写查询语句,对于经常使用Hibernate HQL 查询的福音啊。

具体使用方式代码如下:

public interface UserJpaRepository extends JpaRepository<User,Long>{

    /**

     * 根据姓名查询用户

     * @param name

     * @return

     */

    @Query("select u from User u where u.name = ?1")

    public List<User> findUserByNameByQuery(String name);

    /**

     * 根据姓名(like)和年龄查询用户

     * @param name

     * @param age

     * @return

     */

    @Query("select u from User u where u.name like  CONCAT('%',?1,'%') and u.age = ?2" )

    public List<User> findUserByLikeNameByQuery(String name,Integer age);


    /**

     * 根据姓名(like)和年龄查询用户

     *  命名参数 进行查询

     */

    @Query("select u from User u where u.name like CONCAT('%',:name,'%') and u.age = :age")

    public User findUserByNameAndAgeWithQery(@Param("name") String  name,@Param("age") Integer age);

    /**

     * 根据姓名(like)和年龄查询用户

     *  命名参数 原生方式进行查询

     */

    @Query(value = "select * from user u where u.name like CONCAT('%',:name,'%') and u.age = :age",nativeQuery = true)

    public List<User> findUserByNameAndAgeWithQeryNative(@Param("name") String  name,@Param("age") Integer age);

    /**

     * 查询每个地区的人的个数

     * @return

     */

    @Query("select new cn.lijunkui.springbootlearn.test.model.ResultDTO(u.address,count(u.id))  from User u group by u.address")

    public List<ResultDTO> findCountGroupByAddress();

}

测试用例:

@SpringBootTest

@RunWith(SpringRunner.class)

public class UserJpaRepositoryTest {

    @Autowired

    private UserJpaRepository userJpaRepository;

    @Test

    public void  findUserByNameByQuery(){

        List<User> userList = userJpaRepository.findUserByNameByQuery("ljk");

        Assert.assertNotNull(userList.size()>0);

    }

    @Test

    public void findUserByLikeNameByQuery(){

        List<User> userList = userJpaRepository.findUserByLikeNameByQuery("jk",18);

        Assert.assertNotNull(userList.size()>0);

    }

    @Test

    public void findUserByNameAndAgeWithQery(){

        User user = userJpaRepository.findUserByNameAndAgeWithQery("jk",18);

        Assert.assertNotNull(user);

    }

    @Test

    public void findUserByNameAndAgeWithQeryNative(){

        List<User> userList = userJpaRepository.findUserByNameAndAgeWithQeryNative("jk",18);

        Assert.assertNotNull(userList.size()>0);

    }

    /**

     * 零散参数的接收

     */

    @Test

    public void  findCountGroupByAddress(){

        List<ResultDTO> results  = userJpaRepository.findCountGroupByAddress();

        System.out.println(results);

    }

}

小结

SpringDataJpa 相对于Hibernate 的使用更为简洁,更容易快速上手SQL 逻辑不是很复杂的业务。如果你想更灵活的编写SQL 可以考虑使用Mybaties。 如果你还没有上手过 SpringDataJpa,还等什么抓紧跟着本文操作一遍吧。

代码示例

我本地环境如下:

  • SpringBoot Version: 2.1.0.RELEASE
  • Apache Maven Version: 3.6.0
  • Java Version: 1.8.0_144
  • IDEA:Spring Tools Suite (STS)

整合过程如出现问题可以在我的GitHub 仓库 springbootexamples 中模块名为 spring-boot-2.x-spring-data-jpa 项目中进行对比查看

GitHub:https://github.com/zhuoqianmingyue/springbootexamples

参考文献

https://docs.spring.io/spring-data/jpa/docs/2.0.10.RELEASE/reference/html/

你可能感兴趣的:(【SpringBoot】)