Database design with JPA OneToMany with EmbeddedId? - java

i have an issue (maybe only in my head :-)) with some Database-Design JPA related stuff. I want to have the following:
Permission table:
ProjectId
UserId
This table should have an entry for each user and each project the user has permission to. Example:
ProjectId1 - user1
ProjectId1 - user2
ProjectId2 - user1
For sure, i also have a project table with the projectId as Primary Key. And i want to have a OneToMany dependency to the permission table.
What i do not want to do is to have a generated ID in the permissions table. As far as i know this can be solved with an EmbeddedId.
Using an EmbeddedId, i could not get a OneToMany or ManyToMany dependency to work. I tried a lot of stuff but in the end i am not sure if this is maybe the wrong way to go?
What worked was to have a generatedId in the Permissions table, but i think this should not be needed, or?
Thanks in advance

Related

Spring JPA Multiple join conditions

I am working on my first Spring Boot application and I am having a bit of a struggle within the joining of two tables/entities.
I need to join my user_roles table to the users table so that the user will get its correct roles directly from the database (postgres).
The thing that is my the fact that the roles are depending on the current organization that the user is in. The app allows a user to work for multiple companies at once, and switch between them. The roles a user has in organization 1 may not apply to organization 2.
The user has a roles field in the database which should be mapped to the correct roles. The user also has a 'current_organization_id' field, which contains the current user organization.
I think the solution wouldn't be that difficult but my brain is not solving this case yet.
Roles could be mapped on 'user_id' (UUID) and 'cur_org_id' (UUID) with an table like 'user_role_within_org'. But I don't know how to do that within JPA.
An explanation how your solution is solving this would be nice, it is important to understand code and not just copy paste it ;)
Thanks in advance,
Jesse
Current database model
Try storing a Map of Organization->List in the User.
#Entity
class User {
#ManyToMany
Map<Organization, List<Role>> roles;
}
To get the roles in current organization try the following JPQL:
SELECT VALUE(r)
FROM User u JOIN u.roles r
WHERE KEY(r).id = u.currentOrganization.id

Hibernate attempts to delete parent entity in unidirectional ManyToOne relationship

I have a simple unidirectional ManyToOne relationship on an entity, which unfortunately is defined in a schema I cannot change.
It is defined as follows
#Entity
#Table(name="Profile")
...
public class Profile{
#ManyToOne
#JoinColumn(name="usr_id", nullable=false, updatable=false)
private User usr;
...
and all is well. The relationship is enforced with a foreign key in the db, hence the nullable = false and updatable = false. There is no mention of the profiles in user.
When I try to delete a Profile, hibernate also tries to delete the User entity, which is parent to other relationships and therefore fails. I have no CascadeType annotations anywhere.
My intent is to have a simple reference to the user using this profile in the usr field. This is a unidirectional relationship. The user entity should not be affected whenever I delete a profile.
This appers to be achievable when the usr field may be dereferenced before delete (I can see in the hibernate generated sql that hibernate attempts to set the field to null before deletion) - however that fails because of the foreign key.
Is what I'm trying to do achievable? If so, how?
(I'm using spring data on top of hibernate, if that is relevant.)
further Infos: I have tried optional=false, and it leads to the delete the parent entity behaviour. I have tried all fitting combinations of CascadeTypes, #OnDelete with NO_ACTION (still tries to delete the user) and defining a reverse but owned by user relationship - no success so far. On top of that, I tried the search function ;), which lead me to the conclusion that this is just my problem. If I missed an answered question, I'd appreciate a pointer in the right direction. Thanks.
Do you have some kind of other non-nullable association #ManyToOne or #OneToOne to the Profile entity? You can debug into the deletion process by setting a break point in e.g. the JDBC driver in e.g. Connection.prepareStatement and go down the stack frames to the cascading part to figure out why this cascading happens.
If all that doesn't help, please create a reproducing test case and submit an issue to the Hibernate issue tracker.

Edit JPA-Mapping in IntelliJ Idea

I'm having a JPA-Project in IntelliJ Idea and there are some entities my colleague mapped some time ago. Now the DB team added a bunch of tables I'm trying to add as entities to the Java-Project. But when I'm trying to map a new entity to a existing entity IntelliJ Idea doesn't know the entity. So I'm wondering, if the only way is to re-import the table?
BankEntity exists in the JavaProject, but the mapper doesn't recognize it.
Thanks !
If it is an entity that is newly added to the Intellij project, it is unaware of the related table in the database.
You have to Generate Persistence Mapping -> By Database Schema and choose/define the the datasource and then import the table. If the definition of an already mapped entity have been changed(e.g. new column added), then a refresh might help.
I'm going to answer my own question: When generating the entities, Intelli recognizes that there is an existing entity and only add the new attributes to that class. It's somehow confusing, that you have to select the entity like a new entity...but it work's.

Hibernate: Many To Many Relation with attributes: correct configuration with Annotations

I have a problem with Hibernate (Thanks to Thomas now the problem is more legible).
In Short:
How to configure a ManyToMany association with Hibernate when the relationship has an attribute and we need save, delete and update in cascade?
In Large:
Imagine the following DataBase:
User Profile
M______N
|
attribute
There are 3 tables here:
"User", "Profile" and "User_Profile".
Now imagine User_Profile has 1 attribute for the relation (and obviously the keys).
Ok, now this is translating to Hibernate by the following:
User:
// The relation is Many to Many, but considering that it has an attribute, this is OneToMany with the ManyMany RelationShip
#OneToMany(mappedBy="user", targetEntity=UserProfile.class)
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
#LazyCollection(LazyCollectionOption.TRUE)
private Set<UserProfile> userProfile = new HashSet<UserProfile>();
UserProfile:
#Id
#ManyToOne(targetEntity=User.class,fetch=FetchType.LAZY)
#Cascade({CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="...", referencedColumnName="...") })
private User user;
#Id
#ManyToOne(targetEntity=Profile.class,fetch=FetchType.LAZY)
#Cascade({CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="...", referencedColumnName="...") })
private Profile profile;
So, I think the configuration is correct, and the save, independently if the User has Profile childrens save all of them. The problem is when I try to update the user:
getHibernateTemplate().getSessionFactory().getCurrentSession().clear();
getHibernateTemplate().saveOrUpdate( user );
getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
Hibernate don´t delete the Profile relation if there is an empty set of Profile childrens. Only add the profiles (override the old)... That´s rare... What´s the problem?
Thank you in advance
All you actually do is remove the relation and thus theres no DELETE to cascade, that's why nothing gets deleted.
Try adding the Hibernate cascade type DELETE_ORPHAN (using the #Cascade annotation) to make Hibernate delete entities that are not referenced anymore.
Additionally, I'd not remove the Mini entities alone. If there's no relation, i.e. the set of Minis is empty, it normally makes no sense to keep the SuperMini entities that now represent an empty collection (in rare cases it might make sense, just want you to think about whether you need them or not).
Edit:
Note that with DELETE_ORPHAN you should reuse the set, otherwise all the relations might be deleted and reinserted.
Basically Hibernate would then see the set being changed and would issue a delete for the "old" set and a reinsert for the "new" set. This could be wanted but in case you only want an update, i.e. only delete the entities that are not in the set anymore, you should do the following:
clear the set
add the "new" set to the reused and now cleared set using addAll(...)
This should trigger the update (and deletion of orphans) only.

Hibernate cascade + composite id's issue

I'm currently learning Hibernate, and I've stumbled into this issue:
I have defined 3 entities: User, Module, Permission. Both user and module have a one-to-many relationship with Permission, so that Permission's composite id consists of idUser and idModule. The user class has a property that is a set of Permission's and it is appropriately annotated with #OneToMany, cascade=CascadeType.ALL, etc.
Now, I generated the classes with MyEclipse's reverse engineering feature. The id of permission was created as a separate class that has an idUser and idModule property. I thought I could create a User, add some new permissions to it, and thus saving the user would cascade the operation, and permissions would be saved automatically. This is true except that the operation causes an exception. I run the following code:
Permission p = new Permission();
p.setId(new PermissionId(null, module.getId());
user.getPermissions().add(p);
session.save(user);
The problem I have is that, even though the SQL is being generated correctly (first saves User, then Permission), I get an error from the database driver (Firebird) which states that it can't insert a null value for idUser, which is true, but shouldn't hibernate be passing the newly created user id to the second query?
This particular scenario feels very counter-intuitive to me since I'm inclined to pass a null id to the Permission object since it is new and I want it to be created, but on other hand, I have to set the idModule property since the module already exists, so I don't really understand how an operation like this is supposed to work.
Does anyone know what I'm doing wrong? Thanks
You need to specify a cascade action for Hibernate to perform when you save a User with an attached transient (meaning not-yet-saved) Permission.
By the way, you might want to consider using a different ID strategy for the Permission object, such as a generated ID value - how can the primary key of the permission row in the database contain a null value?

Categories