Spring Data Jpa的使用

一.Spring Data Jpa 简介

JPA

JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR 338,这些接口所在包为javax.persistence)。JPA的出现主要是为了简化持久层开发以及整合ORM技术,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。JPA是在吸收现有ORM框架的基础上发展而来,易于使用,伸缩性强。总的来说,JPA包括以下3方面的技术:

  • ORM映射元数据: 支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系
  • API: 操作实体对象来执行CRUD操作
  • 查询语言: 通过面向对象而非面向数据库的查询语言(JPQL)查询数据,避免程序的SQL语句紧密耦合
Jpa、Hibernate、Spring Data Jpa三者之间的关系

JPA是ORM规范,Hibernate、TopLink等是JPA规范的具体实现,这样的好处是开发者可以面向JPA规范进行持久层的开发,而底层的实现则是可以切换的。Spring Data Jpa则是在JPA之上添加另一层抽象(Repository层的实现),极大地简化持久层开发及ORM框架切换的成本。
在这里插入图片描述

二.使用

  1. 导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
  1. 相关配置
server:
  port: 8080
  servlet:
    context-path: /
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
    username: root
    password: mysql123
  jpa:
    database: MySQL
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: update

ddl-auto upadte:每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新
3. 实体类

package com.example.springbootjpa.entity;

import lombok.Data;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

@Entity
@Table(name = "tb_user")
@Data
public class User {

    @Id
   	@GeneratedValue
    private String id;

    @Column(name = "username", unique = true, nullable = false, length = 64)
    private String username;

    @Column(name = "password", nullable = false, length = 64)
    private String password;

    @Column(name = "email", length = 64)
    private String email;

}

@Table 使用一个特定的数据库表格来保存主键
@GeneratedValue表明是逐渐自增
@Column 数据库中的列和实体类对应

  1. Dao层-- extends JpaRepository
package com.example.springbootjpa.repository;

import com.example.springbootjpa.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, String> {

}

先省略service层
5.Controller层

package com.example.springbootjpa.controller;

import com.example.springbootjpa.entity.User;
import com.example.springbootjpa.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Optional;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @PostMapping()
    public User saveUser(@RequestBody User user) {
        return userRepository.save(user);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable("id") String userId) {
        userRepository.deleteById(userId);
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable("id") String userId, @RequestBody User user) {
        user.setId(userId);
        return userRepository.saveAndFlush(user);
    }

    @GetMapping("/{id}")
    public User getUserInfo(@PathVariable("id") String userId) {
        Optional<User> optional = userRepository.findById(userId);
        return optional.orElseGet(User::new);
    }

    @GetMapping("/list")
    public Page<User> pageQuery(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
                                @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
        return userRepository.findAll(PageRequest.of(pageNum - 1, pageSize));
    }

}

在这里使用RestFul风格,浏览器必须安装插件。普通浏览器不能发送更新,删除请求。如下,火狐浏览器中安装RESTClient插件
在这里插入图片描述
6.就可以测试啦

package com.example;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.example.bean.Gender;
import com.example.bean.User;
import com.example.dao.UserDao;

@SpringBootTest
class DemoJpaApplicationTests {
	@Autowired
private UserDao dao;
	@Test
	void contextLoads() {
		dao.save(new User("lyt",20,Gender.WOMAN));
		
	}

}
Spring Data查询方法

Spring Data JPA中提供了强⼤的查询功能,我们只需要在dao层接⼝中按规则,定出对应的查询⽅法,
那么Spring Data JPA会根据我们所定义的⽅法名,⾃动⽣成对应的sql语句,例如:

public interface UserDao extends JpaRepository<User, Long>{
 User findByName(String name);
 
 List<User> findByAge(int age);
 
 List<User> findByNameOrAge(String name, int age);
 
 List<User> findByNameLike(String name);
 
 User findByNameIgnoreCase(String name);
 
 List<User> findByAgeOrderByNameDesc(int age);
 
 //first和top的效果是⼀样的,默认去第⼀条数据
 User findFirstByOrderByAgeAsc();
 
 User findFirstByOrderByAgeDesc();
 
 User findTopByOrderByAgeAsc();
 
 User findTopByOrderByAgeDesc();
 
 //first和top后⾯都可以跟数字,表示取查询出来数据的前N条数据
 List<User> findFirst2ByGender(Gender gender);
 
}

当在测试类中调⽤我们⾃⼰在接⼝中定义的 findByName ⽅法的时候,会⾃动⽣成对应的sql语句:

@Test
public void test_findByName() throws Exception {
 User user = userDao.findByName("briup");
 System.out.println(user);
}

当在测试类中调⽤我们⾃⼰在接⼝中定义的 findFirst2ByGender ⽅法的时候,会⾃动⽣成对应的sql
语句:

@Test
public void test_findFirst2ByGender() throws Exception {
 List<User> list = userDao.findFirst2ByGender(Gender.MAN);
 list.forEach(System.out::println);
}
4. 排序

Spring Data JPA中可以使⽤ Sort 对查询的结果进⾏排序,让 Sort 作为查询⽅法的参数即可。
⽗接⼝中已经有定好的⽅法的参数含有 Sort ,例如 JpaRepository 中的 List findAll(Sort
sort);
也可以⾃⼰在接⼝中⾃定义⽅法的参数中添加 Sort 参数:

public interface UserDao extends JpaRepository<User, Long>{
 
 List<User> findByGender(Gender gender,Sort sort);
 
 List<User> findFirst25ByGender(Gender gender, Sort sort);
 
}
@Test
public void test_sort1() throws Exception {
 List<User> list = userDao.findAll(Sort.by(Direction.DESC, "age"));
 list.forEach(System.out::println);
}
5.分页

Spring Data JPA中分⻚功能和排序的使⽤⽅式类似,只需要在查询⽅法的参数列表中添加⼀
个 Pageable 类型的参数即可。
⽗接⼝中已经有定好的⽅法的参数含有 Pageable ,例如 PagingAndSortingRepository 中的
Page findAll(Pageable pageable);
也可以⾃⼰在接⼝中⾃定义⽅法的参数中添加 Pageable 参数:

public interface UserDao extends JpaRepository<User, Long>{
 
 Page<User> findByGender(Gender gender,Pageable pageable);
 
 Page<User> findFirst25ByGender(Gender gender, Pageable pageable);
 
 List<User> findTop25ByGender(Gender gender, Pageable pageable);
 
}

⽅法的返回类型是可以是Page类型的,也可以是List集合类型的,只是Page类型的返回值可以提
供更多的分⻚相关信息的获取。

@Test
public void test_pageable1() throws Exception {
 int page = 0;//当前⻚数--第⼀⻚从0开始计算
 int size = 2;//每⻚指定多少个元素
 Pageable pageable = PageRequest.of(page,size);
 Page<User> pageObj = userDao.findAll(pageable);
 System.out.println("总⻚数:\t\t"+pageObj.getTotalPages());
 System.out.println("总数据量:\t\t"+pageObj.getTotalElements());
 System.out.println("当前的⻚码:\t\t"+pageObj.getNumber());
 System.out.println("每⻚条数:\t\t"+pageObj.getSize());
 System.out.println("当前⻚实际条数:\t\t"+pageObj.getNumberOfElements());
 System.out.println("当前⻚是否有数据:\t\t"+pageObj.hasContent());
 System.out.println("当前⻚内容:\t\t"+pageObj.getContent());
 System.out.println("分⻚查询的排序规则为:\t\t"+pageObj.getSort());
 System.out.println("当前是否为第⼀⻚:\t\t"+pageObj.isFirst());
 System.out.println("当前是否为最后⼀⻚:\t\t"+pageObj.isLast());
 System.out.println("当前是否有上⼀⻚:\t\t"+pageObj.hasPrevious());
 System.out.println("当前是否有下⼀⻚:\t\t"+pageObj.hasNext());

在这里插入图片描述
如果返回值类型使⽤List⽽不是Page类型的 ,那么就⽆法直接获取到这些分⻚相关的数据信息。

6.注解

Spring Data JPA中可以在接⼝中⾃定义⽅法上⾯使⽤ @Query 注解,在该注解中可以使⽤JPQL或SQL来
指定此⽅法被调⽤的时候需要执⾏的sql语⾔是什么。
JPQL(JavaPersistence Query Language)是⼀种⾯向对象的查询语⾔,它在ORM框架中最终会翻译成为sql进⾏执⾏。在hibernate框架中,这种语句叫做HQL(Hibernate Query Language)。

  • PQL是⾯向对象的查询语⾔,在查询语句中,出现的不是表的名字、字段的名字,⽽是类的名字和类中
    属性的名字,因为在ORM框架中,类和表,属性和字段都做好了映射关系,所以JPQL最后是可以根据映
    射关系转换sql语句的。
  • JPQL的特点:
    语句中不能出现表名,列名,只能出现java的类名,属性名,并且区分⼤⼩写
    语句中出现的关键字和sql语句中的是⼀样的意思,不区分⼤⼩写
    语句中不能写select * ⽽是要写select 类的别名,或者写select 具体的属性名
@Query("select u from User u where u.name = ?1")
User findByUserName(String name);

该语句中的 ?1 代表第⼀个参数,?2 代表第⼆个参数

7.关联

Spring Data JPA中除了单表操作外,还可以⽀持多表级联操作,但是这需要表和表之间有关联关系,同
时还需要在实体类中把这种关联关系给配置映射出来(Object-Relationl Mapping)。

1对1关系:

Address

package com.briup.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "t_address")
public class Address {
 
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 
 private String city;
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getCity() {
 return city;
 }
 public void setCity(String city) {
 this.city = city;
 }
 
}

