第十五节:SpringBoot使用JPA访问数据库

JPA是Java Persistence API的简写,是官方提出的一种ORM规范!

JPA规范,都在包路径:javax.persistence.*下,像一些常用的如:@Entity、@Id及@Transient都在此路径下。这些也是一些现在市面上常用的ORM一些约定俗成的注解了。

Spring Data JPA是Spring基于Hibernate开发的一个JPA框架。可以极大的简化JPA的写法,可以在几乎不用写具体代码的情况下,实现对资料的访问和操作。除了「CRUD」外,还包括如分页、排序等一些常用的功能。

pom.xml中添加依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-jpa</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>mysql</groupId>
  7. <artifactId>mysql-connector-java</artifactId>
  8. </dependency>

application.properties 配置

  1. spring.datasource.url=jdbc:mysql://localhost:3306/rumenz_springboot
  2. spring.datasource.username=root
  3. spring.datasource.password=root1234
  4. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  5. spring.jpa.hibernate.ddl-auto=update
  6. spring.sql.init.mode=always
  7. spring.sql.init.schema-locations=classpath:/ddl/user-book.sql
  8. spring.sql.init.data-locations=classpath:/ddl/user-book-data.sql

spring.jpa.hibernate.ddl-auto 是否根据实体类更新数据库,有四个属性值

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

spring.sql.init.mode 是否使用sql文件初始化数据库,有3个值

属性值 作用
ALWAYS 始终初始化数据库。
EMBEDDED 仅初始化嵌入式数据库。
NEVER 永远不要初始化数据库。
  • spring.sql.init.schema-locations 指定建表的sql文件
  • spring.sql.init.data-locations指定数据sql文件

创建实体类

  1. User.java
  2. @Getter
  3. @Setter
  4. @Builder
  5. @AllArgsConstructor
  6. @Entity //jpa必填
  7. @DynamicInsert //填充默认值
  8. @DynamicUpdate //填充默认值
  9. @Table(name = "user") //jpa必填
  10. @NoArgsConstructor
  11. public class User {
  12. @Id //jpa必填
  13. @GeneratedValue(strategy = GenerationType.IDENTITY) //jpa必填
  14. private Integer id;
  15. private String name;
  16. private String domain;
  17. @Column(name = "age",columnDefinition = "tinyint default 0")
  18. private Integer age;
  19. }

@GeneratedValue(strategy = GenerationType.IDENTITY)有以下几种类型

  • TABLE:使用一个特定的数据库表格来保存主键。
  • SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
  • IDENTITY:主键由数据库自动生成(主要是自动增长型)
  • AUTO:主键由程序控制。

创建repository

数据持久层,负责访问数据库,在这里声明的方法一般不用实现,只要按照Jpa的规范就可以自动生成SQL语句。

  1. package com.rumenz.lession15.controller.repository;
  2. import com.rumenz.lession15.controller.entity.User;
  3. import org.springframework.data.domain.Page;
  4. import org.springframework.data.domain.Pageable;
  5. import org.springframework.data.jpa.repository.JpaRepository;
  6. import org.springframework.data.repository.PagingAndSortingRepository;
  7. import org.springframework.stereotype.Repository;
  8. import java.util.List;
  9. import java.util.Optional;
  10. /**
  11. * @className: UserRepository
  12. * @description: TODO 类描述
  13. * @author: 入门小站 rumenz.com
  14. * @date: 2021/12/14
  15. **/
  16. @Repository
  17. public interface UserRepository extends PagingAndSortingRepository<User,Integer> {
  18. Optional<User> findById(Integer id);
  19. List<User> findDistinctUserByName(String name);
  20. Integer countUserByName(String name);
  21. List<User> readDistinctByName(String name);
  22. Page<User> findAllByName(String name, Pageable pageable);
  23. }

Jpa可以通过接口名生成对应的sql语句,如 find… By,read… By,query… By,count… By,和get… By 。这些方法可以包含其他表达式,例如在要创建的查询上设置 Distinct 标志。第一个 By 用作分隔符,表示条件的开始,后面定义实体属性的各种条件,并将它们用 And 和 Or 连接起来。例如:

  1. interface RumenzRepository extends JpaRepository<Rumenz, Long> {
  2. List<Rumenz> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
  3. // 为查询启用 distinct 标志
  4. List<Rumenz> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  5. List<Rumenz> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
  6. // 为单个属性启用忽略大小写
  7. List<Rumenz> findByLastnameIgnoreCase(String lastname);
  8. // 为所有属性启用忽略大小写
  9. List<Rumenz> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
  10. // 为查询启用静态 Order by
  11. List<Rumenz> findByLastnameOrderByFirstnameAsc(String lastname);
  12. List<Rumenz> findByLastnameOrderByFirstnameDesc(String lastname);
  13. }

举一些例子

关键字 方法示例 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, Null findByAge(Is)Null … 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)

Repository有3种

JpaRepository继承PagingAndSortingRepository,PagingAndSortingRepository继承CrudRepository。

  • CrudRepository提供CRUD的功能
  • PagingAndSortingRepository提供分页和排序功能
  • JpaRepository提供JPA相关的方法,如刷新持久化数据、批量删除等。

image-20211222163426330

service

业务逻辑层,负责调用Repository处理数据完成业务。

  1. package com.rumenz.lession15.controller.service;
  2. import com.rumenz.lession15.controller.entity.User;
  3. import org.springframework.data.domain.Page;
  4. import java.util.List;
  5. /**
  6. * @className: UserService
  7. * @description: TODO 类描述
  8. * @author: 入门小站 rumenz.com
  9. * @date: 2021/12/14
  10. **/
  11. public interface UserService {
  12. Integer save(User user);
  13. User get(Integer id);
  14. List<User> listByName(String name);
  15. Integer countByName(String name);
  16. List<User> readDistinctByName(String name);
  17. Page<User> listByNamePage(String name, Integer page, Integer pageSize);
  18. }
  19. //实现类
  20. package com.rumenz.lession15.controller.service.lmpl;
  21. import com.rumenz.lession15.controller.entity.User;
  22. import com.rumenz.lession15.controller.repository.UserRepository;
  23. import com.rumenz.lession15.controller.service.UserService;
  24. import org.springframework.beans.factory.annotation.Autowired;
  25. import org.springframework.data.domain.Page;
  26. import org.springframework.data.domain.PageRequest;
  27. import org.springframework.data.domain.Pageable;
  28. import org.springframework.data.domain.Sort;
  29. import org.springframework.stereotype.Service;
  30. import java.util.List;
  31. import java.util.Optional;
  32. /**
  33. * @className: UserServiceImpl
  34. * @description: TODO 类描述
  35. * @author: 入门小站 rumenz.com
  36. * @date: 2021/12/14
  37. **/
  38. @Service
  39. public class UserServiceImpl implements UserService {
  40. @Autowired
  41. UserRepository userRepository;
  42. @Override
  43. public Integer save(User user) {
  44. User save = userRepository.save(user);
  45. return save.getId();
  46. }
  47. @Override
  48. public User get(Integer id) {
  49. Optional<User> opt = userRepository.findById(id);
  50. return opt.isPresent()?opt.get():null;
  51. }
  52. @Override
  53. public List<User> listByName(String name) {
  54. List<User> res = userRepository.findDistinctUserByName(name);
  55. return res;
  56. }
  57. @Override
  58. public Integer countByName(String name) {
  59. return userRepository.countUserByName(name);
  60. }
  61. @Override
  62. public List<User> readDistinctByName(String name) {
  63. return userRepository.readDistinctByName(name);
  64. }
  65. @Override
  66. public Page<User> listByNamePage(String name, Integer page, Integer pageSize) {
  67. Sort sort = Sort.by("id").descending();
  68. Pageable pageable= PageRequest.of(page-1, pageSize, sort);
  69. Page<User> res = userRepository.findAllByName(name, pageable);
  70. return res;
  71. }
  72. }

Controller

前端控制器,负责接收前端请求,调用service,返回数据。

  1. package com.rumenz.lession15.controller;
  2. import com.rumenz.lession15.controller.entity.User;
  3. import com.rumenz.lession15.controller.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.data.domain.Page;
  6. import org.springframework.data.domain.Pageable;
  7. import org.springframework.web.bind.annotation.GetMapping;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RequestParam;
  10. import org.springframework.web.bind.annotation.RestController;
  11. import java.util.List;
  12. /**
  13. * @className: RumenzController
  14. * @description: TODO 类描述
  15. * @author: 入门小站 rumenz.com
  16. * @date: 2021/12/14
  17. **/
  18. @RestController
  19. @RequestMapping("/rumenz")
  20. public class RumenzController {
  21. @Autowired
  22. UserService userService;
  23. //保存数据
  24. //id=1的数据不存在就添加
  25. //id=1的数据存在就更新
  26. @RequestMapping("/save")
  27. public String save(){
  28. User user=User.builder().id(1).name("入门小站123").domain("https://rumenz.com").build();
  29. Integer save = userService.save(user);
  30. return save.toString();
  31. }
  32. //通过id查询数据
  33. @GetMapping("/get")
  34. public User get(@RequestParam("id") Integer id){
  35. return userService.get(id);
  36. }
  37. //带条件查询
  38. @GetMapping("/listByName")
  39. public List<User> get(@RequestParam("name") String name){
  40. return userService.listByName(name);
  41. }
  42. //按条件查询符合条件的数量
  43. @GetMapping("/countByName")
  44. public Integer countByName(@RequestParam("name") String name){
  45. return userService.countByName(name);
  46. }
  47. //带条件查询
  48. @GetMapping("/readDistinctByName")
  49. public List<User> readDistinctByName(@RequestParam("name") String name){
  50. return userService.readDistinctByName(name);
  51. }
  52. //分页查询
  53. //带条件查询
  54. @GetMapping("/listByNamePage")
  55. public Page<User> listByNamePage(@RequestParam("name") String name, @RequestParam("page") Integer page, @RequestParam("pageSize") Integer pageSize){
  56. return userService.listByNamePage(name,page,pageSize);
  57. }
  58. }

本小结源码地址:

介绍

  • 关注【入门小站】回复【1001】获取 linux常用命令速查手册
  • 关注【入门小站】回复【1003】获取 LeetCode题解【java语言实现】
  • 关注【入门小站】回复【1004】获取 Java基础核心总结
  • 关注【入门小站】回复【1009】获取 阿里巴巴Java开发手册
返回笔记列表
入门小站