【问题标题】:MySQLIntegrityConstraintViolationException when updating list of entities mapped as @ManyToMany, @JoinTable and @OrderColumn (with unique constraint)更新映射为 @ManyToMany、@JoinTable 和 @OrderColumn 的实体列表时出现 MySQLIntegrityConstraintViolationException(具有唯一约束)
【发布时间】:2012-03-16 12:54:27
【问题描述】:

PrequalificationCompany 实体 (Partnership) 之间存在多对多关系。这三个表的 DDL 是:

CREATE TABLE Prequalifications
(
  id              INTEGER      NOT NULL IDENTITY,
  user_id         INTEGER      NOT NULL,
  name            VARCHAR(100) NOT NULL,

  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES Users (id)
);

CREATE TABLE Partnerships
(
  prequalification_id INTEGER            NOT NULL,
  company_id          INTEGER            NOT NULL,
  ordinal_nbr         SMALLINT DEFAULT 0 NOT NULL,

  PRIMARY KEY (prequalification_id, company_id),
  FOREIGN KEY (prequalification_id) REFERENCES Prequalifications (id),
  FOREIGN KEY (company_id) REFERENCES Companies (id),
  UNIQUE (prequalification_id, ordinal_nbr)
);

CREATE TABLE Companies
(
  id       INTEGER     NOT NULL,
  dnd_type VARCHAR(10) NOT NULL,

  PRIMARY KEY (id),
  FOREIGN KEY (id) REFERENCES Organizations (id) -- just an inherited ID, never mind!
);

请注意Partnerships 的 PQ ID 和序号的唯一约束:每个 PQ 只能有一个公司职位。

这在Prequalification 中映射为@ManyToMany + @JoinTable,包括@OrderColumn 用于每个PQ 的公司顺序(Partnerships 的顺序列是 ordinal_nbr):

@Entity
@Table(name = "Prequalifications")
public class Prequalification implements Serializable
{
    ...

    @ManyToMany
    @JoinTable(name = "Partnerships", joinColumns = @JoinColumn(name = "prequalification_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "company_id", referencedColumnName = "id"))
    @OrderColumn(name = "ordinal_nbr", nullable = false)
    private List<Company> companies;

    ...
}

这是从 GUI 调用的更新方法。请注意,GUI 上的列表仅包括外部公司,并且内部公司(假定)始终处于索引零:

@ManagedBean
@ViewScoped
public class PqHome implements DropListener, Serializable
{
    ...

    private Prequalification pq;
    private Integer userId;
    private List<Company> participatingCompanies; // xetters omitted!

    ...

    public void update()
    {
        // assign new owner
        pq.setUser(userService.findSingleUser(userId));

        Company internal = companyManager.getInternalCompany();

        // find internal company
        int index = participatingCompanies.indexOf(internal);

        // if internal company missing or at non-zero index: we need to repair this
        if ( index != 0 )
        {
            // if internal exists at some wrong place, remove it
            if ( index > 0 )
            {
                participatingCompanies.remove(index);
            }

            // (re-)add at index zero
            participatingCompanies.add(0, internal);
        }       

        pq.setCompanies(participatingCompanies);

        // update and get *new* merged instance
        pq = pqService.update(pq);

        participatingCompanies = null;
        init(); // some not so important (re-)initialization...
    }

    ...
}

在客户端 JSF 页面中,participatingCompanies 字段的用法如下:

<rich:pickList value="#{pqHome.participatingCompanies}"
               var="company"
               converter="#{companyConverter}"
               orderable="true"
               sourceCaption="Available companies"
               targetCaption="Selected companies">
  <f:selectItems value="#{companyManager.externalCompanies}" />
  <rich:column>#{company.name}</rich:column>
</rich:pickList>

不要被 RichFaces 组件吓倒。引用 #{pqHome.participatingCompanies} 的列表仅包含可选(外部)公司。

当我点击更新按钮(未显示)时,PqHome bean 上的 update 方法被调用。在 GlassFish 3.1.2 上使用 EclipseLink 2.3.2 执行代码时,会引发以下异常:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-2' for key 'partnerships_multi_uq'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.Util.getInstance(Util.java:386)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1039)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3609)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3541)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2002)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2163)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2624)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2127)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2427)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2345)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2330)
    at com.mysql.jdbc.jdbc2.optional.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:875)
    at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:831)
    ... 128 more

此异常在日志中多次重复。其他 PQ 也有同样的问题,只是涉及其他独特的列组合,例如'1-1'。服务器日志可以在这里找到:http://www.eclipse.org/forums/index.php/t/310923/

更新代码有什么问题?它在 Hibernate 上运行没有问题。

【问题讨论】:

    标签: java mysql jpa eclipselink unique-constraint


    【解决方案1】:

    EclipseLink 更新连接表中的@OrderColumn 以有效地维护顺序,因此您不能对其有唯一约束。因此,删除或推迟唯一约束。

    您可以在 EclipseLink 上记录一个错误以添加一个选项以避免此限制。

    【讨论】:

    猜你喜欢
    • 2016-11-21
    • 2022-01-20
    • 2011-04-30
    • 1970-01-01
    • 1970-01-01
    • 2017-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多