JPA: exception obtaining an entity that references another - java

I am writing a simple application using Spring and JPA. I have 2 entities: User and Role, with a relation of N..1.
Whenever I try to get any of this entities from the database, I get an exception (It's shown below). Said exception is thrown when one entity tries to get the other entity through its foreign key.
For example, when I ask for a Role, all its attributes are obtained correctly, excepting userCollection (the group of users assigned to that role).
The exception is always thrown, no matter which method I use to ask the entity (.find(pk), .createNamedQuery(), .createQuery(), ...).
Following is the code. I have skipped the non relevant parts:
User entity:
#Entity
#Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Size(min = 1, max = 50)
private String id;
#JoinColumn(name = "rol", referencedColumnName = "id")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private Role rol;
...
Role entity:
#Entity
#Table(name = "roles")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
#Size(min = 1, max = 50)
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "rol", fetch = FetchType.LAZY)
private Collection userCollection;
...
The exception thrown when I try to obtain a role:
Exception [EclipseLink-6094] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.QueryException
Exception Description: The parameter name [id] in the query's selection criteria does not match any parameter name defined in the query.
Query: ReadAllQuery(name="userCollection" referenceClass=User sql="SELECT ID, EMAIL, NAME, rol FROM users WHERE (rol = ?)")
at org.eclipse.persistence.exceptions.QueryException.parameterNameMismatch(QueryException.java:1031)
at org.eclipse.persistence.internal.expressions.ParameterExpression.getValue(ParameterExpression.java:246)
at org.eclipse.persistence.internal.databaseaccess.DatabaseCall.translate(DatabaseCall.java:918)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:204)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:191)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeSelectCall(DatasourceCallQueryMechanism.java:262)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectAllRows(DatasourceCallQueryMechanism.java:618)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRowsFromTable(ExpressionQueryMechanism.java:2537)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRows(ExpressionQueryMechanism.java:2496)
at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:455)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:997)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:675)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:958)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:432)
...
I tried to remove the userCollection from the Role entity. If I try to get a role it works just fine, but if I try to find an user, I get the following exception:
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Transaction marked for rollback.
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1014)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:755)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
...
I don't know how to solve this issue. I've spent a lot of time. Help would be appreciated :)

First of all, you don't indicate your table layout, which makes answering your question a guessing game.
To begin, I would avoid using #Basic on the ID column in User. #Size is ok, but you might want to match that in JPA as well. See below (the nullable=false and unique=true attributes are redundant with the #Id annotation):
#Id
#Column(length=50, nullable=false, unique=true)
#Size(min = 1, max = 50)
private String id;
If the ID is basically a user name, this is a bad idea. Things can get tricky down the road if the user wants to change his user name. Additionally, foreign key references to the users table require 50 bytes instead of 4 (it's a bit more complex than that, but you get the idea). I'd add a regular Integer ID and a separate userName field. The user never needs to see the ID.
Similar treatment should be given to Role.name:
#Column(length=50, nullable=false, unique=true)
#Size(min = 1, max = 50)
private String name;
Second, it looks like a user can have only one role? Seriously? Anyway, if that's the case the referencedColumnName attribute is not needed, since the ID column in roles uses the default name:
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "rol")
private Role rol;
Having foreign key column named "rol" instead of "role_id" is not a good idea. I'd use the default (just drop the #JoinColumn stuff).
If you intended that users could have more than one role, that requires a join table (you can omit the #JoinTable stuff if you want to use the defaults):
#ManyToMany
#JoinTable(
name = "user_roles",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") }
)
private List<Role> roles;
Third, it's hard to see the usefulness of having the list of users in Role. I'd remove it entirely. Instead use something like user.getRoles.contains(SomeRole) to see if a user has a certain role.
Finally, looking at your code, there are a couple of things that I should clear up:
In the "column" annotations (#Column, #JoinColumn, etc), the name and referencedColumnName attributes allow you to specify the actual column names. They do not refer to field names in your classes.

Related

Long field as foreign key in JPA entities, with on-cascade Delete

I'm having issues with defining a foreign key field within an entity. One specific thing that I can't find an answer to, is how to define such field but as a Long type, and not as that target entity type, and also set it up as ON DELETE CASCADE.
E.g.
#Entity
#Table(name = "user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
and
#Entity
#Table(name = "address")
public class AddressEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JoinColumn(
table = "user",
name = "user_id",
referencedColumnName = "id")
private Long userId;
}
This example works fine, but now one can't easily define this DELETE ON CASCADE for the userId field i.e. Address entity.
One specific thing that I can't find an answer to, is how to define
such field but as a Long type, and not as that target entity type, and
also set it up as ON DELETE CASCADE.
It stands to reason that you cannot find an answer, because JPA does not provide one. If you want JPA to manage relationships between entities, then you must define those relationships in the JPA way, with entities holding references to other entity objects and declaring appropriate relationship annotations.* And if you want cascading deletes in your persistence context then you definitely do want them to be managed / recognized by JPA, for any other kind of approach is likely to create problems involving the context falling out of sync with the underlying data store.
It's unclear what problem you are trying to solve by avoiding JPA-style relationship management, but I'm inclined to think that there must be a better way. For example, if you want to avoid requiring the persistence context to load the associated UserEntity whenever an AddressEntity is loaded, then you would define the relationship with a lazy fetch strategy:
#Entity
public class AddressEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY)
private UserEntity user;
}
#Entity
public class UserEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL,
mappedBy = user)
AddressType address;
}
(Do note, however, that FetchType.LAZY is a hint, not a constraint. The context might sometimes still load the user together with its address if that's convenient.)
If you want to get the associated user id from an address, then the best way to do so is to read it from the user:
// ...
public Long getUserId() {
return (user == null) ? null : user.getId();
}
That does require the UserEntity to define an accessible getId() method, but since you are using JPA field-based access, you do not need also to provide a setter, and you may give the method default access. Or you could just declare UserEntity.id such that it is directly accessible by AddressEntity.
On the other hand, if you want to provide for the user ID to be accessible without loading the user entity then instead of a method such as the above getUserId(), in addition to the relationship field you could define a persistent, read-only AddressEntity.userId field, mapped to the appropriate column. It must be read-only because the value of the id in the underlying data store will necessarily be managed via the entity relationship, so it cannot also be managed via this separate field. For example:
#Entity
public class AddressEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY)
private UserEntity user;
#Column(name = "user_id", insertable = false, updatable = false, nullable = true)
public Long userId;
}
This is a brittle approach, and I do not recommend it. It will be prone to problems with the userId field falling out of sync with the user entity. That may be bearable for the usage you have in mind, but this sort of weirdness is fertile ground for future bugs.
*Side note: as far as I know or can determine, JPA does not define semantics for a #JoinColumn annotation on a non-relationship field such as in your original code. That doesn't mean that your particular persistence provider can't interpret it in a way that you characterize as "works fine", but at minimum you are on thin ice with that.

JPA ManyToMany to use grouping & crosswalks to join data together

