Search code examples
mysqljpaglassfisheclipselinkjpa-2.0

ElementCollection of Map<String,String> collection table not created with Map key as part of table key


EDIT:Issue resolved, cause unknown.

The following entity mapping, which contains a Map<String,String>:

public class Employee {
    @TableGenerator(name="Address_Gen",
            table="ID_GEN",
            pkColumnName="GEN_NAME",
            valueColumnName="GEN_VAL",
            pkColumnValue="Addr_Gen",
            initialValue=10000,
            allocationSize=100)
    @Id @GeneratedValue(generator="Address_Gen")    
    private int id;
    private String name;
    private long salary;

    @ElementCollection
    @CollectionTable(name="EMP_PHONE")
    @MapKeyColumn(name="PHONE_TYPE")
    @Column(name="PHONE_NUM")
    private Map<String, String> phoneNumbers;   
}

results in a collection table that has a key (not even described as a primary key in the generated DDL) that does not include the map key as part of the key:

CREATE TABLE `emp_phone` (
  `Employee_ID` int(11) DEFAULT NULL,
  `PHONE_NUM` varchar(255) DEFAULT NULL,
  `PHONE_TYPE` varchar(255) DEFAULT NULL,
  KEY `FK_EMP_PHONE_Employee_ID` (`Employee_ID`),
  CONSTRAINT `FK_EMP_PHONE_Employee_ID` FOREIGN KEY (`Employee_ID`) REFERENCES `employee` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Is this wrong? The book that I'm studying from (ProJPA2) suggests so:

Unique tuples in the collection table must be the combination of the key column and the foreign key column that references the source entity instance...[In] the resulting collection table, along with the source EMPLOYEE entity table that it references you can see the primary key constraint on the EMPLOYEE_ID and PHONE_TYPE columns.

I'm using eclipse photon, eclipselink 2.5.x, mysql 8.0

Thanks in advance.

EDIT

I've since created a new project using (almost) the same annotations as before but it's now generating the expected DDL:

public class Employee {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private int id;
    private String name;
    private double salary;
    @ManyToOne  
    private Department department;

    @ElementCollection
    @CollectionTable(name="EMP_PHONE")
    @MapKeyColumn(name="PHONE_TYPE")
    @Column(name="PHONE_NUM")
    private Map<String, String> phoneNumbers = new HashMap<>();

DDL:

CREATE TABLE `emp_phone` (
  `Employee_id` int(11) NOT NULL,
  `PHONE_NUM` varchar(255) DEFAULT NULL,
  `PHONE_TYPE` varchar(255) NOT NULL,
  PRIMARY KEY (`Employee_id`,`PHONE_TYPE`),
  CONSTRAINT `FKq4updxf2ebi3swv5tf3n3jp5h` FOREIGN KEY (`Employee_id`) REFERENCES `employee` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Sadly I don't have time to forensically go over the two projects to see exactly what it is that made the difference.


Solution

  • It appears that in this case, EclipseLink is using a more generic approach to the mapping by just adding the map key column to the table it would generate for a List or Set collection type.

    You can file an enhancement request for this but it isn't a bug: JPA doesn't specify exactly what the table generated will look like, only the columns used for the fields. The assumption is that DDL generation is an aid to development prototyping and you would optimize your database on your use case - by adding indexes etc as appropriate, and not all databases work the same anyway.