简体   繁体   English

如何在JPA中正确地保持多对多关系

[英]How to properly persist many-to-many relationship in JPA

I have two simple tables file and row that related as many-to-many. 我有两个简单的表filerow ,它们与多对多相关。 One file can containts several rows and one row can be in several files. 一个文件可以包含多行,一行可以包含多个文件。

CREATE TABLE file
(
  id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
  name VARCHAR(30) DEFAULT '' NOT NULL
);
CREATE UNIQUE INDEX file_id_uindex ON file (id);

CREATE TABLE row
(
    id BINARY(16) PRIMARY KEY NOT NULL,
    content TEXT
);
CREATE UNIQUE INDEX row_id_uindex ON row (id);

CREATE TABLE file_row
(
    file_id INT(11),
    row_id BINARY(16),
    CONSTRAINT file_id___fk FOREIGN KEY (file_id) REFERENCES file (id),
    CONSTRAINT row_id___fk FOREIGN KEY (row_id) REFERENCES row (id)
);
CREATE INDEX file_id___fk ON file_row (file_id);
CREATE INDEX row_id___fk ON file_row (row_id);

As row.id I use hash of row content, so in table can be stored only unique rows. 由于row.id我使用行内容的哈希,因此在表中只能存储唯一的行。

And my mapped classes in java: 我在java中的映射类:

@Getter
@Setter
@Entity
@Table(name = "file")
public class File implements Serializable {

    @Builder
    public File(int id, String name, List<Row> rows) {
        this.id = id;
        this.name =name;
        this.rows = rows;
    }

    public File(){
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

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

    @ManyToMany(cascade = CascadeType.MERGE)
    @JoinTable(name = "file_row",
            joinColumns = @JoinColumn(name = "file_id"),
            inverseJoinColumns = @JoinColumn(name = "row_id"))
    private List<Row> rows;
}

@Getter
@Setter
@Entity
@Table(name = "row")
public class Row implements Serializable{

    public Row() {
    }

    @Builder
    public Row(byte[] id, String content, List<File> files){
        this.id = id;
        this.content = content;
        this.files = files;
    }

    @Id
    @Column(columnDefinition = "BINARY(16)")
    private byte[] id;

    @Lob
    @Column(name = "content")
    private String content;

    @ManyToMany(mappedBy = "rows", cascade = CascadeType.MERGE)
    private List<File> files;
}

To save entity I use persist() method of EntityManager class. 要保存实体,我使用EntityManager类的persist()方法。

When I add new file with unique rows - all fine. 当我添加具有唯一行的新文件时 - 一切都很好。 But if I try to persist file with duplicate rows error occurs: 但是,如果我尝试使用重复行保持文件错误发生:

Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.restapp.entity.Row#[B@183c1f56]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1664)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1152)
    at org.jboss.as.jpa.container.AbstractEntityManager.persist(AbstractEntityManager.java:580)
    at com.restapp.dao.AbstractDao.persist(AbstractDao.java:43)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.jboss.as.ee.component.ManagedReferenceMethodInterceptor.processInvocation(ManagedReferenceMethodInterceptor.java:52)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:437)
    at org.jboss.as.weld.ejb.Jsr299BindingsInterceptor.doMethodInterception(Jsr299BindingsInterceptor.java:82)
    at org.jboss.as.weld.ejb.Jsr299BindingsInterceptor.processInvocation(Jsr299BindingsInterceptor.java:93)
    at org.jboss.as.ee.component.interceptors.UserInterceptorFactory$1.processInvocation(UserInterceptorFactory.java:63)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.invocationmetrics.ExecutionTimeInterceptor.processInvocation(ExecutionTimeInterceptor.java:43)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.jpa.interceptor.SBInvocationInterceptor.processInvocation(SBInvocationInterceptor.java:47)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:437)
    at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:64)
    at org.jboss.as.weld.ejb.EjbRequestScopeActivationInterceptor.processInvocation(EjbRequestScopeActivationInterceptor.java:83)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InitialInterceptor.processInvocation(InitialInterceptor.java:21)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(ComponentDispatcherInterceptor.java:52)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.pool.PooledInstanceInterceptor.processInvocation(PooledInstanceInterceptor.java:51)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:254)
    ... 178 more

And also error is occurs when I persist file with the rows that are already in the table. 当我使用表中已有的行保留文件时,也会发生错误。

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '\xE6t\xDB]\xFA\xF0\xAFz\xD4\xED\xAE\xFC\x84\xD7\x99\xA9' for key 'PRIMARY'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
    at com.mysql.jdbc.Util.getInstance(Util.java:408)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:935)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3970)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3906)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2524)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2677)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2549)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861)
    at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2073)
    at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2009)
    at com.mysql.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:5098)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1994)
    at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:537)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
    ... 129 more

Please, help me to solve my issue. 请帮我解决我的问题。

So, it seems I found solution. 所以,我似乎找到了解决方案。 That's what I did: 这就是我做的:

  1. Use merge() method of EntityManager class instead of persist() to add new file ( link ). 使用EntityManager类的merge()方法而不是persist()来添加新文件( 链接 )。
  2. Add <property name="hibernate.event.merge.entity_copy_observer" value="allow"/> to persistence.xml properties ( link ). <property name="hibernate.event.merge.entity_copy_observer" value="allow"/>persistence.xml属性( 链接 )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM