Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » MySQLIntegrityConstraintViolationException when updating @ManyToMany @JoinTable @OrderColumn
MySQLIntegrityConstraintViolationException when updating @ManyToMany @JoinTable @OrderColumn [message #822311] Fri, 16 March 2012 09:05 Go to previous message
Karsten Wutzke is currently offline Karsten Wutzke
Messages: 110
Registered: July 2009
Senior Member
I have a many-to-many relationship between `Prequalification` and `Company` entities (`Partnership`). The DDL for the three tables is:
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!
);

Please note the unique constraint on `Partnerships`'s PQ ID and ordinal number: there can be only one position of a company per PQ.

This got mapped in `Prequalification` as `@ManyToMany` + `@JoinTable` including an `@OrderColumn` for the order of the companies per PQ (`Partnerships`'s order column is ordinal_nbr):
@Entity
@Table(name = "Prequalifications")
public class Prequalification implements Serializable
{
    ...

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

    ...
}

Here's the update method which is called from the GUI. Note, that the list on the GUI only includes external companies and that the internal company is (supposed to be) at index zero always:
@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...
    }

    ...
}

In the client JSF page the field `participatingCompanies` is used like:
<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>

Don't be intimidated RichFaces component. The list referencing `#{pqHome.participatingCompanies}` just contains optional (external) companies:

index.php/fa/7564/0/

When I hit the update button (not shown), the `update` method on the `PqHome` bean is called. When executing the code on GlassFish 3.1.2 using EclipseLink 2.3.2 the following exception is thrown:

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

This exception is duplicated several times in the log. Other PQ's have the same problem, just involve other unique column combinations, e.g. '1-1'.

What's wrong with the update code? It works without problems on Hibernate.

Thanks
Karsten

[Updated on: Fri, 16 March 2012 09:53]

Report message to a moderator

 
Read Message
Read Message
Read Message
Read Message
Previous Topic:OptimisticLocking selected columns gets last updated values
Next Topic:missing commit
Goto Forum:
  


Current Time: Mon May 20 11:57:01 EDT 2013

Powered by FUDForum. Page generated in 0.01638 seconds