SpringBoot中使用SpringDataJPA

SpringDataJPA的使用

JPA是什么?

JPA(Java Persistence API)是Sun官方提出的Java持久化规范. 为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据. 它的出现是为了简化现有的持久化开发工作和整合ORM技术. 结束各个ORM框架各自为营的局面.

JPA仅仅是一套规范,不是一套产品, 也就是说Hibernate, TopLink等是实现了JPA规范的一套产品.

Spring Data JPA

Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,是基于Hibernate之上构建的JPA使用解决方案,用极简的代码实现了对数据库的访问和操作,包括了增、删、改、查等在内的常用功能.

1.引入相关依赖pom.xml

<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>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2.配置文件application.yml

# DataSource Config
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shiro-jwt?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  jpa:
    properties:
      hibernate:
        hbm2ddl:
          auto: update
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
        format_sql: true
    show-sql: true

hibernate.hbm2ddl.auto 参数的作用主要用于:自动创建、更新、验证数据库表结构,有四个值。

  • create:每次加载 Hibernate 时都会删除上一次生成的表,然后根据 model 类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
  • create-drop:每次加载 Hibernate 时根据 model 类生成表,但是 sessionFactory 一关闭,表就自动删除。
  • update:最常用的属性,第一次加载 Hibernate 时根据 model 类会自动建立起表的结构(前提是先建立好数据库),以后加载 Hibernate 时根据 model 类自动更新表结构,即使表结构改变了,但表中的行仍然存在,不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
  • validate :每次加载 Hibernate 时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

配置文件中:

  • dialect 主要是指定生成表名的存储引擎为 InnoDB
  • show-sql 是否在日志中打印出自动生成的 SQL,方便调试的时候查看

3.实体类

package com.frank.shirojwt.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @author 小石潭记
 * @date 2020/6/20 21:36
 * @Description: ${todo}
 */
@Entity(name="user")
@Data
// 在转化成json的时候,fasterxml.jackson将对象转换为json报错,发现有字段为null。
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false, unique = true)
    private String userName;

    @Column(nullable = false)
    private String passWord;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = true, unique = true)
    private String nickName;

    @Column(nullable = false)
    private String regTime;

    public User(String userName, String passWord, String email, String nickName, String regTime) {
        this.userName = userName;
        this.passWord = passWord;
        this.email = email;
        this.nickName = nickName;
        this.regTime = regTime;
    }

}

注解:

  • @Entity(name="EntityName") 必须,用来标注一个数据库对应的实体,数据库中创建的表名默认和类名一致。其中,name 为可选,对应数据库中一个表,使用此注解标记 Pojo 是一个 JPA 实体。
  • @Table(name="",catalog="",schema="") 可选,用来标注一个数据库对应的实体,数据库中创建的表名默认和类名一致。通常和 @Entity 配合使用,只能标注在实体的 class 定义处,表示实体对应的数据库表的信息。
  • @Id 必须,@Id 定义了映射到数据库表的主键的属性,一个实体只能有一个属性被映射为主键。
  • @GeneratedValue(strategy=GenerationType,generator="") 可选,strategy: 表示主键生成策略,有 AUTO、INDENTITY、SEQUENCE 和 TABLE 4 种,分别表示让 ORM 框架自动选择,generator: 表示主键生成器的名称。
  • @Column(name = "user_code", nullable = false, length=32) 可选,@Column 描述了数据库表中该字段的详细定义,这对于根据 JPA 注解生成数据库表结构的工具。name: 表示数据库表中该字段的名称,默认情形属性名称一致;nullable: 表示该字段是否允许为 null,默认为 true;unique: 表示该字段是否是唯一标识,默认为 false;length: 表示该字段的大小,仅对 String 类型的字段有效。
  • @Transient可选,@Transient 表示该属性并非一个到数据库表的字段的映射,ORM 框架将忽略该属性。
  • @Enumerated 可选,使用枚举的时候,我们希望数据库中存储的是枚举对应的 String 类型,而不是枚举的索引值,需要在属性上面添加 @Enumerated(EnumType.STRING) 注解。

4. Repository构建

package com.frank.shirojwt.repository;

import com.frank.shirojwt.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author 小石潭记
 * @date 2020/6/20 21:38
 * @Description: ${todo}
 */
public interface UserRepository extends JpaRepository<User,Long> {

    User findByUserNameOrEmail(String userName, String email);

    User findByUserName(String userName);

}

5.controller接口测试

package com.frank.shirojwt.web;

import com.frank.shirojwt.entity.User;
import com.frank.shirojwt.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @author 小石潭记
 * @date 2020/6/20 21:38
 * @Description: ${todo}
 */