Student

package com.briup.bean;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name = "t_studnet")
public class Student {
 
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 
 private String name;
 
 /**
 * @OneToOne 表示当前studnet实体类和Address实现类之间是1对1关系
 * cascade=CascadeType.ALL 表示student和address之间的级联类型为ALL
 *
 * @JoinColumn 该注解专⻔⽤来指定外键列的名字
 * 在这⾥表示:将来student表中将会有⼀个外键列为 address_id,
 * 该外键列默认引⽤Address表中的主键列(id)的值
 */
 @OneToOne(cascade=CascadeType.ALL)
 @JoinColumn(name = "address_id")
 private Address address;
 public Address getAddress() {
 return address;
 }
 public void setAddress(Address address) {
 this.address = address;
 }
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 } }
public interface StudentDao extends JpaRepository<Student, Long> {
}
@Autowired
private StudentDao studentDao;
@Test
public void test_one2one() throws Exception {
}

运⾏测试⽅法后,可以看到建表语句,根据我们配置的映射信息,之间建表

  1. 如果需要,可以进⾏⾃动建表
  2. create可以每次启动测试先进⾏删除表,然后再建表
  3. update可以每次启动测试时先检查数据库中是否有表,没对应的表则会⾃动建表,有表单话就不再创建了
    spring.jpa.hibernate.ddl-auto=update

测试:

@Test
public void test_one2one_insert() throws Exception {
 
 Student stu = new Student();
 Address address = new Address();
 address.setCity("上海");
 stu.setName("tom");
 //学⽣对象和地址对象在内存中建⽴关系,最后会映射成数据库中俩个表中数据的关系
 stu.setAddress(address);
 studentDao.save(stu);
}
@Test
public void test_one2one_select() throws Exception {
 Student stu = studentDao.findById(1L).orElse(null);
 System.out.println(stu);
 System.out.println(stu.getAddress());
}
1对N关系:

假设⼀个⽼师有多辆⻋,⼀辆⻋只属于⼀个⽼师,那么⽼师和汽⻋的关系就是1对N

  • 注意,1对N关系中,数据库表中的外键列需要设置在N的⼀⽅,否则会有数据冗余。

package com.briup.bean;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "t_teacher")
public class Teacher {
 
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 
 private String name;
 
 private Double salary;
 
 /**
 * @OneToMany 表示我(teacher)和对⽅(car)之间是⼀对多关系
 *
 * mappedBy="teacher"
 * 表示我们之间⼀多对关系的外键列
 * 是由对⽅(car)的teacher属性类映射维护的
 *
 */
 @OneToMany(mappedBy="teacher",cascade=CascadeType.ALL,fetch =
FetchType.EAGER)
 private List<Car> cars = new ArrayList<>();
 public List<Car> getCars() {
 return cars;
 }
 public void setCars(List<Car> cars) {
 this.cars = cars;
 }
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 
 public Double getSalary() {
 return salary;
 }
 public void setSalary(Double salary) {
 this.salary = salary;
 }
 
 
}

Car

package com.briup.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "t_car")
public class Car {
 
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 
 private String type;
 
 private Double price;
 
 /**
 * @ManyToOne 表示我(car)和对⽅(teacher)之间的关系是多对⼀
 * @JoinColumn 表示当前属性为外键列属性,并且指定外键列的名字
 */
 @ManyToOne
 @JoinColumn(name="teacher_id")
 private Teacher teacher;
 public Teacher getTeacher() {
 return teacher;
 }
 public void setTeacher(Teacher teacher) {
 this.teacher = teacher;
 }
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getType() {
 return type;
 }
 public void setType(String type) {
 this.type = type;
 }
 public Double getPrice() {
 return price;
 }
 public void setPrice(Double price) {
 this.price = price;
 } }
package com.briup.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.briup.bean.Teacher;
public interface TeacherDao extends JpaRepository<Teacher, Long> {
}
package com.briup.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.briup.bean.Car;
public interface CarDao extends JpaRepository<Car, Long> {
}

级联保存

