JoinColumn with unique keys as combination of joinColumns and inverseJoinColumns - spring-data-jpa

#JoinTable(name="policy_attributes",
joinColumns = #JoinColumn(name="policy_id"),
inverseJoinColumns = #JoinColumn(name="attribute_id"))
private Set<Attribute> attributeSet;
Problem with above code is 'It fails if we have duplicate key for attribute id. I want combination of policy_id and attribute_id to identify my uniqueness(Two policies can refer to same attributes).
Can you please help me with this.

Related

JHipster 4.14.4 : Registering a user with additional information

Following the steps described here I get an org.hibernate.cfg.RecoverableException: Unable to find column with logical name: id in org.hibernate.mapping.Table(user_extra) and its related supertables and secondary tables. It doesn't matter, if I change the entries in the liquibase changelogs regarding "user_extra.user_id". Even if I erase the liquibase changelogs (without the initial_schema) and/or remove the h2-database ( with ./gradlew clean) - every time I get the error.
The link mentioned above is close to the tipp on the jhipster.tech documentation, so I think I am missing something, but I am searching since hours for my failure(s)...
How does jhipster know to map the jhi_user.id to user_extra.user_id (OneToOne) if there is no explicit declaration for this in UserExtra.json? Is the declaration in the domain object UserExtra.java with
#OneToOne
#MapsId
private User user;
enough to trigger it?
Can somebody give me a hint, where Spring Data JPA maps the user_id from the user_extra-table to the #Id Long id of the domain object UserExtra?
My code generated with JHipster 4.14.4 is on github.
Please clarify my questions above, if you like!
Nevertheless I have found my mistakes. Unfortunately, there are no additional ManyToMany-relationships to UserExtra mentioned in the jhipster.tech-tipp - I had to change the user_extra.id to user_extra.user_id not only in the liquibase-changelogs, but also in the domain classes, which are linked ManyToMany to Spring MVC domain class UserExtra!
I faced the same issue, and Based on Jochen Gebsattel's answer, this is what I did:
Update the 202xxxxx_added_entity_constraints_UserExtra.xml
where I had
referencedColumnNames="id"
referencedTableName="user_extra"
I replaced by
referencedColumnNames="user_id"
referencedTableName="user_extra"
user_id being the name of the column containing the foreign key, in my user_extra entity/table
Update the UserExtra class (here again, replace id by user_id)
Where I had many to many relations with other entities, for example
#JoinTable(name = "user_extra_store",
joinColumns = #JoinColumn(name = "user_extra_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "store_id", referencedColumnName = "id"))
I replaced with
#JoinTable(name = "user_extra_store",
joinColumns = #JoinColumn(name = "user_extra_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "store_id", referencedColumnName = "id"))
Note: If you created a new liquibase changelog file in step 1 (instead of update), don't forget to reference it in the master.xml

JPA one-to-one bidirectional mapping on same object using association table

I have a table Person and now I want to express a relation like "best friend". Assuming a person can only have one best friend I don't want to alter the Person table to add a best friend column, rather I want to have an additional mapping table, e.g.:
Table Person (id name):
1 foo
2 bar
3 somebody
4 somebodyelse
Table BestFriendMapping (personId bestfriendId):
1 2
3 4
I was doing something like this:
class Person {
#OneToOne()
#Fetch(FetchMode.SELECT)
#JoinTable(name = "BestFriendMapping",
joinColumns = #JoinColumn(name = "personId", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "bestfriendId",
referencedColumnName = "id"))
private Person bestFriend;
}
The problem is, that now when I add a new Person, the mapping table is populated with two entries, for example the newly added Person is having id=10 and his bestFriend 20, then the entries are:
10 20
20 10
I would like to have just one entry, but still be able to get the best friend of a person no matter which I have in my hand currently. I found out that I probably have done two unidirectional instead of one bi-directional mapping, so I have to use mappedBy, but I am not sure what is the syntax when it is about the one and the same entity object, thus one and the same field inside the object. The examples on the internet are always showing the mapping of two entities via a mapping table.
Or maybe something like this?!? In addition to the JoinColumns and InverseJoinColumns to add mappedBy to the OnetoOne just like this #OneToOne(mappedBy="bestFriend"), kind of weird :)

How to use mappedBy with more than one value JPA

I have 2 tables:
Currency and Rate
The currency's primary key is referenced by the inputCurrency and outputCurrency foreigh keys in the Rate.
How can I map these in order to cascadeALL?
I tried in Rate (the table that contains the foreigh keys)
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "INPUT_CURRENCY")
private Currency inputCurrency;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "OUTPUT_CURRENCY")
private Currency ouputCurrency;
But if I delete an item from currency It results a primary key violation.I can't remove a Currency becouse it is referenced by inputCurrency or outputCurrency in Rate.
If I want to annotate the list of rates in Currency, how can I say mappedBy = "inputCurrency" and "outputCurrency" ?
#OneToMany(cascade = CascadeType.ALL,mappedBy ="inputCurrency,outputCurrency")
List<Rate> rates;
The solution is using two list of rates in Currency: one for the fk inputCurrency
and another for the fk outputCurrency
#OneToMany(cascade = CascadeType.ALL,mappedBy ="inputCurrency")
List<Rate> ratesIC;
#OneToMany(cascade = CascadeType.ALL,mappedBy ="outputCurrency")
List<Rate> ratesOC;
You can settle with two collections:
List<Rate> inputRate;
List<Rate> outputRate;
Each with a corresponding mappedBy
and a third #Transient List<Rate> rates;
You can use a #PostLoad event to unite the two persistent lists into the transient one.
And similarly, when adding/removing elements to one of the persistent lists, make sure to add/remove from the joint list.

Many To Many JPA Annotations more than two tables

Currently I have three tables.
User, Role and Institution.
As you know user and roles has many to many relation. (One user can have multiple roles, one role can have multiple users).
Create a many to many annotations as follows.
#In User Table.
#ManyToMany
#JoinTable(name = "USER_ROLES",
joinColumns = {#JoinColumn(name = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID")}
)
private Set<Role> roles = new HashSet<Role>();
#In Role Table.
#ManyToMany
#JoinTable(name = "USER_ROLES",
joinColumns = {#JoinColumn(name = "ROLE__ID")},
inverseJoinColumns = {#JoinColumn(name = "USER_ID")}
)
private Set<User> users = new HashSet<User>();
But later I have realized that a user can have a role in one institution and a different role in another Institution or the same in different institutions. How can I set that relation?
First of all, your simple many to many mapping is wrong. One side of the bidirectional association must be the inverse side, using the mappedBy attribute. For example:
#ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<User>();
Now to answer your question, you just need one more entity. Let's call it Participation. A participation contains a role of a given user in a given institution. So, you have the following locical associations:
user - OneToMany - participation
participation - ManyToOne - role
participation - ManyToOne - institution

Java EE 6 JPA 2 ManyToOne Relation Creates Invalid Foreign Key

I am trying to create two entities where both entities have embeddedIds. One of the entities have 2 references to the other entity, where both of those references are related as ManyToOne.
Example codes are written below;
#Embeddable
public class ItemPK {
#Column(nullable = false, length = 100)
private String itemId;
#Column(name = "item_client_id", nullable = false)
private int clientId;
...
}
#Entity
#Table(name = "item")
public class Item {
#EmbeddedId
private ItemPK id;
#ManyToOne
#JoinColumn(name = "item_client_id")
private Client client;
#OneToMany(mappedBy="item", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<RelatedItem> relatedItems;
#OneToMany(mappedBy="relatedItem", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<RelatedItem> relatedItemsRHS;
...
}
#Embeddable
public class RelatedItemPK {
#Column(name = "itemId", length = 100, nullable = false)
private String itemId;
#Column(name = "item_client_id", nullable = false)
private int clientId;
#Column(name = "relatedItemId", length = 100, nullable = false)
private String relatedItemId;
#Column(name = "related_item_client_id", nullable = false)
private int relatedItemClientId;
...
}
#Entity
#Table(name = "related_item")
public class RelatedItem {
#EmbeddedId
private RelatedItemPK id;
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumns({
#JoinColumn(name="itemId", referencedColumnName="itemId", insertable=false, updatable=false),
#JoinColumn(name="item_client_id", referencedColumnName="item_client_id", insertable=false, updatable=false)
})
private Item item;
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumns({
#JoinColumn(name="related_item_client_id", referencedColumnName="item_client_id", insertable=false, updatable=false),
#JoinColumn(name="relatedItemId", referencedColumnName="itemId", insertable=false, updatable=false)
})
private Item relatedItem;
...
}
The problem is while creating foreign keys for RelatedItem entity, I got an SQLException. It is the second ManyToOne relation that fails. The foreign key generation sql is below,
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_related_item_client_id FOREIGN KEY (related_item_client_id, relatedItemId) REFERENCES item (item_client_id, itemId)
Since item table is indexed first by itemId then by item_client_id, this statement causes MySQL to produce an error.
I would like to switch the places of columns so that the SQL should look like the following,
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_relatedItemId FOREIGN KEY (relatedItemId, related_item_client_id) REFERENCES item (itemId,item_client_id)
I tried changing the order of "JoinColumn"s but the result didn't change. I also tried renaming the fields to check if persistence provider choses the order by column name but again the result didn't change.
So, is there a way to enforce the column ordering?
p.s. I use following stuff:
MySQL 5.1
EclipseLink 2.0.0
Java EE 6
JPA 2
GlassFish v3
Edit: EclipseLink produces following SQL, which fails to run;
CREATE TABLE related_item (SIMILARITY DOUBLE, widget_id INTEGER NOT NULL, relatedItemId VARCHAR(100) NOT NULL, itemId VARCHAR(100) NOT NULL, related_item_client_id INTEGER NOT NULL, item_client_id INTEGER NOT NULL, PRIMARY KEY (widget_id, relatedItemId, itemId, related_item_client_id, item_client_id));
CREATE TABLE item (IMAGEURL VARCHAR(2048), STATUS VARCHAR(64), URL VARCHAR(2048), PRICE DOUBLE, STOCK INTEGER, DESCRIPTION TEXT(64000), NAME VARCHAR(255), ITEMID VARCHAR(100) NOT NULL, item_client_id INTEGER NOT NULL, PRIMARY KEY (ITEMID, item_client_id));
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_itemId FOREIGN KEY (itemId, item_client_id) REFERENCES item (itemId, item_client_id);
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_related_item_client_id FOREIGN KEY (related_item_client_id, relatedItemId) REFERENCES item (item_client_id, itemId);
ALTER TABLE item ADD CONSTRAINT FK_item_item_client_id FOREIGN KEY (item_client_id) REFERENCES client (ID);
Please include the stack trace. However, I strongly recommend you skip the #JoinColumn tags unless you have a VERY good reason for specifying the foreign keys yourself. By specifying the mappedBy attribute in one of the directions, JPA can figure out what to do by itself.
Java EE 6 and JPA put a lot of effort into enabling Convention over Configuration, which means that most of the time, things will work out of the box. It's desirable for you, the programmer because you have less boiler plate code to worry about, and it's desirable for the JPA and Jave EE container implementors because it gives them freedom to chose the best performing solutions. By declaring the foreign key relationships yourself, you rob both you and JPA of this advantage.
Edit: In fact, I suspect that both specifying mappedBy and specifying the #JoinTable could be the root cause of your problem. But I need to see the stack trace to tell for sure.
The order of the columns should not matter. If it does, then you could change the order in your index to match, or change the order you list your primary key in, or just use your scripts to generate your DDL.