Как сохранить отношения «многие ко многим» с помощью spring-data-jpa и составных ключей - PullRequest
0 голосов
/ 14 мая 2019

У меня есть отношение многие ко многим, которое я пытаюсь сохранить, используя spring-data-jpa и hibernate.Оба класса имеют составные ключи.Я использую класс соединения с обоими классами в списке.Я получаю ошибку, которая подразумевает, что объект уже существует.Я не знаю, что я делаю здесь не так.Вот мой код:

Транзакция

@Entity
@Table(name = "transaction")
@IdClass(Transaction.TransactionPK.class)
public class Transaction {
    @Id
    private Long userId;

    @Id
    @GeneratedValue(generator = "transaction_generator")
    @SequenceGenerator(
            name = "transaction_generator",
            sequenceName = "transaction_sequence",
            initialValue = 1000,
            allocationSize = 1
    )
    private Long transactionId;

    @OneToMany(mappedBy = "transaction",
            cascade = {CascadeType.PERSIST, CascadeType.MERGE}
            )
    private Set<TransactionTag> tags = new HashSet<>();

    public void addTag(Tag tag) {
        TransactionTag postTag = new TransactionTag(this, tag);
        tags.add(postTag);
        tag.getTransactions().add(postTag);
    }

    //getters and setters

    static class TransactionPK implements Serializable {
        private Long transactionId;
        private Long userId;

        public TransactionPK(Long userId, Long transactionId) {
            this.userId = userId;
            this.transactionId = transactionId;
        }

        public TransactionPK() {}

    //equals, hashcode and getters and setters

Метка

@Entity
@Table(name = "tag", uniqueConstraints={
        @UniqueConstraint(columnNames = {"userId", "tagName"})
})
@IdClass(Tag.TagPK.class)
public class Tag {
    @Id
    private Long userId;

    @Id
    @GeneratedValue(generator = "tag_generator")
    @SequenceGenerator(
            name = "tag_generator",
            sequenceName = "tag_sequence",
            initialValue = 1000,
            allocationSize = 1
    )
    private Long tagId;

    @NotNull
    @Column(nullable = false)
    private String tagName;

    @OneToMany(
            mappedBy = "tag",
            cascade = {CascadeType.PERSIST, CascadeType.MERGE}
    )
    private Set<TransactionTag> transactions;

    //getters and setters

    static class TagPK implements Serializable {

        private Long userId;
        private Long tagId;

        public TagPK(Long userId, Long tagId) {
            this.userId = userId;
            this.tagId = tagId;
        }

        //equals, hashcode and getters and setters

TransactionTag

@Entity
public class TransactionTag implements  Serializable {

    @EmbeddedId
    private TransactionTagId id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({
            @JoinColumn(name="user_id", insertable = false, updatable = false),
            @JoinColumn(name="transaction_id", insertable = false, updatable = false)
    })
    private Transaction transaction;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({
            @JoinColumn(name="user_Id", insertable = false, updatable = false),
            @JoinColumn(name="tag_Id", insertable = false, updatable = false)
    })
    private Tag tag;

    private TransactionTag() {}

    public TransactionTag(Transaction transaction, Tag tag) {
        this.transaction = transaction;
        this.tag = tag;
        this.id = new TransactionTagId(transaction.getTransactionId(),tag.getTagId());
    }

    //equals, hashcode and getters and setters

    @Embeddable
    static class TransactionTagId implements Serializable {

        @Column(name = "transaction_id")
        private Long transactionId;

        @Column(name = "tag_id")
        private Long tagId;

        private TransactionTagId() {}

        public TransactionTagId(
                Long transactionId,
                Long tagId) {
            this.transactionId = transactionId;
            this.tagId = tagId;
        }

        //equals, hashcode and getters and setters

Сервисный код

public Transaction save(Long userId, TransactionDto transactionDto) {
        Transaction transaction = new Transaction();

        if(transactionDto.getTags()!=null && transactionDto.getTags().size()>0) {
            for(Long tagId:transactionDto.getTags()) {
                Tag tag = tagService.findByUserIdAndTagId(userId, tagId);
                transaction.addTag(tag);
            }
        }

        return transactionRepo.saveAndFlush(transaction);

Я получаю следующую ошибку:

javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.checking.register.model.TransactionTag#com.checking.register.model.TransactionTag$TransactionTagId@7a9]
        at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
        at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
        at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
        at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1460) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
        at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1440) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
...
...

Редактировать

Было предложено удалить одну из строк в методе addTag() в транзакции.Если я удаляю третью строку, я получаю:

Hibernate: insert into transaction_tag (tag_id, transaction_id) values (?, ?)
2019-05-15 11:19:45.128  WARN 29144 --- [nio-5000-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: 23502
2019-05-15 11:19:45.134 ERROR 29144 --- [nio-5000-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : ERROR: null value in column "user_id" violates not-null constraint
  Detail: Failing row contains (null, null, 1000, null, null, null, null).

Если я удаляю вторую строку, я получаю:

org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.checking.register.model.TransactionTag.transaction -> com.checking.register.model.Transaction

1 Ответ

0 голосов
/ 15 мая 2019

Я думаю, что проблема в этой части кода

public void addTag(Tag tag) {
    TransactionTag postTag = new TransactionTag(this, tag);
    tags.add(postTag);
    tag.getTransactions().add(postTag);
}

Вы создаете TransactionTag объект и уже связали tag с postTag в TransactionTag конструкторе.

и позже в 3-й строке вы добавляете postTag к tag. Это может быть проблемой.

Попробуйте связать TransactionTag и Tag только в 1 месте.

...