@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/save")
    public void save(){
        Date data = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String formattedDate = dateFormat.format(data);

        userRepository.save(new User("aa","aa123456","[email protected]","aa",formattedDate));
        userRepository.save(new User("bb","bb123456","[email protected]","bb",formattedDate));
        userRepository.save(new User("cc","cc123456","[email protected]","cc",formattedDate));
    }

    @GetMapping("/getUserInfo")
    public User getUserInfo(@RequestParam("id") String id){
        return userRepository.getOne(Long.parseLong(id));
    }

    @GetMapping("/getUserList")
    public List<User> getUserList(){
        return userRepository.findAll();
    }
}

先保存user对象信息:http://localhost:8087/save

自定义查询

Spring Data JPA 可以根据接口方法名来实现数据库操作,主要的语法是 findXXBy、readAXXBy、queryXXBy、countXXBy、getXXBy 后面跟属性名称,利用这个功能仅需要在定义的 Repository 中添加对应的方法名即可,使用时 Spring Boot 会自动帮我们实现.

根据用户名查询用户:

User findByUserName(String userName);

也可以加一些关键字 And、or:

User findByUserNameOrEmail(String username,String email);

修改、删除、统计也是类似语法:

Long deleteById(Long id);
Long countByUserName(String userName)

基本上 SQL 体系中的关键词都可以使用,如 LIKE 、IgnoreCase、OrderBy:

List<User> findByEmailLike(String email);

User findByUserNameIgnoreCase(String userName);

List<User> findByUserNameOrderByEmailDesc(String email);

关键字的使用和生产SQL:

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 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 age) … 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)

自定义SQL查询

1.在UserRepository中增加方法:

/**
     * @Author Smith
     * @Description 自定义Sql查询.(这个本来是HQL的写法,我的运行不了,改成了本地的SQL)
     * @Date 10:18 2019/1/24
     * @Param 
     * @return org.springframework.data.domain.Page<com.jpa.springdatajpa.model.User>
     **/
    @Query(value = "select * from user",nativeQuery = true)
    Page<User> findALL(Pageable pageable);

    /**
     * @Author Smith
     * @Description 原生SQL的写法,?1表示方法参数中的顺序
     * @Date 10:20 2019/1/24
     * @Param 
     * @return org.springframework.data.domain.Page<com.jpa.springdatajpa.model.User>
     **/
    @Query(value = "select * from user where nick_name = ?1",nativeQuery = true)
    Page<User> findByNickName(String nickName, Pageable pageable);

    /**
     * @Author Smith
     * @Description 修改,添加事务的支持
     * @Date 10:21 2019/1/24
     * @Param 
     * @return int
     **/
    @Transactional(timeout = 10)
    @Modifying
    @Query("update User set userName = ?1 where id = ?2")
    int modifyById(String  userName, Long id);

    /**
     * @Author Smith
     * @Description 删除
     * @Date 10:22 2019/1/24
     * @Param 
     * @return void
     **/
    @Transactional
    @Modifying
    @Query("delete from User where id = ?1")
    @Override
    void deleteById(Long id);

2.测试

@GetMapping("/getUserListPage")
    public Object getUserListPage(){
        // 当前页
        int page = 0;
        // 每页的条数
        int size = 3;
        Sort sort = by(Sort.Direction.DESC,"id");
        Pageable pageable = PageRequest.of(page,size,sort);
        Page<User> all = userRepository.findALL(pageable);
        return all;
    }

    @GetMapping("/getUserListPage1")
    public Object getUserListPage1(){
        int page = 0;
        int size = 1;
        Sort sort = by(Sort.Direction.DESC,"id");
        Pageable pageable = PageRequest.of(page,size,sort);
        Page<User> all = userRepository.findByNickName("bb",pageable);
        return all;
    }

限制查询

只需要查询前 N 个元素,或者只取前一个实体。

User findFirstByOrderByNickNameAsc();

User findTopByOrderByIdDesc();

Page<User> queryFirst10ByNickName(String nickName, Pageable pageable);

List<User> findFirst10ByNickName(String nickName, Sort sort);

List<User> findTop10ByNickName(String nickName, Pageable pageable);

复杂查询(后续完善)

在某些情况下查询条件很多,需要不断拼接属性,方法名会显得很长,这个时候就要使用JpaSpecificationExecutor 接口了.

概念:

  • Root root,代表了可以查询和操作的实体对象的根,开一个通过 get("属性名") 来获取对应的值。
  • CriteriaQuery query,代表一个 specific 的顶层查询对象,它包含着查询的各个部分,比如 select 、from、where、group by、order by 等。
  • CriteriaBuilder cb,来构建 CritiaQuery 的构建器对象,其实就相当于条件或者是条件组合,并以 Predicate 的形式返回。

参考文档

代码下载

猜你喜欢

转载自blog.csdn.net/qq_33371766/article/details/106878408