Составной первичный внешний ключ в репозитории данных загрузки Spring - PullRequest
0 голосов
/ 26 мая 2020

Я пытаюсь установить sh связь между двумя сущностями, а именно Subscription и Delivery. Обе сущности должны использовать идентификатор пользователя в качестве своего первичного ключа (т. Е. Оба находятся во взаимно-однозначном отношении с User, но Delivery использует только идентификаторы, которые также присутствуют в Subscription). Delivery также использует атрибут email в качестве дополнительного ключа (вместе с идентификатором пользователя для sh составного первичного ключа). В то время как eclipselink, который я использую в качестве бэкэнда jpa для весенней загрузки, кажется, подходит для этого определения, мое приложение вылетает при включении репозитория 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 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?

1 Ответ

0 голосов
/ 27 мая 2020

Вы можете сопоставить Subscription с одним атрибутом отношения первичного ключа:

@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;
...

В качестве альтернативы вы можете сопоставить Subscription с @MapsId (надеясь, что это не совсем то же самое, что вы сказали в комментарии выше, что уже пробовали :)):

@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;
...
...