Search code examples
javaoraclejpaforeign-keyseclipselink

How does EclipseLink name foreign keys?


I am using EclipseLink 2.6.2 and Oracle with my Java project. I want to understand how EclipseLink decides to name the foreign keys.

Currently when I start up my app I get an error that the constraint name already exists. I can query the database and see what the foreign keys are that were created, and I see the one that has the same name, but it's not clear to me how it's creating the name. It's obviously stripping out any vowels, but is it using table one with table two, or the column names?

Here is the error:

Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.2.v20151217-774c696): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: ORA-02264: name already used by an existing constraint
Error Code: 2264
Call: ALTER TABLE publctncompntdef_hist_ref ADD CONSTRAINT pblctncmpntdfbdpckCmpnntDfntnd FOREIGN KEY (bidpackComponentDefinitionId) REFERENCES bidpckcompntdef (bidpackComponentDefinitionId)

The name of the foreign key it's trying to create is: pblctncmpntdfbdpckCmpnntDfntnd

It's trying to create a foreign key for table publctncompntdef_hist_ref to table with column bidpackComponentDefinitionId referencing table bidpckcompntdef and column bidpackComponentDefinitionId

But it has already created a foreign key with the same name for table publctncompntdefref with column bidpackComponentDefinitionId to table bidpckcompntdef and column bidpackComponentDefinitionId

I've googled and whatnot but can't seem to find documentation about the naming strategy for foreign keys.

Also, here are the @JoinTable values:

@JoinTable(name = "publctncompntdef_hist_ref",
           joinColumns={@JoinColumn(name="publicationHistoryId",referencedColumnName="id")},
           inverseJoinColumns={@JoinColumn(name="bidpackComponentDefinitionId",referencedColumnName="bidpackComponentDefinitionId")})

@JoinTable(name = "publctncompntdefref",
        joinColumns={@JoinColumn(name="bidpackComponentDefinitionId",referencedColumnName="bidpackComponentDefinitionId")},
        inverseJoinColumns={@JoinColumn(name="publicationId",referencedColumnName="id")})

Solution

  • I think I figured it out. I didn't initially realize that EclipseLink is open source, but I finally found that out and downloaded the code from: https://github.com/eclipse/eclipselink.runtime

    Basically what it seems to do is

    1. Drop the FK_ prefix if FK_ + table name + _ + column name is greater than the limit (30 characters for Oracle).
    2. If it's still too long, remove any underscores or anything else that's not a letter or digit.
    3. If it's still too long, remove the vowels
    4. If it's still too long:
      4a. Check if the column name without vowels is greater than the limit (30chars). If so, make the foreign key name just the column name minus the vowels, truncated from the end to be the max size
      4b. Else, truncate the table name to however many characters are left over (max limit - column name length)

    Here's what was happening in my situation:
    Table Name = publctncompntdefref (19chars) and column name = bidpackcomponentdefinitionid (28chars)
    Minus the vowels = pblctncmpntdfrf (15chars) and bdpckcmpnntdfntnd (17chars)
    Concatenating these two gives us 32 characters. Since the column name without the vowels is less than 30 chars (max for oracle), truncate the table name to (30-17 = 13 characters), which is pblctncmpntdf. The whole foreign key name is pblctncmpntdfbdpckcmpnntdfntnd.

    Similarly, when I had table name = publctncompntdef_hist_ref and column name = bidpackcomponentdefinitionid, without the vowels I ended up with pblctncmpntdfhstrf and bdpckcmpnntdfntnd. The table name being 18 characters and the column name is 17 characters. Therefore the table needs to be trucated to be 13 characters, so pblctncmpntdfhstrf becomes pblctncmpntdf, and the whole foreign key becomes pblctncmpntdfbdpckcmpnntdfntnd.

    This is explains why EclipseLink is telling me there is already a foreign key constraint with that name.