【问题标题】:Obtain column data from One-To-Many join relation in Spring data JPA从Spring数据JPA中的一对多连接关系中获取列数据
【发布时间】:2020-01-25 04:01:32
【问题描述】:

假设我有两个数据库表,Product 和 ProductDetails。

create table Product
{
    product_id int not null,
    product_name varchar(100) not null,
    PRIMARY KEY (product_id)
}
create table ProductDetails
{
    detail_id int not null,
    product_id int not null,
    description varchar(100) not null,
    PRIMARY KEY (detail_id,product_id),
    FOREIGN KEY (product_id) REFERENCES Product(product_id)
}

每个产品可以有多个产品详情条目,但每个产品详情只能属于一个产品。在 SQL 中,我希望能够检索每个产品详细信息,但也可以使用产品名称,我会使用 join 语句来实现。

select p.product_id,pd.detail_id,p.product_name,pd.description
from Product p join ProductDetails pd on p.product_id=pd.product_id

现在我需要在 Spring data JPA 表单中包含这个概念。我目前的理解如下:

@Table(name = "Product")
public class ProductClass
{
    private int productID;
    private String productName;
}

@Table(name = "ProductDetails")
public class ProductDetailsClass
{
    private int detailID;
    private int productID;

    // this is the part I don't know how to set. @OneToMany? @ManyToOne? @JoinTable? @JoinColumn?
    private String productName;

    private String description;
}

(我没有包含任何属性,例如@Id 以保持代码最少)

我需要写什么才能让private String productName; 工作? 我对@JoinTable@OneToMany 和其他属性的研究让我更加困惑。

附:这是我继承的遗留 Java 程序。 private String productName; 部分不在原始代码中,但现在我需要 ProductDetails 类才能使 productName 可用。

附言在尝试任何事情和部署之前,我想清楚地了解我在做什么。这是一个部署到生产环境的遗留程序,据我了解,这里的任何代码更改也会改变数据库结构,再多的钱也不足以让我想恢复 Java 程序、Spring 框架、Apache如果发生任何灾难性事件,服务器和 MySQL 数据库将恢复正常工作。另外我真的没有开发环境来测试这个。帮助...

【问题讨论】:

    标签: java join spring-data-jpa


    【解决方案1】:

    您的研究已经朝着正确的方向发展:您需要@OneToMany 关系。对 Hibernate 的最佳描述是 Vlad Mihalcea。在他的网页上,您还可以找到对这些关系的很好解释:The best way to map a @OneToMany relationship with JPA and Hibernate

    首先,您必须正确创建实体(实体由关系数据库中的表表示)。

    单向 (@OneToMany)

    @Entity
    @Table(name = "product")
    public class Product
    {
        @Id
        @GeneratedValue
        private Long productID;
    
        private String productName;
    
        @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
        private List<ProductDetail> productDetails;
    
        //Constructors, getters and setters...
    }
    
    @Entity
    @Table(name = "product_details")
    public class ProductDetail
    {
        @Id
        @GeneratedValue
        private Long detailID;
    
        private String description;
    
        //Constructors, getters and setters...
    }
    

    这是基于单向关系。因此,每个 Product 都知道所有分配的 ProductDetails。但是 ProductDetails 没有指向其产品的链接。但是,不推荐这种单向实现。这会导致数据库的大小增加,即使使用@JoinColumn 进行优化也不理想,因为SQL 调用较多。

    单向 (@ManyToOne)

    @Entity
    @Table(name = "product")
    public class Product
    {
        @Id
        @GeneratedValue
        private Long productID;
    
        private String productName;
    
        //Constructors, getters and setters...
    }
    
    @Entity
    @Table(name = "product_details")
    public class ProductDetail
    {
        @Id
        @GeneratedValue
        private Long detailID;
    
        private String description;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = product_id)
        private Product product;
    
        //Constructors, getters and setters...
    }
    

    在这种单向关系中,只有 ProductDetails 知道分配给它们的 Product。考虑一下每个产品的大量 ProductDetail 对象。

    @JoinColumn 注释指定表product_details 的列的名称,其中保存了 Product 的外键(其 id)。它也可以不使用,但使用此注释会更有效。

    双向(@OneToMany 和 @ManyToOne)

    @Entity
    @Table(name = "product")
    public class Product
    {
        @Id
        @GeneratedValue
        private Long productID;
    
        private String productName;
    
        @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
        private List<ProductDetail> productDetails;
    
        //Constructors, add, remove method, getters and setters...
    }
    
    @Entity
    @Table(name = "product_details")
    public class ProductDetail
    {
        @Id
        @GeneratedValue
        private Long detailID;
    
        private String description;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = product_id)
        private Product product;
    
        //Constructors, getters and setters...
    }
    

    通过双向关系,双方的对象(Product 和 ProductDetail)知道哪些其他对象被分配给它们。

    但根据 Vlad Mihalcea 的说法,this should not be used if too many ProductDetails exist per Product

    还记得为列表条目实现正确的添加和删除方法(再次参见articleotherwise weird exceptions)。

    杂项

    通过级联,产品中的更改也会应用于其 ProductDetails。 OrphanRemoval 避免了没有 Product 的 ProductDetails。

    Product product = new Product("Interesting Product");
    
    product.getProductDetails().add(
        new ProductDetails("Funny description")
    );
    product.getProductDetails().add(
        new ProductDetails("Different description")
    );
    
    entityManager.persist(product);
    

    关于正确的 equals 和 hashCode 方法的问题通常是您脑海中的一个复杂难题。特别是对于双向关系,但在其他依赖数据库连接的情况下,建议使用implement them quite simply as described by Vlad

    将对象用于原始数据类型也是一种很好的做法。这使您可以选择在调用 getter 时检索正确的 null。

    Avoiding eager fetching should be quite clear...

    当您现在尝试从数据库中检索产品时,该对象会自动拥有分配给它的所有 ProductDetails 的列表。为此,JPA repositories in Spring could be used。不必实施简单的方法。当您需要更多自定义功能时,请查看this article by Baeldung

    【讨论】:

    • 我实际上对 ProductDetail 的对象比对 Product 更感兴趣。如果我有一个 ProductDetail 对象,我需要在该对象中有 productName。根据 Vlad Mihalcea 的文章,这是否意味着我必须放入 ProductDetail 类,这部分“@ManyToOne(fetch = FetchType.LAZY) [newline] @JoinColumn(name = "product_id") [newline] private Product prod; ”,然后通过“proddet.prod.productName”之类的方式访问产品名称?
    • @VincentTan 在 Vlad 的文章和上面的更新帖子中可以找到两个不同的版本。添加@ManyToOne 会导致双向关系,如果每个产品的ProductDetail 对象数量不太高(不要忘记mappedBy),这可能会很好。另一个版本将只有@ManyToOne 注释,而另一侧没有@OneToMany。这导致了另一种单向关系,其中只有 ProductDetail 对象知道他们的产品。您必须决定哪一个更适合您的用例。
    猜你喜欢
    • 2021-11-30
    • 2018-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-16
    • 1970-01-01
    相关资源
    最近更新 更多