【问题标题】:How do you save a many-to-many relationship with spring-data-jpa and composite keys如何使用 spring-data-jpa 和复合键保存多对多关系
【发布时间】:2019-10-01 16:51:25
【问题描述】:

我尝试使用 spring-data-jpa 和休眠来保存多对多关系。这两个类都有复合键。我正在使用列出两个类的连接类。我收到一个错误,暗示该对象已经存在。我不知道我在这里做错了什么。这是我的代码:

交易

@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

交易标签

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

编辑

有人建议我删除Transaction中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

【问题讨论】:

    标签: hibernate spring-data-jpa composite-key


    【解决方案1】:

    我认为问题出在这部分代码

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

    您正在创建 TransactionTag 对象,并且您已经将 tag 链接到 TransactionTag 构造函数中的 postTag。

    稍后在第 3 行中,您将添加 postTag 到 tag。这可能是问题所在。

    尝试仅在 1 个位置链接 TransactionTag 和 Tag。

    【讨论】:

    • 请查看我对我尝试您的建议的问题的补充。
    • 创建 TransactionTag 不会将其添加到事务中。我们需要明确地将其添加到其中一个,也可能同时添加到两者。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多