Community
Participate
Working Groups
Build ID: N/A Steps To Reproduce: Given the following method: @ManyToMany @JoinTable(name = "playlist_to_clip", joinColumns = @JoinColumn(name = "playlist_id", nullable = false), inverseJoinColumns = @JoinColumn(name = "clip_id", nullable = false)) public List<Clip> getClips(); EclipseLink will generate a join-table with 2 columns (playlist_id, clip_id) with a primary key defined over those two. This makes it impossible to store duplicate clips per playlist which is clearly implied by the use of List instead of Set. EclipseLink should allow duplicates if the method returns any collection except a Set. Specifically, Collection and List should definitely allow duplicates. More information:
It looks like the only thing DDL is doing wrong is generating a primary key. Omitting it fixes the problem for me.
Changed severity to critical because even once I remove the primary key by hand I run into the following bug: 1) A playlist has a unique "name" and a list of clips 2) Save a new playlist with 3 clips to the database 3) Look up that same playlist by name 4) The database clearly contains 3 clips associated with the playlist, but EclipseLink returns *one* clip from the 2nd-level cache 5) Repeating this query keeps on returning one clip 6) Restart EclipseLink (thereby flushing the 2nd-level cache) 7) Reissue the query in #4 8) 3 clips are returned (correct value) I believe I'm stuck at this point. I don't think I can implement my application until this bug is fixed.
I'm going to attach the EclipseLink log with logging level "finest". Here are specific lines to look at... Line 611: Notice how we insert PlaylistVersion[id=1301, version=39] with *two* video elements. Line 620 and 623: Notice how we insert [video_id=2, playlist_id=1301] *twice* into the "playlist_to_video" table. This reinforces the fact that there are *two* videos associated with PlaylistVersion #1301. Line 642: Notice how PlaylistVersion[id=1301, version=39] suddenly contains *one* video element.
Created attachment 119084 [details] EclipseLink log
See http://www.nabble.com/%40ManyToMany-relationship-for-a-List--td20749567.html for a related discussion.
Discussion of workaround from Nabble article above: One workaround I can think of is to add another level of indirection. i.e. PlayList has a OneToMany to PlayListItem PlayListItem has a OneToOne to Video PlayListItem has its own identity. You could even implement the mthods in PlayList to make PlayListItem pretty much transparent to whoever was using your model. ---- I don't understand how "PlayListItem has a OneToOne to Video" would work. Each video can be associated with multiple playlists. As a consequence, each PlaylistItem would need to be associated with multiple videos as well. Shouldn't I do this instead? Playlist has a OneToMany to PlaylistItem PlaylistItem has a OneToMany to Video PlaylistItem has its own identity This would give you: Playlist[id] Video[id] PlaylistItem[id, playlist_id, video_id] What do you think? ---- Depending on how you want things to work, it is certainly possible to do it the way you have suggested. Let me explain a bit further my suggestion, and then you can decide which fits your model better. - PlayList has a OneToMany to PlayListItem - PlayListItem has a OneToOne to Video - PlayListItem has its own identity. Consider the following objects: - PlayList: PL1 - Video: V1 - Video: V2 Lets say we want to add V1 to PL1: 1. pl1.add(v1) - on this call, we create an instance of PlayListItem pli1 and associate it with v1: - pli1 = new PlayListItem() - pli1.video = v1 - we then associate pli1 to pl1. - pl1.addPlayListItem(pli1) 2. p11.add(v2) - we do basically the same thing as above: - pli2 = new PlayListItem() - pli2.video = v2 - pl1.addPlayListItem(pli2) 3. pl1.add(v1) - add a duplicate item - pli3 = new PlayListItem() // this will have a different PK than pli1 - pli3.video = v1 - pl1.addPlayListItem(pli3) - The key here is keys that form the list are the foreign keys on PlayList and PlayListItem, so you do not get conflicts where there are duplicate items. PlayListItem has separate foreign keys that associate it with Video. When we are done: pl1 has a list of 3 PlayListItems (pli1, pli2, pli3) pli1 has a 1-1 relationship with v1 pli2 has a 1-1 relationship with v2 pli3 has a 1-1 relationship with v1 With this set of mappings, pl1 hold 2 pointers to v1 ---- I think I see it now. I guess I was confused by: class PlaylistItem { @OneToOne public Video getVideo(); } I thought @OneToOne meant that there may only be one PlaylistItem for every Video but (correct me if I'm wrong) what @OneToOne actually means is that there that every PlaylistItem has exactly one Video associated with it. **The relationship says nothing about the association from the remote end** Is that correct? PS: What's the difference on a table-level between @ManyToOne and @OneToOne? Don't they both result in the same schema in this case? I mentioned @ManyToOne would result in: Playlist[id] Video[id] PlaylistItem[id, playlist_id, video_id] Wouldn't @OneToOne result in the same? ---- You are correct, the OneToOne relationship from PlayListItem to Video does not restrict how many PlayListItems can refer to the same Video. ManyToOne is used in JPA to be explicit about the fact that a relationship is on the Many-side of a OneToMany relationship. In EclipseLink, both OneToOne and ManyToOne are implemented with the same class and therefore can be used to end up with the same schema. ---- 'm going to try your approach but I don't think you can hide the details under the hood. If I want to add a new Playlist->Video relationship I'm going to need to either expose PlaylistItem to the end-user (to populate and save) or expose a save(Playlist, Video) method in the service layer. Either approach is ugly. Originally I simply used Playlist.getVideos().add() or remove() and the changes got persisted under the hood. I wish I could have a similarly clean approach (without breaking my layers). ---- Nevermind, I got it working. I now expose "List<Video> VideoManager.getVideos(Playlist)" and the list implementation handles PlaylistItem under the hood.
I agree that replacing the M:M with a 1:M->1:1 is the way to go to allow multiple relationships to the same object in the same collection. This also has the effect of uniquely identifying each relationship in the join table as well. Without this it would not be possible to remove one of the items from the list without removing all of them. I am unsure what we do in EclipseLink to handle this. Maybe a how-to illustrating the pattern is the best solution.
Doug, Why not handle this seamlessly under the hood in EclipseLink? For example, users could specify a @ManyToMany with @JoinTable and EclipseLink would implement it as a M:1 - 1:1 under the hood.
What does the JPA standard say about this? My own interpretation is that the standard goes out of its way to talk about persisting Lists (versus Sets). Clearly they expect implementations to be able to persist Lists. I fail to see how one can say you support persisting Lists *without* supporting duplicates, since duplicates are one of the main reasons for using Lists in the first place. In short: persisting Lists, whether they contain duplicates or not, should "just work".
In order to persist duplicates in a list there must be a unique way to identify each entry. In the case of ManyToMany the join table is typically a combination of the two primary key sets of source and target. To support duplicates an additional column would be required in the join and the state of this maintained. the only way to do this transparently would be to use the index in the list as the additional value to uniquely identify each entry and support full CRUD operations on each relation. We should ensure this case is supported when we add JPA 2.0's support for maintaining ordering. http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/ordered_lists
Updating priority due to revised bug categorization process. See the following page for details: http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines#Priority_and_Target_Milestone If you feel the updated priority is incorrect, please send an email to eclipselink-users@eclipse.org.
*** Bug 395438 has been marked as a duplicate of this bug. ***
This bug is still a big issue. Is there no generic solution to this? Lists with duplicate entries are "possible" regarding JPA, isn't it?
Johannes, the Wiki says (https://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/ordered_lists) that already JPA 2.0 "should enable the storage of duplicates in ManyToMany lists" and comment authors like Doug too. In fact, EclipseLink still does not persist lists with duplicate entities, which is catastrophic is my case. However, some people say, that JPA does not really require duplicates to be persisted. So this is clearly an issue that should have been clarified in JPA 2.1 (but isn't) or 2.2. There should also be a test case in the TCK. I agree with you, there should be a documented workaround but currently, it seems, they think that this problem has P5 (lowest priority). Would be interesting to know, whether other providers support duplicates, but changing the persistence provider for an application server isn't always easy/possible. This is an extremly time consuming problem. At the end, we'll have to manage our relation manually, as we did not achieve anything for 8 years. I would absolutely appreciate just to understand why the priority is so low.
@Marc: The question was rhetorical, but I agree with you this is a major issue and not a low priority one. To answer your question: Yes, Hibernate is able to store duplicate entries to a list.
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink