在Spring Data JPA中,使用@OneToMany和@ManyToOne来表示一对多的双向关联。例如,一端(Author)使用@OneToMany,多端(Article)使用@ManyToOne。
在JPA规范中,一对多的双向关系由多端(如Article)来维护。就是说多端为关系的维护端,负责关系的增删改查。一端则为关系的被维护端,不能维护关系。
一端(Author)使用@OneToMany注解的mappedBy="author"属性表明一端(Author)是关系的被维护端。多端(Article)使用@ManyToOne和@JoinColumn来注解属性author,@ManyToOne表明Article是多端,@JoinColumn设置在article表的关联字段(外键)上。
使用Spring Data JPA实现Author与Article的一对多关系映射 1)创建持久化实体类 2)创建数据访问层 3)创建业务层 4)创建控制器类 5)运行
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.onetomany</groupId> <artifactId>SpringBootOneToMany</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <!-- 声明项目配置依赖编码格式为 utf-8 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <fastjson.version>1.2.24</fastjson.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 添加MySQL依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>8.0.13</version><!--$NO-MVN-MAN-VER$ --> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
server.port=8089 server.servlet.context-path=/ch6_2 spring.datasource.url=jdbc:mysql://localhost:3306/springbootjpa?serverTimezone=UTC&autoReconnect=true spring.datasource.username=root spring.datasource.password=admin spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.database=MYSQL spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update spring.jackson.serialization.indent-output=true
1)创建持久化实体类 在com.ch.ch6_2.entity包中,创建名为Author和Article的持久化实体类。 @Entity @Table(name = "author_table") @JsonIgnoreProperties(value = { "hibernateLazyInitializer"}) public class Author implements Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; //作者名 private String aname; //文章列表,作者与文章是一对多的关系 @OneToMany( mappedBy = "author", cascade=CascadeType.ALL, targetEntity = Article.class, fetch=FetchType.LAZY ) private List<Article> articleList; //省略set和get方法 }
package com.ch.ch6_2.entity; import java.io.Serializable; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Entity @Table(name = "author_table") @JsonIgnoreProperties(value = { "hibernateLazyInitializer" }) public class Author implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 作者名 private String aname; // 文章列表,作者与文章是一对多的关系 @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, targetEntity = Article.class, fetch = FetchType.LAZY) private List<Article> articleList; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAname() { return aname; } public void setAname(String aname) { this.aname = aname; } public List<Article> getArticleList() { return articleList; } public void setArticleList(List<Article> articleList) { this.articleList = articleList; } }
package com.ch.ch6_2.entity; import java.io.Serializable; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; 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.Lob; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Entity @Table(name = "article_table") @JsonIgnoreProperties(value = { "hibernateLazyInitializer" }) public class Article implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 标题 @NotEmpty(message = "标题不能为空") @Size(min = 2, max = 50) @Column(nullable = false, length = 50) private String title; // 文章内容 @Lob // 大对象,映射 为MySQL的Long文本类型 @Basic(fetch = FetchType.LAZY) @NotEmpty(message = "内容不能为空") @Size(min = 2) @Column(nullable = false) private String content; // 所属作者,文章与作者是多对一的关系 @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH }, optional = false) // 可选属性optional=false,表示author不能为空。删除文章,不影响用户 @JoinColumn(name = "id_author_id") // 设置在article表中的关联字段(外键) @JsonIgnore private Author author; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } }
2)创建数据访问层
在com.ch.ch6_2.repository包中,创建名为AuthorRepository和ArticleRepository的接口。
package com.ch.ch6_2.repository; //import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import com.ch.ch6_2.entity.Author; public interface AuthorRepository extends JpaRepository<Author, Integer> { /** * 根据文章标题包含的内容,查询作者(关联查询) 相当于JPQL语句:select a from Author a inner join * a.articleList t where t.title like %?1% */ public Author findByArticleList_titleContaining(String title); /** * 根据文章标题包含的内容,查询作者(关联查询) */ @Query("select a from Author a inner join a.articleList t where t.title like %?1%") public Author findAuthorByArticleListtitleContaining(String title); }
package com.ch.ch6_2.repository; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import com.ch.ch6_2.entity.Article; public interface ArticleRepository extends JpaRepository<Article, Integer> { /** * 根据作者id查询文章信息(关联查询,根据author属性的id) 相当于JPQL语句:select a from Article a where * a.author.id = ?1 */ public List<Article> findByAuthor_id(Integer id); /** * 根据作者名查询文章信息(关联查询,根据author属性的aname) 相当于JPQL语句:select a from Article a where * a.author.aname = ?1 */ public List<Article> findByAuthor_aname(String aname); }
3)创建业务层
在com.ch.ch6_2.service包中,创建名为AuthorAndArticleService的接口和接口实现类AuthorAndArticleServiceImpl。
package com.ch.ch6_2.service; import java.util.List; import com.ch.ch6_2.entity.Article; import com.ch.ch6_2.entity.Author; public interface AuthorAndArticleService { public void saveAll(); public List<Article> findByAuthor_id(Integer id); public List<Article> findByAuthor_aname(String aname); public Author findByArticleList_titleContaining(String title); public Author findAuthorByArticleListtitleContaining(String title); }
package com.ch.ch6_2.service; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ch.ch6_2.entity.Article; import com.ch.ch6_2.entity.Author; import com.ch.ch6_2.repository.ArticleRepository; import com.ch.ch6_2.repository.AuthorRepository; @Service public class AuthorAndArticleServiceImpl implements AuthorAndArticleService { @Autowired private AuthorRepository authorRepository; @Autowired private ArticleRepository articleRepository; @Override public void saveAll() { // 保存作者(先保存一的一端) Author a1 = new Author(); a1.setAname("陈恒1"); Author a2 = new Author(); a2.setAname("陈恒2"); ArrayList<Author> allAuthor = new ArrayList<Author>(); allAuthor.add(a1); allAuthor.add(a2); authorRepository.saveAll(allAuthor); // 保存文章 Article at1 = new Article(); at1.setTitle("JPA的一对多111"); at1.setContent("其实一对多映射关系很常见111。"); // 设置关系 at1.setAuthor(a1); Article at2 = new Article(); at2.setTitle("JPA的一对多222"); at2.setContent("其实一对多映射关系很常见222。"); // 设置关系 at2.setAuthor(a1);// 文章2与文章1作者相同 Article at3 = new Article(); at3.setTitle("JPA的一对多333"); at3.setContent("其实一对多映射关系很常见333。"); // 设置关系 at3.setAuthor(a2); Article at4 = new Article(); at4.setTitle("JPA的一对多444"); at4.setContent("其实一对多映射关系很常见444。"); // 设置关系 at4.setAuthor(a2);// 文章3与文章4作者相同 ArrayList<Article> allAt = new ArrayList<Article>(); allAt.add(at1); allAt.add(at2); allAt.add(at3); allAt.add(at4); articleRepository.saveAll(allAt); } @Override public List<Article> findByAuthor_id(Integer id) { return articleRepository.findByAuthor_id(id); } @Override public List<Article> findByAuthor_aname(String aname) { return articleRepository.findByAuthor_aname(aname); } @Override public Author findByArticleList_titleContaining(String title) { return authorRepository.findByArticleList_titleContaining(title); } @Override public Author findAuthorByArticleListtitleContaining(String title) { return authorRepository.findAuthorByArticleListtitleContaining(title); } }
4)创建控制器类
在com.ch.ch6_2.controller包中,创建名为TestOneToManyController的控制器类。
package com.ch.ch6_2.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ch.ch6_2.entity.Article; import com.ch.ch6_2.entity.Author; import com.ch.ch6_2.service.AuthorAndArticleService; @RestController public class TestOneToManyController { @Autowired private AuthorAndArticleService authorAndArticleService; @RequestMapping("/saveOneToMany") public String save() { authorAndArticleService.saveAll(); return "作者和文章保存成功!"; } @RequestMapping("/findArticleByAuthor_id") public List<Article> findByAuthor_id(Integer id) { return authorAndArticleService.findByAuthor_id(id); } @RequestMapping("/findArticleByAuthor_aname") public List<Article> findByAuthor_aname(String aname) { return authorAndArticleService.findByAuthor_aname(aname); } @RequestMapping("/findByArticleList_titleContaining") public Author findByArticleList_titleContaining(String title) { return authorAndArticleService.findByArticleList_titleContaining(title); } @RequestMapping("/findAuthorByArticleListtitleContaining") public Author findAuthorByArticleListtitleContaining(String title) { return authorAndArticleService.findAuthorByArticleListtitleContaining(title); } }
首先,运行Ch62Application主类。然后,访问“http://localhost:8089/ch6_2/saveOneToMany/”。
package com.ch.ch6_2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Ch62Application { public static void main(String[] args) { SpringApplication.run(Ch62Application.class, args); } }