Building a Spring Boot REST service backed by MySQL here. I'm adding a super-simple chat feature to an app and this service will handle its backend/enndpoints. I'm new to JPA and have two concerns: (1) that my primordial data model itself may be a little awry; and (2) that I'm not wrapping that model correctly using JPA conventions/best practices.
So first: an overview of the simple problem I'm trying to solve: Users can send Messages to 1+ other Users. This creates a Conversation, which is really just a container of 1+ Messages. If the Conversation is only between 2 Users, it's considered (by the app) to be a Direct Message (DM). Otherwise its considered to be a Group Chat.
My tables (pseudo-schema):
[users]
=======
id PRIMARY KEY AUTO_INC INT NOT NULL,
username VARCHAR(255) NOT NULL
[conversations]
===============
id PRIMARY KEY AUTO_INC INT NOT NULL,
created_on DATETIME NOT NULL
[messages]
==========
id PRIMARY KEY AUTO_INC INT NOT NULL,
conversation_id FOREIGN KEY INT NOT NULL, # on conversations table
sender_id FOREIGN KEY INT NOT NULL, # on users table
text VARCHAR(2000) NOT NULL,
sent_at DATETIME
[users_x_conversations]
=======================
id PRIMARY KEY AUTO_INC INT NOT NULL,
conversation_id FOREIGN KEY INT NOT NULL, # on conversations table
user_id FOREIGN KEY INT NOT NULL, # on users table
So in my design above, you can see I'm really just using the [conversations] table as a placeholder and as a way of grouping messages to a single conversation_id, and then [users_x_conversations] is crosswalk (many-to-many) table where I'm actually storing who is a "member of" which conversation.
Is this the right approach to take or is there a better way to relate the tables here? That's Concern #1.
Assumning I'm modeling the problem at the database correctly, then I have the following JPA/entity classes:
#MappedSuperclass
abstract public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Ctors, getters & setters down here...
}
#Entity(name = 'messages')
#AttributeOverrides({
#AttributeOverride(name = 'id', column=#Column(name='message_id'))
})
public class Message extends BaseEntity {
#OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinColumn(name = 'conversation_id', referencedColumnName = 'conversation_id')
#NotNull
#Valid
private Conversation conversation;
#OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinColumn(name = 'user_id', referencedColumnName = 'user_id')
#NotNull
#Valid
private User sender;
#Column(name = 'message_text')
#NotEmpty
private String text;
#Column(name = 'message_sent_at')
#NotNull
private Date sentAt;
// Ctors, getters & setters down here...
}
#Entity(name = 'conversations')
#AttributeOverrides({
#AttributeOverride(name = 'id', column=#Column(name='conversation_id'))
})
public class Conversation extends BaseEntity {
#Column(name = 'conversation_created_on')
#NotNull
private Date createdOn;
// Ctors, getters & setters down here...
}
What I'm stuck on now is: how should I model my [users_x_conversations] table at the JPA layer? Should I create something like this:
#Entity(name = 'users_x_conversations')
#AttributeOverrides({
#AttributeOverride(name = 'id', column=#Column(name='users_x_conversations_id'))
})
public class UserConversations extends BaseEntity {
#ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinTable(
name="users_x_conversations",
joinColumns=[
#JoinColumn(name="user_id")
],
inverseJoinColumns=[
#JoinColumn(name="conversation_id")
]
)
private Map<User,Conversation> userConversations;
// Ctors, getters & setters down here...
}
Basically my service will want to be able to do queries like:
Given a conversationId, who are all the users that are members of that conversation?; and
Given a userId, what are all the conversations that user is a member of (DM and Group Chat alike)?
Is this the right approach to take or is there a better way to relate the tables here?
Your approach seems OK at the DB layer, except that if users_x_conversations serves only as a join table (i.e. if there are no extra properties associated with the (user, conversation) associations represented within), then I would use (conversation_id, user_id) as its PK instead of giving it a surrogate key. If you don't do that, then you should at least put a uniqueness constraint on that pair.
What I'm stuck on now is: how should I model my [users_x_conversations] table at the JPA layer?
I take you to be asking whether you should model that table as an entity. If you insist on giving it a surrogate key as you have done, then that implies "yes". But as I already discussed, I don't think that's needful. Nor much useful, for that matter. I would recommend instead modeling a direct many-to-many relationship between Conversation and User entities, with this table (less its id column) serving as the join table:
#Entity
#Table(name = "converations")
public class Conversation extends BaseEntity {
#Column(name = 'conversation_created_on')
#NotNull
private Date createdOn;
#ManyToMany(mappedBy = "conversations")
#JoinTable(name = "users_x_conversations",
joinColumns = #JoinColumn(name="conversation_id", nullable = false, updateable = false),
inverseJoinColumns = #JoinColumn(name = "user_id", nullable = false, updateable = false)
)
private Set<User> users;
// Ctors, getters & setters down here...
}
#Entity
#Table(name = "users")
public class User extends BaseEntity {
#NotNull
private String username;
#ManyToMany(mappedBy = "users")
// this is the non-owning side of the relationship; the join table mapping
// is declared on the other side
private Set<Conversation> conversations;
// Ctors, getters & setters down here...
}
Note in that case that User and Conversation entities are directly associated in the object model.
On the other hand, if you did choose to model users_x_conversations via an entity of its own, then the code you present for it is all wrong. It would look more like this:
#Entity
#Table(name = "users_x_converations", uniqueConstraints =
#UniqueConstraint(columnNames={"converation_id", "user_id"}))
public class UserConversation extends BaseEntity {
#ManyToOne(optional = false)
#JoinColumn(name = "conversation_id", nullable = false, updatable = false)
Conversation conversation;
#ManyToOne(optional = false)
#JoinColumn(name = "user_id", nullable = false, updatable = false)
User user;
// Ctors, getters & setters down here...
}
Note well that:
This makes the object-level association between Conversations and Users indirect, via UserConversation entities. If the relationships are navigable from the other side, then they would be modelled via #OneToMany relationship fields of type Set<UserConversation> or List<UserConversation>.
It requires more code, and more objects in the system at runtime.
On the other hand, it does have the minor advantage of saving you from making a somewhat arbitrary choice of which side of a direct #ManyToMany relationship is the owning side.

