java - Spring Boot 数据存储库上的复合主外键

标签 java jpa spring-data-jpa eclipselink

我正在尝试在两个实体之间建立关系,即订阅交付。两个实体都应使用用户的 id 作为主键(即,两者都与 User 处于一对一关系,但 Delivery 仅使用也为用户的 id存在于订阅中)。 Delivery 还使用属性 email 作为附加键(与用户 ID 一起建立复合主键)。虽然我用作 spring boot 的 jpa 后端的 eclipselink 似乎适合此定义,但在包含 jpa 存储库并显示以下错误消息时,我的应用程序崩溃了。

错误消息:

Caused by: java.lang.IllegalArgumentException: Expected id attribute type [class com.tnt.entity.Subscription$DeliveryPK] on the existing id attribute [SingularAttributeImpl[EntityTypeImpl@482885994:Subscription [ javaType: class com.tnt.entity.Subscription descriptor: RelationalDescriptor(com.tnt.entity.Subscription --> [DatabaseTable(tbl_subscription)]), mappings: 6],org.eclipse.persistence.mappings.ManyToOneMapping[subscription]]] on the identifiable type [EntityTypeImpl@1615668218:Delivery [ javaType: class com.tnt.entity.Subscription$Delivery descriptor: RelationalDescriptor(com.tnt.entity.Subscription$Delivery --> [DatabaseTable(tbl_subscription_delivery)]), mappings: 2]] but found attribute type [class com.tnt.entity.Subscription].
    at org.eclipse.persistence.internal.jpa.metamodel.IdentifiableTypeImpl.getId(IdentifiableTypeImpl.java:204) ~[org.eclipse.persistence.jpa-2.7.0.jar:na]
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation$IdMetadata.<init>(JpaMetamodelEntityInformation.java:262) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:88) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:66) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:211) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:161) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:144) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:69) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:312) ~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297) ~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:212) ~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.util.Lazy.get(Lazy.java:94) ~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300) ~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:121) ~[spring-data-jpa-2.2.3.RELEASE.jar:2.2.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    ... 88 common frames omitted

实体:

package com.tnt.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Objects;

@Entity
@Table(name = "tbl_subscription")
public class Subscription {
    @Column(name = "user_id")
    @Id
    private Long id;

    @Column(name = "daily_report")
    @Basic
    private boolean receiveDailyReport;

    @Column(name = "weekly_report")
    @Basic
    private boolean receiveWeeklyReport;

    @Column(name = "monthly_report")
    @Basic
    private boolean receiveMonthlyReport;

    @Column(name = "multi_report")
    @Basic
    private boolean multiReport;

    @PrimaryKeyJoinColumn(name = "user_id")
    @OneToOne(optional = false)
    @JsonIgnore
    private User user;

    public Subscription() {

    }

    public Subscription(User user){
        this.user = user;
        this.receiveDailyReport = true;
    }

    @Entity
    @Table(name = "tbl_subscription_delivery")
    @IdClass(DeliveryPK.class)
    public static class Delivery {
        @JoinColumn(name = "subscription_id")
        @ManyToOne
        @Id
        private Subscription subscription;

        @Column(name = "email")
        @Id
        private String email;

        public Delivery() {

        }

        public Delivery(Subscription subscription, String email){
            this.subscription = subscription;
            this.email = email;
        }

        public Subscription getSubscription() {
            return subscription;
        }

        public void setSubscription(Subscription subscription) {
            this.subscription = subscription;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        // ... equals, hashCode
    }

    public static class DeliveryPK implements Serializable {

        private Long subscription;

        private String email;

        public DeliveryPK() {
        }

        public Long getSubscription() {
            return subscription;
        }

        public void setSubscription(Long subscription) {
            this.subscription = subscription;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        // ... equals, hashCode
    }

    // ... getter, setter, equals, hashCode
}

存储库接口(interface):

interface DeliveryRepository : JpaRepository<Delivery, DeliveryPK> {
    @Query("SELECT d FROM Delivery d WHERE d.subscription = :subscription AND d.email = :email")
    fun findByEmail(subscription: Subscription, email: String): Optional<Delivery>
}

有人知道我做错了什么吗?

编辑:这个问题也可以用不同的方式提出 - 如果我用 SQL 对此进行建模,它可能看起来像这样:

CREATE TABLE tbl_user (
    id BIGINT NOT NULL PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    password VARCHAR(255),
    salt VARCHAR(64)
);

CREATE TABLE tbl_subscription (
    user_id BIGINT NOT NULL REFERENCES tbl_user(id),
    daily_report BOOLEAN,
    weekly_report BOOLEAN,
    monthly_report BOOLEAN,
    multi_report BOOLEAN,
    PRIMARY KEY (user_id)
);

CREATE TABLE tbl_subscription_delivery (
    subscription_id BIGINT NOT NULL REFERENCES tbl_subscription(user_id),
    email VARCHAR(255),
    PRIMARY KEY(email, subscription_id)
);

如何在 JPA 2.0 中建模这种行为?

最佳答案

您可以使用单个主键关系属性映射订阅:

@Entity
@Table(name = "tbl_subscription")
public class Subscription {
    @Id
    @OneToOne(optional = false)
    @JoinColumn(name = "user_id")
    private User user;

    @Column(name = "daily_report")
    @Basic
    private boolean receiveDailyReport;

    @Column(name = "weekly_report")
    @Basic
    private boolean receiveWeeklyReport;

    @Column(name = "monthly_report")
    @Basic
    private boolean receiveMonthlyReport;

    @Column(name = "multi_report")
    @Basic
    private boolean multiReport;
...

或者,您可以使用 @MapsId 映射 Subscription (希望这与您在上面的评论中已经尝试过的内容不完全相同:) ):

@Entity
@Table(name = "tbl_subscription")
public class Subscription {
    @Id
    private Long id;

    @OneToOne
    @JoinColumn(name = "user_id")
    @MapsId
    private User user;

    @Column(name = "daily_report")
    @Basic
    private boolean receiveDailyReport;

    @Column(name = "weekly_report")
    @Basic
    private boolean receiveWeeklyReport;

    @Column(name = "monthly_report")
    @Basic
    private boolean receiveMonthlyReport;

    @Column(name = "multi_report")
    @Basic
    private boolean multiReport;
...

关于java - Spring Boot 数据存储库上的复合主外键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62023911/

相关文章:

java - 导入的 java 类中的公共(public)静态最终变量

java - GoogleAppEngine 日志警告

java - Hibernate标准问题: could not resolve property:

java - 对 Hibernate(和 JPA)标准的表达能力的限制?

jpa - Spring Boot JPA 批量插入

java - Spring Data JPA 返回空存储库

java - Android ImageView 顶部裁剪

java - 使进程等待

java - Spring 数据JPA : filter data based on used ManyToOne field

spring - 在具有相似 ID 的 Spring Boot 中加入没有外键的两个表