@Autowired
private TeacherDao teacherDao;
@Test
public void test_one2many_insert() throws Exception {
 Teacher t = new Teacher();
 t.setName("tom");
 t.setSalary(10000D);
 Car c1 = new Car();
 c1.setType("BMW");
 c1.setPrice(300000D);
 Car c2 = new Car();
 c2.setType("BENZ");
 c2.setPrice(400000D);
 //很重要的步骤,建⽴起⽼师对象和汽⻋对象之间的关联关系
 //这个关系会映射到数据库中
 c1.setTeacher(t);
 c2.setTeacher(t);
 List<Car> list = new ArrayList<>();
 list.add(c1);
 list.add(c2);
 t.setCars(list);
 teacherDao.save(t);
}
@Test
public void test_one2many_select() throws Exception{
 Teacher t = teacherDao.findById(1L).orElse(null);
 System.out.println(t);
 System.out.println(t.getCars());
}
N对N关系:

注意,1对N关系中,数据库中需要有第三张桥表来关联另外俩张表。
player

package com.example.bean;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Entity
@Table(name = "t_player")
public class Player {
	@Id
	 @GeneratedValue(strategy=GenerationType.IDENTITY)
	 private Long id; 
	 private String name;
	 /**
	  * mappedBy属性,在双向关联中,必定会出现
	  *
	  * mappedBy表示 外键的配置/维护 由对⽅来处理
	  * 如果不配置mappedBy,则默认表示 外键的配置/维护 由我⽅来处理
	  *
	  * mappedBy="gameList" 表示 我⽅(Game)和对⽅(Player)之间关系的维护
	  * 是由对⽅(Player)的属性(gameList)来进⾏配置/维护的。
	  * 其实是由对⽅(Player)中的属性(gameList)上的注解进⾏配置/维护的
	  *
	  */
	 @ManyToMany(fetch = FetchType.EAGER)
	 @JoinTable(
			 name="player_game",
	 joinColumns=@JoinColumn(name="player_id"),
	 inverseJoinColumns=@JoinColumn(name = "game_id")
	 )
	 private List<Game> gameList = new ArrayList<>();
	 public Player() {}
	 
	 public Player(String name) {
	 this.name = name;
	 }
	 public List<Game> getGameList() {
		 return gameList;
		 }
		 public void setGameList(List<Game> gameList) {
		 this.gameList = gameList;
		 }
		 public Long getId() {
		 return id;
		 }
		 public void setId(Long id) {
		 this.id = id;
		 }
		 public String getName() {
		 return name;
		 }
		 public void setName(String name) {
		 this.name = name;
		 }

		@Override
		public String toString() {
			return "Player [id=" + id + ", name=" + name + ", gameList=" +  "]";
		}
		 
		 
		}

game

package com.example.bean;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "t_game")
public class Game {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	 private Long id;
	 
	 private String name;
	 @ManyToMany(mappedBy="gameList",fetch = FetchType.EAGER)
	 private List<Player> playerList = new ArrayList<>();
	 public Game() {}
	 
	 public Game(String name) {
	 this.name = name;
	 }
	 public List<Player> getPlayerList() {
	 return playerList;
	 }
	 public void setPlayerList(List<Player> playerList) {
	 this.playerList = playerList;
	 }
	 public Long getId() {
	 return id;
	 }
	 public void setId(Long id) {
	 this.id = id;
	 }
	 public String getName() {
	 return name;
	 }
	 public void setName(String name) {
	 this.name = name;
	 }

	@Override
	public String toString() {
		return "Game [id=" + id + ", name=" + name + "]";
	}

}

package com.example.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.bean.Game;

public interface GameDao extends JpaRepository<Game, Long>{

}

package com.example.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.bean.Player;

public interface PlayerDao extends JpaRepository<Player,Long>{

}


@Test
	public void test02() {
		/*
		 * Player p1 = new Player(); p1.setName("tom1"); Player p2 = new Player();
		 * p2.setName("tom2"); Game g1 = new Game(); g1.setName("疯狂篮球"); Game g2 = new
		 * Game(); g2.setName("疯狂⾜球"); Game g3 = new Game(); g3.setName("疯狂排球");
		 * List<Game> gameList1 = new ArrayList<>(); gameList1.add(g1);
		 * gameList1.add(g2); gameList1.add(g3); List<Game> gameList2 = new
		 * ArrayList<>(); gameList2.add(g1); gameList2.add(g3); //建⽴对象之间的关联关系
		 * p1.setGameList(gameList1); p2.setGameList(gameList2); //注意,这样要记得先保存game对象的数据
		 * //然后再保存player对象的数据,因为是让player关联的game gameDao.save(g1); gameDao.save(g2);
		 * gameDao.save(g3); playerDao.save(p1); playerDao.save(p2);
		 */
		
		Player p = playerDao.findById(1L).orElse(null);
		 System.out.println(p);
		 System.out.println(p.getGameList());
	
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值