文旦
什么是h2
H2是Thomas Mueller提供的一个开源的、纯java实现的关系数据库。它可以被嵌入Java应用程序中使用,或者作为一个单独的数据库服务器运行。
什么是JPA
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。
JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范,由此可见,经过这几年的实践探索,能够脱离容器独立运行,方便开发和测试的理念已经深入人心了。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。
JPA的总体思想和现有Hibernate、TopLink、JDO等ORM框架大体一致。总的来说,JPA包括以下3方面的技术:
ORM映射元数据
JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
API
用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
查询语言
这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合
因为我们使用JPA和H2,所以我们需要同时添加这两个依赖,同时为了偷懒,使用lombok
springboot版本我们使用2.4.1
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.huahua</groupId>
<artifactId>spring-boot-h2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-h2</name>
<description>spring-boot-h2</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.4.1</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<mainClass>cn.huahua.springbooth2.SpringBootH2Application</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
我们使用的是阿里云的starter服务器创建的,创建后竟然已经有配置文件了
# 应用名称
spring.application.name=spring-boot-h2
#************H2 Begin****************
#创建表的MySql语句位置
spring.datasource.schema=classpath:schema.sql
#插入数据的MySql语句的位置
spring.datasource.data=classpath:data.sql
#remote visit
spring.h2.console.settings.web-allow-others=true
#console url。Spring启动后,可以访问 http://127.0.0.1:8080/h2-console 查看数据库
spring.h2.console.path=/h2-console
#default true。咱也可以用命令行访问好数据库,感兴趣的同学点这个链接 http://www.h2database.com/html/tutorial.html?highlight=Mac&search=mac#firstFound
spring.h2.console.enabled=true
spring.h2.console.settings.trace=true
#指定数据库的种类,这里 file意思是文件型数据库
spring.datasource.url=jdbc:h2:file:~/test
#用户名密码不需要改,都是临时值
spring.datasource.username=san
spring.datasource.password=
#指定Driver,有了Driver才能访问数据库
spring.datasource.driver-class-name=org.h2.Driver
有两个配置是用来初始化我们的数据库的
#创建表的MySql语句位置
spring.datasource.schema=classpath:schema.sql
#插入数据的MySql语句的位置
spring.datasource.data=classpath:data.sql
# 如果不指定会在内存中 关闭就没了
#指定数据库的种类,这里 file意思是文件型数据库
spring.datasource.url=jdbc:h2:file:~/test
#这是内存数据库
spring.datasource.url=jdbc:h2:mem:testdb
insert into "user_info"
values (default,'admin','admin');
CREATE TABLE "user_info"
(
id INTEGER PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
);
启动后直接终止,忘记导入web依赖,没有占用端口
加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
什么是网页管理
h2 web consloe是一个数据库GUI管理应用,就和phpMyAdmin类似。程序运行时,会自动启动h2 web consloe。当然你也可以进行如下的配置。
spring.h2.console.settings.web-allow-others=true
,进行该配置后,h2 web consloe就可以在远程访问了。否则只能在本机访问。spring.h2.console.path=/h2-console
,进行该配置,你就可以通过YOUR_URL/h2-console访问h2 web consloe。YOUR_URL是你程序的访问URl。spring.h2.console.enabled=true
,进行该配置,程序开启时就会启动h2 web consloe。当然这是默认的,如果你不想在启动程序时启动h2 web consloe,那么就设置为false。
我们会发现找不到我们的库,尝试修改配置 改为内存数据库
spring.datasource.url=jdbc:h2:mem:testdb
注意修改地址
表结构有了
查看数据
噢啦
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
@Data
@Entity("t_user")
//只加了@entity 没加主键 idea会爆红
public class User {
}
实体类最终代码
package cn.huahua.springbooth2.entity;
import lombok.Data;
import javax.persistence.*;
/**
* @author LoveHuahua
* @date 2022年06月12日 23:38
* @description believe in yourself
*/
@Data
@Entity(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", nullable = false)
private Long id;
private String username;
private String password;
}
package cn.huahua.springbooth2.dao;
import cn.huahua.springbooth2.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
package cn.huahua.springbooth2.controller;
import cn.huahua.springbooth2.dao.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author LoveHuahua
* @date 2022年06月12日 23:54
* @description believe in yourself
*/
@RestController
public class TestJpaController {
@Autowired
UserRepository userRepository;
@GetMapping("/testList")
public Object testList() {
return userRepository.findAll();
}
}
目前没有数据,我们在控制台添加几条
重新测试接口
/**
* 测试jpa的新增
* @param
* @return
*/
@GetMapping("/testInsert")
public Object testInsert() {
ArrayList<User> entities = new ArrayList<>();
entities.add(new User(10L,"John", "John"));
entities.add(new User(50L,"John", "John"));
entities.add(new User(100L,"John", "John"));
return userRepository.saveAll(entities);
}
结论 在
@GeneratedValue(strategy = GenerationType.AUTO)
时JPA会忽略ID 同时需要注意一点,如果你的id已经使用过了,这个insert会被忽略,如果有数据变更,会执行update,否则控制台只有select语句
分页查询
@GetMapping("/testList")
public Object testList(Integer page, Integer size) {
Pageable pageable = PageRequest.of(page, size);
Page<User> all = userRepository.findAll(pageable);
return all;
}
在repository中增加方法
package cn.huahua.springbooth2.dao;
import cn.huahua.springbooth2.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
/**
* 参数名不影响,只要是第一个位置就行 实际上会生成 select * from t_user where username = ?1
* 根据用户名查询用户
* @param username 用户名
* @return
*/
public User findUserByUsername(String username);
}
@GetMapping("/testSelectCondition")
public Object testSelectCondition(String username) {
return userRepository.findUserByUsername(username);
}
/**
* 根据用户名和密码查询用户
* @param username
* @return
*/
public User findUserByUsernameAndPassword(String username, String password);
@GetMapping("/testSelectAndCondition")
public Object testSelectAndCondition(String username,String password) {
return userRepository.findUserByUsernameAndPassword(username, password);
}
http://localhost:8080/testSelectAndCondition?username=John222&password=John
http://localhost:8080/testSelectLikeCondition?username=Joh%25
注意这个%25 因为使用GET请求的时候 百分号是需要转义的
/**
* 根据用户名模糊搜索
* @param username
* @return
*/
public List<User> findByUsernameLike(String username);
命名查询语法如下
Keyword | Sample | JPQL snippet |
---|---|---|
Distinct | findDistinctByLastnameAndFirstname | select distinct … where x.lastname = ?1 and x.firstname = ?2 |
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<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
自定义查询
自定义sql分为两种 一种是通过JPQL,还有一种方式是原生sql执行
一句JPQL一定是HQL,但是HQL不一定是JPQL,HQL是Hibernate提供的 而JPQL是JPA的一部分
两种参数传递
/**
* 自定义sql 参数的第一种形式
*
* @param username
* @return
*/
@Query(value = "SELECT t FROM t_user t WHERE t.username = :username")
public List<User> customerSql(String username);
/**
* 自定义sql 参数的第二种形式
*
* @param username
* @return
*/
@Query(value = "SELECT t FROM t_user t WHERE t.username = ?1")
public List<User> customerSql2(String username);
For queries with named parameters you need to use provide names for method parameters. Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters.
修改代码
添加@Param注解 指定参数名
使用占位参数是不需要注解的
最终repository代码
/**
* 自定义sql 参数的第一种形式
*
* @param username
* @return
*/
@Query(value = "SELECT t FROM t_user t WHERE t.username = :username")
public List<User> customerSql(@Param("username") String username);
/**
* 自定义sql 参数的第二种形式
*
* @param username
* @return
*/
@Query(value = "SELECT t FROM t_user t WHERE t.username = ?1")
public List<User> customerSql2(String username);
测试LIKE模糊条件
/**
* 自定义sql like的使用
*
* @param username
* @return
*/
@Query(value = "SELECT t FROM t_user t where t.username like %?1% ")
public List<User> customerLikeSql(@Param("username") String username);
/**
* 自定义sql like的使用
*
* @param username
* @return
*/
//也可以是 @Query(value = "SELECT t FROM t_user t WHERE t.username like %:username%")
@Query(value = "SELECT t FROM t_user t WHERE t.username like concat('%',:username ,'%')")
public List<User> customerLikeSql2(@Param("username") String username);
网上有的说不行 不确定是版本问题还是啥
/**
* 自定义sql like的使用
*
* @param username
* @return
*/
@Query(value = "SELECT t.* FROM t_user t where t.username like '%'||:username||'%' ",nativeQuery = true)
public List<User> customerNativeLikeSql(@Param("username") String username);
/**
* 自定义sql like的使用
*
* @param username
* @return
*/
@Query(value = "SELECT t.* FROM t_user t where t.username like '%'||?1||'%' ",nativeQuery = true)
public List<User> customerNativeLikeSql2(@Param("username") String username);
控制台
/**
* 复杂查询 分页 + 排序 + 筛选
* @param username
* @return
*/
@Query(value = "SELECT * FROM t_user as t WHERE t.username like '%'||:username||'%'",nativeQuery = true,countQuery = "select count(1) from(SELECT * FROM t_user as t WHERE t.username like '%'||:username||'%') as count")
public Page<User> customerNativeDifficultSql(@Param("username") String username, Pageable pageable);
@GetMapping("/testNativeExecuteDifficultQuery")
public Object testNativeExecuteDifficultQuery(String username,Integer page,Integer size){
Sort sort = Sort.by(Sort.Direction.ASC, "username");
PageRequest request = PageRequest.of(page, size, sort);
return userRepository.customerNativeDifficultSql(username,request);
}
自动分页 排序 计算总数
学习网站 https://www.baeldung.com/spring-data-jpa-query
场景说明
app收集学生信息.每一用户都需要上传学生信息 -> 一个用户对应一个学生信息 -> 一对一关联在student使用user_id来标识
package cn.huahua.springbooth2.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import javax.persistence.*;
/**
* @author LoveHuahua
* @date 2022年07月10日 19:15
* @description believe in yourself
*/
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", nullable = false)
private Long id;
/**
* 学生班级
*/
@Column(name = "class_name")
private String className;
/**
* 教师名称
*/
@Column(name = "teacher_name")
private String teacherName;
}
package cn.huahua.springbooth2.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.management.MalformedObjectNameException;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author LoveHuahua
* @date 2022年06月12日 23:38
* @description believe in yourself
*/
@Data
@Entity(name = "t_user")
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
* 主键
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
/**
* 账号
*/
@Column(name = "username")
private String username;
/**
* 密码
*/
@Column(name = "password")
private String password;
/**
* 一对一关联 用户关联学生表
*/
@OneToOne(cascade = CascadeType.ALL)
private Student student;
public User(String username, String password, Student student) {
this.username = username;
this.password = password;
this.student = student;
}
}
/**
* 测试jpa的新增
*
* @param
* @return
*/
@GetMapping("/testInsert")
public Object testInsert() {
/**
* 插入用户表数据
*/
ArrayList<User> entities = new ArrayList<>();
User user1 = new User("John", "John",new Student(null,"JAVA1班","JAVA老师"));
entities.add(user1);
User user2 = new User("John2", "John2",new Student(null,"JAVA2班","JAVA老师2"));
entities.add(user2);
User user3 = new User("John3", "John3",new Student(null,"JAVA3班","JAVA老师3"));
entities.add(user3);
return userRepository.saveAll(entities);
}
http://localhost:8080/testInsert
如果手动指定外键字段,使用
@JoinColumn
指定字段
例如
@JoinColumn(name = "my_student_id",referencedColumnName = "id")
效果如下:
混淆点:
@JoinColumn(name = "student_id",referencedColumnName = "id")
@JoinColumn和@PrimaryKeyJoinColumn
的区别
Use PrimaryKeyJoinColumn
Use JoinColumn
地址:https://stackoverflow.com/questions/3417097/jpa-difference-between-joincolumn-and-primarykeyjoincolumn
双向其实就是在关系的另一边,也进行一遍关系的维护,例如下面的实体类
package cn.huahua.springbooth2.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import javax.persistence.*;
/**
* @author LoveHuahua
* @date 2022年07月10日 19:15
* @description believe in yourself
*/
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
public Student(Long id, String className, String teacherName) {
this.id = id;
this.className = className;
this.teacherName = teacherName;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", nullable = false)
private Long id;
/**
* 学生班级
*/
@Column(name = "class_name")
private String className;
/**
* 教师名称
*/
@Column(name = "teacher_name")
private String teacherName;
/**
* 双向
*/
@OneToOne(cascade = CascadeType.ALL,mappedBy = "student")
private User user;
}
/**
* 一对一双向测试
* @param user
* @return
*/
@PostMapping("/oneToOne")
public Object oneToOne(@RequestBody User user) {
List<User> all = userRepository.findAll();
return all;
}
@JsonIgnoreProperties
注解忽略sudent中的user属性,这样就不会有循环序列化的问题了/**
* 一对一关联 用户关联学生表
*/
@OneToOne
@JsonIgnoreProperties(value = {"user"})
@JoinColumn(name = "id",referencedColumnName = "user_id")
private Student student;
新增其实已经看过了,就是调用save方法,我们使用restful风格去编写下四种操作
/**
* 一对一 测试新增
* @param user
* @return
*/
@PutMapping("/oneToOne")
public Object oneToOneSave(@RequestBody User user) {
userRepository.save(user);
return ResponseEntity.ok().body("success");
}
{
"id":1,
"username":"",
"password":"",
"student":{
"id":1,
"className":"JAVA1班",
"teacherName":"花花1号"
}
}
请求地址http://localhost:8080/oneToOne
返回
查看控制台
级联删除
如果id不存在 会直接报错
org.springframework.dao.EmptyResultDataAccessException: No class cn.huahua.springbooth2.entity.User entity with id 1 exists!
测试几种级联操作
@OneToMany
有一个属性是cascade
jpa的级联类型(Cascade Types)包括:
delete语句
查看数据库 也并没有删除操作@OneToOne(cascade = {CascadeType.MERGE,CascadeType.PERSIST})
使用了
CascadeType.REFRESH后,会级联的获取子对象在数据库的信息。
修改我们上面已经看过了,其实还是save
这里不展示了
查询也是一样 上面已经用很大篇幅说了 调用findAll就行
场景: 校内组织了论坛活动,每个用户都可以发布多个文章 -> 一对多
package cn.huahua.springbooth2.entity;
import lombok.Data;
import javax.persistence.*;
/**
* @author LoveHuahua
* @date 2022年07月09日 22:38
* @description believe in yourself
*/
@Entity(name = "article")
@Data
public class Article {
/**
* 主键id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
/**
* 标题
*/
@Column(name = "title")
private String title;
/**
* 文章内容
*/
@Column(name = "content")
private String content;
public Article(String title, String content) {
this.title = title;
this.content = content;
}
public Article() {
}
}
@OneToMany(cascade = CascadeType.ALL)
//为了避免权限问题 我们使用级联操作为 CascadeType.ALL
private List<Article> articles = new ArrayList<>();
package cn.huahua.springbooth2.entity;
import lombok.Data;
import javax.management.MalformedObjectNameException;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author LoveHuahua
* @date 2022年06月12日 23:38
* @description believe in yourself
*/
@Data
@Entity(name = "t_user")
public class User {
/**
* 主键
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
/**
* 账号
*/
@Column(name = "username")
private String username;
/**
* 密码
*/
@Column(name = "password")
private String password;
/**
* 一对一关联 用户关联学生表
*/
@OneToOne
@JoinColumn(name = "id",referencedColumnName = "user_id")
private Student student;
@OneToMany(cascade = CascadeType.ALL)
private List<Article> articles = new ArrayList<>();
public User(Long id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public User(String username, String password, List<Article> articles) {
this.username = username;
this.password = password;
this.articles = articles;
}
public User() {
}
}
修改数据插入操作
/**
* 测试jpa的新增
*
* @param
* @return
*/
@GetMapping("/testInsert")
public Object testInsert() {
/**
* 插入用户表数据
*/
ArrayList<User> entities = new ArrayList<>();
User user1 = new User( "John", "John",new ArrayList<Article>(){{
add(new Article("标题1", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}});
entities.add(user1);
User user2 = new User( "John2", "John2",new ArrayList<Article>(){{
add(new Article("标题2", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}});
entities.add(user2);
User user3 = new User( "John3", "John3",new ArrayList<Article>(){{
add(new Article("标题3", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}});
entities.add(user3);
/**
* 插入学生数据
*/
studentRepository.save(new Student(null, 1L, "JAVA1班", "花花1"));
studentRepository.save(new Student(null, 2L, "JAVA2班", "花花2"));
studentRepository.save(new Student(null, 3L, "JAVA3班", "花花3"));
;
return userRepository.saveAll(entities);
}
因为我们设置了级联操作为ALL
,所以保存是会传递的
默认中间表的命名是一对多中一表名_一对多中多的表名
使用@JoinColumn(name = "user_id")
指定关联字段 避免中间表的产生
注意点
@JoinColumn如果不加,也可以生成一对多的关联,但是会生成中间表,一般情况下1对多我们不会建立的,所以都会加上
@ManyToOne(cascade = CascadeType.ALL) //级联操作
@JoinColumn(name = "my_user_id",referencedColumnName = "id") //定义外键 也就是多表的字段是my_user_id
private User user;
@OneToMany(cascade = CascadeType.ALL,mappedBy = "user",fetch=FetchType.EAGER)
// @JoinColumn(name = "user_id") //注意这里 使用mappedBy 不然会有两个外键
// @JsonIgnoreProperties(value = {"user"}) 要忽略Article内部的user 不然序列化会死循环
private List<Article> articles = new ArrayList<>();
存在问题
使用增加接口,会发现我们的my_user_id
字段没有字段关联
/**
* 一对多 测试新增
* @param user
* @return
*/
@PutMapping("/oneToMany")
public Object oneToMany(@RequestBody User user) {
for (Article article : user.getArticles()) {
article.setUser(user);
}
userRepository.save(user);
return ResponseEntity.ok().body("success");
}
请求测试接口
{
"id":1,
"username":"",
"password":"",
"student":{
"id":1,
"className":"JAVA1班",
"teacherName":"花花1号 测试更新"
},
"articles":[
{
"title":"标题2",
"bookName":"java从入门到精通2"
},
{
"title":"标题3",
"bookName":"java从入门到精通3"
}
]
}
场景: 图书馆的借书场景,一本书可以被借多次,一个用户可以借多本
/**
* 学生借的书
*/
@ManyToMany(cascade = CascadeType.ALL)
private List<Book> books = new ArrayList<>();
package cn.huahua.springbooth2.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
/**
* @author LoveHuahua
* @date 2022年07月13日 23:38
* @description believe in yourself
*/
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
/**
* 书本名称
*/
@Column(name = "book_name")
private String bookName;
}
public User(String username, String password, List<Article> articles,List<Book> books) {
this.username = username;
this.password = password;
this.articles = articles;
this.books = books;
}
/**
* 测试jpa的新增
*
* @param
* @return
*/
@GetMapping("/testInsert")
public Object testInsert() {
/**
* 插入用户表数据
*/
ArrayList<User> entities = new ArrayList<>();
User user1 = new User("John", "John", new ArrayList<Article>() {{
add(new Article("标题1", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}}, new ArrayList<Book>() {{
add(new Book(null, "java从入门到精通1"));
}});
entities.add(user1);
User user2 = new User("John2", "John2", new ArrayList<Article>() {{
add(new Article("标题2", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}}, new ArrayList<Book>() {{
add(new Book(null, "java从入门到精通2"));
}});
entities.add(user2);
User user3 = new User("John3", "John3", new ArrayList<Article>() {{
add(new Article("标题3", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}}, new ArrayList<Book>() {{
add(new Book(null, "java从入门到精通3"));
}});
entities.add(user3);
/**
* 插入学生数据
*/
studentRepository.save(new Student(null, 1L, "JAVA1班", "花花1"));
studentRepository.save(new Student(null, 2L, "JAVA2班", "花花2"));
studentRepository.save(new Student(null, 3L, "JAVA3班", "花花3"));
return userRepository.saveAll(entities);
}
发现我们什么都没动,他就已经可以实现多表关联了
修改books参数
/**
* 学生借的书
*/
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "t_book_user",joinColumns = {
@JoinColumn(name = "user_id"),
},inverseJoinColumns = {
@JoinColumn(name = "book_id")
})
private List<Book> books = new ArrayList<>();
/**
* 学生借的书
*/
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "t_book_user", joinColumns = {
@JoinColumn(name = "user_id"),
}, inverseJoinColumns = {
@JoinColumn(name = "book_id")
})
@JsonIgnoreProperties(value = {"users"})
private List<Book> books = new ArrayList<>();
@ManyToMany(cascade = CascadeType.ALL,mappedBy = "books")
private List<User> users = new ArrayList<User>();
/**
* 测试jpa的新增
*
* @param
* @return
*/
@GetMapping("/testInsert")
public Object testInsert() {
Book book = new Book(null, "java从入门到精通3");
/**
* 插入用户表数据
*/
ArrayList<User> entities = new ArrayList<>();
User user1 = new User("John", "John",new Student(null,"JAVA1班","JAVA老师"),new ArrayList<Article>() {{
add(new Article("标题1", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}}, new ArrayList<Book>() {{
add(new Book(null, "java从入门到精通1"));
add(book);
}});
entities.add(user1);
User user2 = new User("John2", "John2",new Student(null,"JAVA2班","JAVA老师2"), new ArrayList<Article>() {{
add(new Article("标题2", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}}, new ArrayList<Book>() {{
add(new Book(null, "java从入门到精通2"));
add(book);
}});
entities.add(user2);
User user3 = new User("John3", "John3",new Student(null,"JAVA3班","JAVA老师3"), new ArrayList<Article>() {{
add(new Article("标题3", "有秀儿1不理我啊啊啊啊啊啊啊啊啊啊啊啊啊"));
}}, new ArrayList<Book>() {{
add(book);
}});
entities.add(user3);
return userRepository.saveAll(entities);
}
http://localhost:8080/testList?page=0&size=10
@JsonIgnoreProperties(value = {"users"})
删除了,并且为了防止死循环,在book中的users添加了@JsonIgnoreProperties(value = {"books"})