Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 320969

Summary: @ElementCollection on Map<@Entity, Primitive> causes DDL generation error (duplicate column names)
Product: z_Archived Reporter: Michael A <sdfvo87fhk324d98csadc>
Component: EclipselinkAssignee: Nobody - feel free to take it <nobody>
Status: NEW --- QA Contact:
Severity: major    
Priority: P3 CC: michael.f.obrien, tom.ware
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Windows XP   
Whiteboard: postgresql tableperclass

Description Michael A CLA 2010-07-26 20:06:45 EDT
Build Identifier: 2.1.0.v20100614-r7608 

##############################################################################################################
# Configuration output from EclipseLink
##############################################################################################################

[EL Info]: EclipseLink, version: Eclipse Persistence Services - 2.1.0.v20100614-r7608
[EL Fine]: Detected Vendor platform: org.eclipse.persistence.platform.database.PostgreSQLPlatform
[EL Config]: Connection(20003078)--connecting(DatabaseLogin(
	platform=>PostgreSQLPlatform
	user name=> "ibend"
	datasource URL=> "jdbc:postgresql://localhost/ibend"
))
[EL Config]: Connection(9883409)--Connected: jdbc:postgresql://localhost/ibend
	User: ibend
	Database: PostgreSQL  Version: 8.4.2
	Driver: PostgreSQL Native Driver  Version: PostgreSQL 8.4 JDBC4 (build 701)
[EL Config]: Connection(16022517)--connecting(DatabaseLogin(
	platform=>PostgreSQLPlatform
	user name=> "ibend"
	datasource URL=> "jdbc:postgresql://localhost/ibend"
))
[EL Config]: Connection(16139149)--Connected: jdbc:postgresql://localhost/ibend
	User: ibend
	Database: PostgreSQL  Version: 8.4.2
	Driver: PostgreSQL Native Driver  Version: PostgreSQL 8.4 JDBC4 (build 701))

##############################################################################################################
# Attribute.java
##############################################################################################################

package com.ibend.model.attribute;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;

import com.ibend.model.common.PersistentEntity;

@Entity
public class Attribute extends PersistentEntity {
	
	@Column(length = 50)
	@Basic(optional = false)
	private String name;
	@ManyToOne(optional = false)
	private AttributeCategory category;
	
