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

Bug 344448

Summary: wrong mapping of java.util.Map<Entity, Entity>
Product: z_Archived Reporter: aurelia.hihn
Component: EclipselinkAssignee: Project Inbox <eclipselink.orm-inbox>
Status: NEW --- QA Contact:
Severity: normal    
Priority: P2 CC: karl.nicholas, thomas.lefort, tom.ware
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Whiteboard:

Description aurelia.hihn CLA 2011-05-02 09:16:58 EDT
Build Identifier: 2.0.0

In an entity called Entity1, I have following mapping :

	@ManyToMany
	@MapKeyJoinColumn(name="entity2_id")
	@JoinTable(joinColumns=@JoinColumn(name="entity1_id"), inverseJoinColumns=@JoinColumn(name="entity3_id"))
	private Map<Entity2, Entity3> entities;

In database, a table entity1_entity_3 is generated with 
    entity1_id integer NOT NULL,
    entity3_id integer NOT NULL,
    entity2_id integer,
  CONSTRAINT entity1_entity3_pkey PRIMARY KEY (entity1_id, entity3_id), ...

I was expecting : 
    entity1_id integer NOT NULL,
    entity3_id integer NOT NULL,
    entity2_id integer NOT NULL,
  CONSTRAINT entity1_entity2_entity3_pkey PRIMARY KEY (entity1_id, entity2_id, entity3_id)
or eventually,
  CONSTRAINT entity1_entity2_pkey PRIMARY KEY (entity1_id, entity2_id)

Because of the generated schema, we can't do this : 

    Map<Entity2, Entity3> map = new HashMap<Entity2, Entity3>();
    map.put(entity2A, entity3);
    map.put(entity2B, entity3);	
    entity1.setEntities(map);
    entityManager.persist(entity1);

It causes following error :
    ERROR: duplicate key value violates unique constraint "entity1_entity3_pkey"

The current generated schema is not coherent with standard use of a java.util.Map


  



Reproducible: Always
Comment 1 aurelia.hihn CLA 2011-05-02 09:48:32 EDT
same generated schema with eclipse link 2.2.0.v20110202-r8913
Comment 2 Tom Ware CLA 2011-05-12 15:43:42 EDT
The best workaround is to generate the schema using our sql-script type generation and change the generated schema manually and apply it to the database.
Comment 3 Tom Ware CLA 2011-05-20 08:37:08 EDT
Setting target and priority.  See the following page for the meanings of these fields:

http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines

Community: Please vote for this bug if it is important to you.  Votes are one of the main criteria we use to determine which bugs to fix next.
Comment 4 Karl Nicholas CLA 2016-02-21 10:52:48 EST
Getting this same bug as of Eclipse 2.6.2, in both @OneToMany and @ManyToMany mapping. Further you can't fix it by messing around with the @JoinTable and @MapKeyClass annotations. I know a workaround is to use a manually generated schema, but that's pretty ugly.
Comment 5 Thomas LEFORT CLA 2018-12-28 06:24:40 EST
I have this very same problem although changing the schemas won't fix the issue. This is what I implemented as a test case

        EntityManager em = EMF.get().createEntityManager();

        // update schema first
        em.getTransaction().begin();
        em.createNativeQuery("ALTER TABLE license_euladocument DROP CONSTRAINT license_euladocument_pkey;" +
                "ALTER TABLE license_euladocument ADD CONSTRAINT license_euladocument_pkey PRIMARY KEY (license_id, values_key);").executeUpdate();
        em.getTransaction().commit();

        {
            em.getTransaction().begin();
            License license = new License();
            license.setName("Test license");
            HashMap<CaseValue, EULADocument> caseValues = new HashMap<CaseValue, EULADocument>();
            CaseValue firstCaseValue = new CaseValue();
            firstCaseValue.setValue("Case 1");
            em.persist(firstCaseValue);
            CaseValue secondCaseValue = new CaseValue();
            secondCaseValue.setValue("Case 2");
            em.persist(secondCaseValue);
            CaseValue thirdCaseValue = new CaseValue();
            thirdCaseValue.setValue("Case 3");
            em.persist(thirdCaseValue);
            EULADocument firstEULADocument = new EULADocument();
            firstEULADocument.setName("EULA document 1");
            em.persist(firstEULADocument);
            EULADocument secondEULADocument = new EULADocument();
            secondEULADocument.setName("EULA document 2");
            em.persist(secondEULADocument);

            caseValues.put(firstCaseValue, firstEULADocument);
            caseValues.put(secondCaseValue, firstEULADocument);
            caseValues.put(thirdCaseValue, secondEULADocument);
            license.setValues(caseValues);

            em.persist(license);

            em.getTransaction().commit();
        }

        em.getEntityManagerFactory().getCache().evictAll();
        em.clear();

        {
            TypedQuery<License> licenseTypedQuery = em.createQuery("select l from License l", License.class);
            List<License> licenses = licenseTypedQuery.getResultList();

            assert licenses.size() == 1 && licenses.get(0).getValues().size() == 3;

            em.getTransaction().begin();
            License license = licenses.get(0);
            Map<CaseValue, EULADocument> values = license.getValues();
            // change map values set them all to the same document
            List<EULADocument> documents = new ArrayList<EULADocument>(values.values());
            for (CaseValue caseValue : values.keySet()) {
                values.put(caseValue, documents.get(0));
            }
            em.getTransaction().commit();
        }



I haven't added the code for the entities because they are very trivial. I can add them if required.

This code FAILS. I don't have the primary key constraint error anymore but this is the new error

[EL Fine]: sql: 2018-12-28 12:21:06.615--ClientSession(260580453)--Connection(1375503918)--DELETE FROM LICENSE_EULADOCUMENT WHERE ((values_ID = ?) AND (License_ID = ?))
	bind => [5, 6]
[EL Fine]: sql: 2018-12-28 12:21:06.615--ClientSession(260580453)--Connection(1375503918)--INSERT INTO LICENSE_EULADOCUMENT (values_ID, License_ID, values_KEY) VALUES (?, ?, ?)
	bind => [4, 6, 1]
[EL Fine]: sql: 2018-12-28 12:21:06.63--ClientSession(260580453)--SELECT 1
[EL Warning]: 2018-12-28 12:21:06.63--ClientSession(260580453)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.3.v20180807-4be1041): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "license_euladocument_pkey"
  Detail: Key (license_id, values_key)=(6, 1) already exists.

As you can see the issue is that the DELETE is made using the ((values_ID = ?) AND (License_ID = ?)) combination instead of the primary key I defined.

Is there a way to fix this? I am really stuck looking for  alternatives.

Thanks!
Comment 6 Eclipse Webmaster CLA 2022-06-09 10:03:51 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink