JHipster 4.14.4 : Registering a user with additional information - spring-data-jpa

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

Related

How to avoid id collisions in Spring Data JPA/Hibernate-generated database?

We use a dockerized postgres database and have hibernate auto-generate the tables (using spring.jpa.hibernate.ddl-auto: create) for our integration tests. Using something like H2 is not an option because we do some database-specific operations in a few places, e.g. native SQL queries.
Is there any way to avoid id collisions when all entities use auto-incremented ids? Either by offsetting the start id or, better yet, having all tables use a shared sequence?
Schema is created when the docker container is launched, tables are created by Spring Data JPA/Hibernate
Example
Examples use kotlin syntax and assumes the "allopen"-plugin is applied for entities.
Sometimes we've had bugs where the wrong foreign key was used, e.g. something like this:
#Entity
class EntityOne(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
)
#Entity
class EntityTwo(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
)
#Entity
class JoinEntity(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, columnDefinition = "SERIAL")
var id: Long,
#ManyToOne
#JoinColumn(name = "entity_one_id")
var entityOne: EntityOne,
#ManyToOne
#JoinColumn(name = "entity_two_id")
var entityTwo: EntityTwo,
)
#Repository
interface JoinEntityRepository : JpaRepository<JoinEntity, Long> {
//
// Bug here! Should be "WHERE entityOne.id = :entityOneId"
//
#Query("SELECT entityTwo FROM JoinEntity WHERE entityTwo.id = :entityOneId")
fun findEntityTwoByEntityOneId(entityOneId: Long): Collection<EntityTwo>
}
These bugs can in some circumstances be very hard to find because when the table is created, there may very well be an Entity2 with the same id as some Entity1, and so the query succeeds but the test fails somewhere down the line because while it is returning one or more Entity2, it's not the expected ones.
Even worse, depending on the scope of the test it may pass even if the wrong entity is fetched, or fail only when tests are run in a specific order (due to ids getting "out of sync"). So ideally it should fail to even find an entity when the wrong id is passed. But because the database structure is created from scratch and the ids are auto-incremented they always start at 1.
I found a solution to this.
In my resources/application.yml (in the test folder, you most likely do not want to do this in your main folder) I add spring.datasource.initialization-mode: always and a file data.sql.
The contents of data.sql are as follows:
DROP SEQUENCE IF EXISTS test_shared_sequence;
CREATE SEQUENCE test_shared_sequence;
ALTER TABLE entity_one ALTER COLUMN id SET DEFAULT nextval('test_shared_sequence');
ALTER TABLE entity_two ALTER COLUMN id SET DEFAULT nextval('test_shared_sequence');
After Spring has auto-generated the tables (using spring.jpa.hibernate.ddl-auto: create) it will run whatever is in this script, and the script will change all tables to auto-generate ids based on the same sequence, meaning that no two entities will ever have the same id regardless of which table they're stored in, and as such any query that looks in the wrong table for an id will fail consistently.

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 :)

Prevent Concurrent Modification. Eclipselink (OptimisticLockException)

Problem:
Two Admins -> Admin1 and Admin2 Simultaneously modifying UserA from different terminals.
There is a many-to-many relation between User and UserGroup entity.
Here what is happening is when Admin1 assigns Group1 to UserA, the information is saved.
And when Admin2 from different terminal assigns Group2 to UserA, the information saved but overwrites the changes made by Admin1. Means when I check in DB, UserA is assigned to Group2 while Admin2 is not aware of changes made by Admin1.
Is this Optimistic Situation?
If this is optimistic situation then why this is not throwing OptimisticLockException?
How would I notify Admin2 about the changes made by Admin1.
Using Eclipselink as below:
#ManyToMany
#JoinTable(name = "user_group", joinColumns = { #JoinColumn(name = "user_group_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") })
private List<User> userList;
#ManyToMany
#JoinTable(name = "user_group",joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "user_group_id", referencedColumnName = "id") })
private List<UserGroup> userGroupList;
#Version annotation is marked over version column which is updated on each operation.
Let know if you require more information from my side.
When group1 is assigned to user1, save last modified timestamp. Next time when the request comes to get the user1 details make sure you provide last modified date along with other details. When admin1 modifies the request ask him to provide the same last modified date which you supplied during get operation. Compare the last modified date s same as what you provided to admin1 before saving the requestrequest. Basically here last modified date is kind of token. You store a token, provide the same to user accessing the data and then when user modifies the data and sends it for persist operation make sure token in the database matches the one user has sent back to you else it means that the record was modifed and the token is changed in databse and dont allow the user to persist bad data.
Hope this helps.

jpa OneToMany & ManyToOne

i have a oneToMany and ManyToOne mapping in my models:
class User
#OneToMany (cascade = { CascadeType.REFRESH, CascadeType.DETACH }, fetch = FetchType.EAGER)
private Set<Judgement> judgements;
class Judgement
#ManyToOne(cascade = { CascadeType.REFRESH, CascadeType.DETACH})
#JoinColumn(name = "user_id")
private User judge;
and in DB, i have to tables as Users and Judgements, when i tried to run my code, it showed error as:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "users_judgements" does not exist
does that mean i have to create the table users_judgements by hand, jpa cannot automatically create the relationship for me? RoR can do it...Thanks.
If you have a foreign key from USER table, user_id, in JUDGMENT table then there is no need to have another table. That will be the #JoinColumn(name = "user_id").
See if you are missing something; I can not see any "mappedBy" attribute, which will be "user" in your case.
Please take a look at this article; page 4 that the link will take you, give details about one-to-many relationship. It will be worth reading the whole article.
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa2.html?page=4
Also, the part 1 of the same tutorial is good for basics;
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa1.html

JPA #Jointable does not create column for id

I'm working on JBoss AS 7 using JPA to have a List of Beans in a Entity-Bean like this:
#Entity
class section {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private List<Component> components;
// ...
The table to join the two tables gets created, but it does not contain an Id, which leads to JPA creating a unique-constrain on one of the columns (SECTION_ID). Which is not really what I want, because one section can have more than one component. One component can be used in more than one section too.
I already tried
#JoinTable(name="SECTION_COMPONENT",
joinColumns = {
#JoinColumn(name="section_id", unique = false)
},
inverseJoinColumns =
#JoinColumn(name="component", unique = false)
}
I guess JPA needs at least one unique column, so it just adds that to the last column if nothing else is specified. I'd be fine with adding a new column "id" to set up a primary (or unique) key. But I am not sure how to do that.
Thanks a lot for any help
The mapping is not correct: #ManyToOne in your case means that you have one component that has many sections:
#ManyToOne
private Component component;
According to your description, you need an #ManyToMany relationship:
#ManyToMany
private List<Component> components;