使用 Spring Boot 的 JPA / Hibernate 多对多映射示例

x33g5p2x  于2021-10-20 转载在 Spring  
字(6.4k)|赞(0)|评价(0)|浏览(458)

在本文中,您将学习如何使用 JPA 和 Hibernate 在应用程序中的对象级别映射多对多数据库关系。

考虑以下表格,其中 poststags 显示彼此之间的多对多关系 -

多对多关系是使用名为 post_tags 的第三个表实现的,该表包含帖子的详细信息及其相关标签。

现在让我们从头开始创建一个项目,并学习如何使用 JPA 和 Hibernate 实现这种多对多关系。

创建项目

您可以通过在终端中键入以下命令来使用 Spring Boot CLI 创建项目 -

spring init -n=jpa-many-to-many-demo -d=web,jpa,mysql --package-name=com.example.jpa jpa-many-to-many-demo

如果您没有安装 Spring Boot CLI,您可以使用 Spring Initializr 网络工具按照以下说明引导项目 -

  1. 前往http://start.spring.io
    1.输入Artifact为“jpa-many-to-many-demo”
  2. 单击 Options 下拉列表以查看与项目元数据相关的所有选项。
  3. 包名称更改为“com.example.jpa”
  4. 选择WebJPAMysql 依赖项。
  5. 点击Generate下载项目。

以下是项目的目录结构供大家参考——

此时,您的引导项目不会有 modelrepository 包以及这些包中的所有类。我们很快就会创建它们。

配置数据库和休眠日志级别

我们需要配置 MySQL 数据库 URL、用户名和密码,以便 Spring 可以在启动时与数据库建立连接。

打开 src/main/resources/application.properties 并向其添加以下属性 -

# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:mysql://localhost:3306/jpa_many_to_many_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=root

# Hibernate

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE

请务必根据您的 MySQL 安装更改 spring.datasource.usernamespring.datasource.password 属性。此外,创建一个名为 jpa_many_to_many_demo 的数据库。

spring.jpa.hibernate.ddl-auto = update 属性确保应用程序中的数据库表和域模型同步。每当您更改域模型时,hibernate 将在您重新启动应用程序时自动更新数据库中的映射表。

我还指定了 hibernate 的日志级别,以便我们可以调试 hibernate 执行的 SQL 查询。

定义领域模型

让我们定义将映射到我们之前看到的表的域模型。首先,在 com.example.jpa 中创建一个名为 model 的包,然后在 model 包中添加以下类 -

1.后模型

package com.example.jpa.model;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "posts")
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(max = 100)
    @Column(unique = true)
    private String title;

    @NotNull
    @Size(max = 250)
    private String description;

    @NotNull
    @Lob
    private String content;

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "posted_at")
    private Date postedAt = new Date();

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "last_updated_at")
    private Date lastUpdatedAt = new Date();


    @ManyToMany(fetch = FetchType.LAZY,
            cascade = {
                CascadeType.PERSIST,
                CascadeType.MERGE
            })
    @JoinTable(name = "post_tags",
            joinColumns = { @JoinColumn(name = "post_id") },
            inverseJoinColumns = { @JoinColumn(name = "tag_id") })
    private Set<Tag> tags = new HashSet<>();


    public Post() {

    }

    public Post(String title, String description, String content) {
        this.title = title;
        this.description = description;
        this.content = content;
    }

    // Getters and Setters (Omitted for brevity)
}

2.标签型号

package com.example.jpa.model;

import org.hibernate.annotations.NaturalId;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "tags")
public class Tag {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(max = 100)
    @NaturalId
    private String name;

    @ManyToMany(fetch = FetchType.LAZY,
            cascade = {
                CascadeType.PERSIST,
                CascadeType.MERGE
            },
            mappedBy = "tags")
    private Set<Post> posts = new HashSet<>();

    public Tag() {

    }

    public Tag(String name) {
        this.name = name;
    }

    // Getters and Setters (Omitted for brevity)
}

我们使用 @ManyToMany 注释在两个实体之间创建多对多关系。在双向关联中,@ManyToMany 注释用于两个实体,但只有一个实体可以是关系的所有者。

指定 @JoinTable 的实体是关系的拥有方,指定 mappedBy 属性的实体是反面。

在上面的例子中,Post 实体是关系的所有者,而 Tag 实体是相反的一方。

定义存储库

让我们定义 repositories 来访问数据库中的 PostTag 数据。

首先,在 com.example.jpa 包中创建一个名为 repository 的新包,然后在 repository 包中创建以下存储库 -

1. PostRepository

package com.example.jpa.repository;

import com.example.jpa.model.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {

}

2.标签库

package com.example.jpa.repository;

import com.example.jpa.model.Tag;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TagRepository extends JpaRepository<Tag, Long> {

}

编写代码来测试多对多关系

是时候编写一些代码来测试我们的多对多关联设置了。打开主类 JpaManyToManyDemoApplication.java 并将其替换为以下代码 -

package com.example.jpa;

import com.example.jpa.model.Post;
import com.example.jpa.model.Tag;
import com.example.jpa.repository.PostRepository;
import com.example.jpa.repository.TagRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JpaManyToManyDemoApplication implements CommandLineRunner {

    @Autowired
    private TagRepository tagRepository;

    @Autowired
    private PostRepository postRepository;

    public static void main(String[] args) {
        SpringApplication.run(JpaManyToManyDemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // Cleanup the tables
        postRepository.deleteAllInBatch();
        tagRepository.deleteAllInBatch();

        // =======================================

        // Create a Post
        Post post = new Post("Hibernate Many to Many Example with Spring Boot",
                "Learn how to map a many to many relationship using hibernate",
                "Entire Post content with Sample code");

        // Create two tags
        Tag tag1 = new Tag("Spring Boot");
        Tag tag2 = new Tag("Hibernate");


        // Add tag references in the post
        post.getTags().add(tag1);
        post.getTags().add(tag2);

        // Add post reference in the tags
        tag1.getPosts().add(post);
        tag2.getPosts().add(post);

        postRepository.save(post);

        // =======================================

    }
}

上述类中的 run() 方法在应用程序启动时被调用。在 run() 方法中,我们首先清理表格,然后创建一个 Post,两个标签,然后在帖子中添加标签。最后,我们使用 PostRepository::save() 方法保存帖子引用,该方法也保存标签。

运行应用程序

您可以通过在终端中键入以下命令来运行该应用程序 -

mvn spring-boot:run

应用程序启动后,检查控制台是否有 hibernate 特定的日志,以查看 hibernate 为我们在 run() 方法中执行的操作执行了哪些 SQL 语句 -

org.hibernate.SQL : delete from post_tags where (post_id) in (select id from posts)
org.hibernate.SQL : delete from posts
org.hibernate.SQL : delete from post_tags where (tag_id) in (select id from tags)
org.hibernate.SQL : delete from tags
org.hibernate.SQL : insert into posts (content, description, last_updated_at, posted_at, title) values (?, ?, ?, ?, ?)
org.hibernate.SQL : insert into tags (name) values (?)
org.hibernate.SQL : insert into tags (name) values (?)
org.hibernate.SQL : insert into post_tags (post_id, tag_id) values (?, ?)
org.hibernate.SQL : insert into post_tags (post_id, tag_id) values (?, ?)

相关文章

微信公众号

最新文章

更多