Is Hibernate ManyToMany self-join possible for non-key columns? getting mappingException

I am having following problem. I have a user entity that has a many to many relationship with other user entities. Hence I want to make a self-join with manytomany annotation. This relation is based on already existing table that is used across the system so I cannot make changes to the DB at all. So we have 2 tables User(Id, ShortName) and UserLink(ParentId, ChildId).
The annotation of ID is assigned to ShortName, but the actual keys in both User and UserLink are ID from User and ParentId and ChildId from UserLink.
I am trying to handle this the following way from the User entity:
#Id
#Column(name = "ShortName")
private String shortName;
#Column(name = "Id")
private long id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "UserLink",
joinColumns = { #JoinColumn(name = "ParentId", referencedColumnName = "Id") },
inverseJoinColumns = { #JoinColumn(name = "ChildId", referencedColumnName = "Id") })
private Collection<UserEntity> children;
Since the key in the User entity is on the ShortName field, I have to specify the "Id" as referenced column name param. If I don't do that, it takes the ShortName as the key and doesn't fetch any data.
When I try to do this the way I showed above, I get the following exception:
Caused by: org.hibernate.MappingException: Duplicate property mapping of **_entity_UserEntity_children found in **.entity.UserEntity
at org.hibernate.mapping.PersistentClass.checkPropertyDuplication(PersistentClass.java:486)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:476)
at org.hibernate.mapping.RootClass.validate(RootClass.java:268)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1287)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1729)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:84)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:904)
... 81 more
Do you have any idea how this could be fixed? One idea is that I could change the #Id in the entity and move it to the Id property that is used for joins, but this would need a lot of effort to rewrite bad existing code.
Anyway, is it possible to make a self-join manytomany on columns that are not keys?
Cheers
Adam

JPA - Mapping OneToMany association between the same table using an intermediate table

I'm creating an application where one large aspect is the ability for users to share content with friends. I'm trying to represent this in the object model and I'm having trouble getting the association to work properly. I'm using a mapping table that records the friender and the friendee, both of which are represented by the primary key (id) of the user. A user can have many friends, and also be referenced by other users. This is what the schema looks like:
Users:
int user_id (PK)
varchar(32) email
varchar(64) password
Users_Map:
int users_map_id (PK)
int friendee_id (FK references users(user_id))
int friender_id (FK references users(user_id))
And this is how I have the User entity set up:
#Data
#Entity
#Table(name = "users")
public class User extends AbstractPersistable<Long> {
#Id
#Column(name = "user_id")
private Long id;
#Column
private String email;
#Column
private String password;
#OneToMany
#JoinTable(name = "users_map",
joinColumns = { #JoinColumn(name = "friender_id") },
inverseJoinColumns = { #JoinColumn(name = "friendee_id") })
private List<User> friends;
}
I run into the following error when deploying the application:
org.hibernate.AnnotationException: A Foreign key refering
com.x.webapp.data.entity.User from
com.x.webapp.data.entity.User has the wrong number of
column. should be 2
I've tried quite a few other configurations, including adding a "referencedColumnName" attribute to each #JoinColumn, but they have also yielded errors. I'm also not entirely sure whether the schema I currently have is the best way to go about mapping users together.
I appreciate any help!
Removing the extension of AbstractPersistable fixed the problem - that contained an #Id reference and clashed with the #Id reference I put inside of User.

Hibernate Spring: #ManyToMany DataIntegrityViolationException ConstraintViolationException

I am building a sample for ManyToMany relationship between: User(1) - ()AccessLevel() - (1)Role
I have implemented 3 classes in Java with hibernate implementation as follow:
Class User
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue
#Column(name="USER_ID")
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "access_level", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "role_id", nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<Role>(0);
Class Role
#Entity
#Table(name="role")
public class Role {
#Id
#GeneratedValue
#Column(name="role_id")
private Integer id;
#Column(name="role_name")
private String roleName;
Class AccessLevel
#Entity
#Table(name="access_level")
public class AccessLevel {
#Id
#GeneratedValue
private Integer id;
#Column(name="role_id")
private Integer roleId;
#Column(name="user_id")
private Integer userId;
Problem:
When I am persisting the User bean using merge method then an exception arise:
#Service
public class UserServiceImpl implements UserService {
#PersistenceContext
private EntityManager em;
#Override
#Transactional
public void save(User user) {
em.merge(user);
}
Exception
org.springframework.web.util.NestedServletException: Request process
ing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; SQL [insert into access_level (user_id, role_id) values (?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
As you can see hibernate is trying to run this query:
insert into access_level (user_id, role_id) values (?, ?)
From my point of view it seems like hibernate is not generating the primary key for AccessLevel even though I have added the #GeneratedValue to the id attribute.
Note:
I am working on production environment with Postgresql and evelopment environment with HSQL database that creates all schemas from the begining based on the entities description. Both environments generate same issue.
Regards,
Cristian Colorado
Reason:
It seems for ManyToMany relationships you do not need to define a class for the "Joining Table". Therefore if I eliminate AccessLevel entity the logic would work perfectly fine. I explain further:
Explanation:
When I defined the User class I also described the relationship with Role:
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "access_level", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "role_id", nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<Role>(0);
Important thing here is I have told hibernate that User entity will relate to Role entity through a table known as "access_level" and such table will have user_id and role_id columns in order to join previous entities.
So far this is all hibernate needs in order to work the many to many relationship, therefore when mergin it uses that information to create and tun this script:
insert into access_level (user_id, role_id) values (?, ?)
Now, the problem cames when I defined a new entity for AccessLevel:
#Entity
#Table(name="access_level")
public class AccessLevel {
#Id
#GeneratedValue
private Integer id;
#Column(name="role_id")
private Integer roleId;
#Column(name="user_id")
private Integer userId;
Now I am telling hibernate that there is a table "access_level" related to AccessLevel entity and it has 3 columns, the most important would be Id which is primary key.
So I defined "access_level" twice!
Solution:
I eliminated the Entity for access_level table.
I re-write my production script in order to have "access_level" with
user_id/role_id columns only.
Note: It would be good to know how to add a primary key to the joining table without generating issues. An alternative would be adding a composed primary key in database(user_id/role_id) which would be independient from hibernate.
Why do you need a PK column in the join table? There will be a composite PK composed of user_id and role_id. Now, as you have discovered a JoinTable for #ManyToMany will only ever have two columns and at some point you may require additional data about this relationship.
e.g.
user_id
role_id
date_granted
You may then want to use your AccessLevel entity however you replace the #ManyToMany with #OneToMany from User to AccessLevel and optionally from Role > AccessLevel.
The Hibernate docs themselves advise against #ManyToMany:
Do not use exotic association mappings:
Practical test cases for real many-to-many associations are rare. Most
of the time you need additional information stored in the "link
table". In this case, it is much better to use two one-to-many
associations to an intermediate link class. In fact, most associations
are one-to-many and many-to-one. For this reason, you should proceed
cautiously when using any other association style.

Categories

Resources