	public String getName() {
		return this.name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public AttributeCategory getCategory() {
		return this.category;
	}
	
	public void setCategory(AttributeCategory category) {
		this.category = category;
	}
}

##############################################################################################################
# Product.java
##############################################################################################################

package com.ibend.model.product;

import java.util.Map;
import java.util.Set;

import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.MapKeyJoinColumn;

import com.ibend.model.attribute.Attribute;
import com.ibend.model.common.PersistentEntity;

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Product extends PersistentEntity {
	
	@Basic(optional = false)
	private String name;
	
	@JoinColumn(name = "primary_category_id")
	private Category primaryCategory;
	
	@JoinTable(joinColumns = @JoinColumn(name = "product_id"), inverseJoinColumns = @JoinColumn(name = "category_id"))
	private Set<Category> categories;
	
	@ElementCollection
	@MapKeyJoinColumn(name = "attribute_id", referencedColumnName = "id")
	@CollectionTable(name = "product_attribute")
	private Map<Attribute, String> attributes;
	
	public String getName() {
		return this.name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public Category getPrimaryCategory() {
		return this.primaryCategory;
	}
	
	public void setPrimaryCategory(Category primaryCategory) {
		this.primaryCategory = primaryCategory;
	}
	
	public Set<Category> getCategories() {
		return this.categories;
	}
	
	public void setCategories(Set<Category> categories) {
		this.categories = categories;
	}
	
	public Map<Attribute, String> getAttributes() {
		return this.attributes;
	}
	
	public void setAttributes(Map<Attribute, String> attributes) {
		this.attributes = attributes;
	}
}

##############################################################################################################
# Good.java
##############################################################################################################
package com.ibend.model.product;

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;

import com.ibend.model.attribute.Attribute;

@Entity
public class Good extends Product {
	
	@OneToMany
	@JoinTable(joinColumns = @JoinColumn(name = "good_id"), inverseJoinColumns = @JoinColumn(name = "identifier_id"))
	private List<GoodIdentifier> identifiers;
	
	@ManyToMany
	@JoinTable(joinColumns = @JoinColumn(name = "good_id"), inverseJoinColumns = @JoinColumn(name = "attribute_type_id"), name = "good_inventory_attribute")
	private List<Attribute> inventoryItemAttribute;
	
	public List<GoodIdentifier> getIdentifiers() {
		return this.identifiers;
	}
	
	public void setIdentifiers(List<GoodIdentifier> identifiers) {
		this.identifiers = identifiers;
	}
	
	public List<Attribute> getInventoryItemAttribute() {
		return this.inventoryItemAttribute;
	}
	
	public void setInventoryItemAttribute(List<Attribute> inventoryItemAttribute) {
		this.inventoryItemAttribute = inventoryItemAttribute;
	}
}

##############################################################################################################
# DDL Generation output
##############################################################################################################

... deleted ...

[EL Fine]: Connection(9883409)--ALTER TABLE product_attribute DROP CONSTRAINT FK_product_attribute_attribute_id
[EL Fine]: SELECT 1
[EL Warning]: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.1.0.v20100614-r7608): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: relation "product_attribute" does not exist
Error Code: 0
Call: ALTER TABLE product_attribute DROP CONSTRAINT FK_product_attribute_attribute_id
Query: DataModifyQuery(sql="ALTER TABLE product_attribute DROP CONSTRAINT FK_product_attribute_attribute_id")

.. deleted ...

[EL Fine]: Connection(9883409)--DROP TABLE product_attribute
[EL Fine]: SELECT 1
[EL Warning]: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.1.0.v20100614-r7608): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: table "product_attribute" does not exist
Error Code: 0
Call: DROP TABLE product_attribute
Query: DataModifyQuery(sql="DROP TABLE product_attribute")
[EL Fine]: Connection(9883409)--CREATE TABLE product_attribute (Good_ID BIGINT NOT NULL, ATTRIBUTES VARCHAR(255), attribute_id VARCHAR(255), Product_ID VARCHAR(255) NOT NULL, ATTRIBUTES VARCHAR(255))
[EL Fine]: SELECT 1
[EL Warning]: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.1.0.v20100614-r7608): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: column "attributes" specified more than once
Error Code: 0
Call: CREATE TABLE product_attribute (Good_ID BIGINT NOT NULL, ATTRIBUTES VARCHAR(255), attribute_id VARCHAR(255), Product_ID VARCHAR(255) NOT NULL, ATTRIBUTES VARCHAR(255))
Query: DataModifyQuery(sql="CREATE TABLE product_attribute (Good_ID BIGINT NOT NULL, ATTRIBUTES VARCHAR(255), attribute_id VARCHAR(255), Product_ID VARCHAR(255) NOT NULL, ATTRIBUTES VARCHAR(255))")

... deleted ...

##############################################################################################################
# Changes
##############################################################################################################

If I remove the two annotations from Product.java
	@MapKeyJoinColumn(name = "attribute_id", referencedColumnName = "id")
	@CollectionTable(name = "product_attribute

then I get the following DDL generation, which works but is not what I desire.

##############################################################################################################
# DDL with aforementioned changes
##############################################################################################################

CREATE TABLE product_attributes
(
  product_id character varying(255) NOT NULL,
  attributes character varying(255),
  attributes_key bigint,
  CONSTRAINT fk_product_attributes_attributes_key FOREIGN KEY (attributes_key)
      REFERENCES attribute (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE
);
ALTER TABLE product_attributes OWNER TO ibend;


CREATE TABLE good_attributes
(
  good_id bigint NOT NULL,
  attributes character varying(255),
  attributes_key bigint,
  CONSTRAINT fk_good_attributes_attributes_key FOREIGN KEY (attributes_key)
      REFERENCES attribute (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE
);
ALTER TABLE good_attributes OWNER TO ibend;

Reproducible: Always

Steps to Reproduce:
1. Create data model as shown (external classes to problem omitted)
2. Start with empty database (no tables, sequences etc)
3. Using Eclipse Helios. run JPA DDL generation (I assume it is the same regardless of means of invocation of the DDL generator)
Comment 1 Michael A CLA 2010-07-26 21:40:27 EDT
It would seem this is related to the fact that Product is an abstract entity with table inheritance style TAPLE_PER_CLASS. The extra fields seem to be coming from the fact that it is trying to generate product_attribute for all (known) subclasses of Product, yet these tables never join.

good_attribute should be created but since I am specifying the collection table name, it's trying to mash all of the fields into the same table. A work around for this I would suggest is to detect the presence of the table inheritance structure and override the @CollectionTable annotation (or throw an error). Another workaround would be to allow the overriding of the @CollectionTable per subclass. If I do no override, I get table names that I do not like.
Comment 2 Tom Ware CLA 2010-08-09 13:59:38 EDT
Setting target and priority.  See the following page for details of the meanings of these fields:

http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines
Comment 3 Eclipse Webmaster CLA 2022-06-09 10:24:15 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink