I have a problem with the definition of the OneToOne bidirectional relationship with the same entity.
In particular, I defined a class User and I wanted to give it an attribute partner, that is a User itself.
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true)
private Long ID;
...
#OneToOne(cascade = CascadeType.ALL, targetEntity = User.class)
#JoinTable(
name = "user_partners",
joinColumns = {#JoinColumn(name = "u1", referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name = "u2", referencedColumnName = "ID")}
)
private User partner;
...
}
Now, if I test this case defined in my repository UserRepository.java:
...
User um = new User(...);
User uf = new User(...);
um.setPartner(uf);
...
I would expect that both um and uf have the reference to their partner. Instead, only um has the reference as I explicitly defined it.
What am I doing wrong?
Related
I have two jpa entities User + profile with many to many relationship like below :
User Entity
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "t_user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long Id;
#Column(nullable = false)
private String username;
#Column(nullable = false, length = 14)
private String siret;
#Cascade({
org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.MERGE,
org.hibernate.annotations.CascadeType.PERSIST
})
#ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinTable(name = "t_user_profile", joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "profile_id")})
private Set<ProfileEntity> profiles = new HashSet<>();
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinColumn(name = "ressource_id")
private RessourceEntity ressource;
}
and Profile entity :
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "t_profile")
public class ProfileEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "profile_id")
private Long Id;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private Profiles profile;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "profiles")
private Set<UserEntity> user = new HashSet<>();
}
The problem is when I send an http request contaning the same profiles with different users, I obtain new duplicated profiles records with new IDs in the Profile table in the database like below :
but the correct way is to add the informations ONLY in the joined table t_user_profile because profile already exists in profile table .
I tried cascade.All , persist and merge .... but the same result .
And if I remove cascade and i search for profile Id using findById and get the Id and insert... I got a problem when the profile is new and don't exist in DB and the problem is because I removed cascade ... So I want if the profile already exists , I want to add the relationship in the joined table ONLY.
I'm junit testing inside the below function:
#Test
public void testQueryUser(){
User user1 = userRepository.findById(12L).orElse(null);
assertThat(user1)
.hasFieldOrPropertyWithValue("username", "adam");
}
The below is my domain class User:
#Entity
#Table(name = "sys_user")
#Data
#NoArgsConstructor
#AllArgsConstructor
#DynamicUpdate
public class User extends BaseEntity implements Serializable{
#Id
#Column(name = "user_id")
#NotNull(groups = {Update.class})
#Null(groups = {Create.class})
#ApiModelProperty(value = "ID", hidden = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#ApiModelProperty(value = "user_role")
#JoinTable(name = "sys_users_roles",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id", referencedColumnName = "role_id")})
private Set<Role> roles;
#ManyToOne
#JoinColumn(referencedColumnName = "dept_id")
private Dept dept;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "sys_user_jobs",
joinColumns = {#JoinColumn(name="user_id", referencedColumnName = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "job_id", referencedColumnName = "job_id")})
private Set<Job> jobs;
#NotBlank
#Column(unique = true)
#ApiModelProperty(value = "username")
private String username;
}
Got the below error message as expected:
java.lang.AssertionError:
Expecting
User(id=12, roles=[], dept=null, jobs=[], username=adam)
to have a property or a field named "username" with value
"adam"
but value was:
"paul"
But when switching #ManyToMany(fetch = FetchType.EAGER) back to #ManyToMany(fetch = FetchType.LAZY) inside domain class
I got
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: com.adminsys.modules.system.domain.User.roles,
could not initialize proxy - no Session
My questions are:
I'm only testing the field username but why JUnit is looking into field roles?
Why no session?
What's the best practice for my scenario?
I have an ClientUser entity with #OneToMany field customField:
#Entity
#Data
public class ClientUser {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "client_user_custom_field",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "field_id", referencedColumnName = "id"))
private Collection<ClientFieldValue> customFields;
}
And a ClientFieldValue entity:
#Entity
#Data
public class ClientFieldValue {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String value;
}
How can I create ClientFieldValue's for all existing ClientUser?
I know that I can do smth like:
final Iterable<ClientUser> clientUsers = userRepository.findAll();
clientUsers.forEach(clientUser ->
clientUser.setCustomFields(Collections.singleton(new ClientFieldValue())));
userRepository.saveAll(clientUsers);
But I don't want to query all and save them after set.
Is there another way to do this?
I am working on app that uses microservices architecture, i have project A which has this entity User with this code
#JoinTable(
name = "USER_AUTHORITY",
joinColumns = { #JoinColumn(
name = "USER_ID",
referencedColumnName = "id") },
inverseJoinColumns = { #JoinColumn(
name = "AUTHORITY_ID",
referencedColumnName = "id") })
private List<Authority> authorities;
and another entity Authority
#Entity
#Table(name = "AUTHORITY")
public class Authority {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "authority_seq")
#SequenceGenerator(name = "authority_seq",
sequenceName =
"authority_seq", allocationSize = 1)
private Long id;
#ManyToMany(mappedBy = "authorities", fetch = FetchType.LAZY)
private List<User> users;
}
and i have project B which has entity AAA with this code:
#Entity
public class subUser extends User
so when i run the the project B I get the following error:
org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: com.A.model.User.authorities[com.A.model.Authority
I get the solution from here the link
may be it help someone
I'm trying to create a join table with #JoinTable from 3 different entities. Below is the code sample I'm using. While creating join table I'm getting below error. Please help to resolve.
There are 3 entities in my design. Credential, Category and Tenant.
Now I'm trying to make a join table that will contain the pk of these 3 tables using #ManyToMany annotation between them. Below is the relationship which I'm trying to make.
#Entity
#Table(name = "CREDENTIALS")
public class CredentialsEntity {
/** The credential id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int credentialId;
#ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "CATEGORY_HAS_CREDENTIALS",
joinColumns = {
#JoinColumn(table="CATEGORY", name = "CATEGORY_ID", referencedColumnName = "ID"),
#JoinColumn(table ="TENANT", name = "TENANT_ID", referencedColumnName = "ID")},
inverseJoinColumns = #JoinColumn(table="CREDENTIALS", name = "CREDENTIAL_ID", referencedColumnName = "ID"))
private List<TenantEntity> tenantEntities;
}
==========================
#Entity
#Table(name = "TENANT")
public class TenantEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int tenantId;
#ManyToMany(mappedBy = "tenantEntities", cascade = CascadeType.ALL)
private List<CredentialsEntity> credentialsEntities;
}
==========================
#Entity
#Table(name = "CATEGORY")
public class NodeCategory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int categoryId;
#ManyToMany(cascade = CascadeType.ALL)
private List<CredentialsEntity> credentialsEntities;
}
Caused by:
org.hibernate.AnnotationException: Cannot find the expected secondary
table: no TENANT available for
com.aricent.aricloud.entity.CredentialsEntity at
org.hibernate.cfg.Ejb3Column.getJoin(Ejb3Column.java:363) at
org.hibernate.cfg.Ejb3Column.getTable(Ejb3Column.java:342) at
org.hibernate.cfg.Ejb3Column.checkPropertyConsistency(Ejb3Column.java:584)
at
org.hibernate.cfg.annotations.CollectionBinder.buildCollectionKey(CollectionBinder.java:1018)
at
org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1336)
EDIT
I'm able to do the jointable like below, as mentioned in link
is this correct approach or I'm doing something wrong?
#ManyToMany(cascade={CascadeType.ALL})
#JoinTable(name = "CATEGORY_HAS_CREDENTIALS",
joinColumns = {
#JoinColumn(name = "CREDENTIAL_ID", referencedColumnName = "ID")},
inverseJoinColumns = #JoinColumn(table = "CATEGORY", name = "CATEGORY_ID", referencedColumnName = "ID"))
#MapKeyJoinColumn(name = "TENANT_ID", referencedColumnName ="ID")
#ElementCollection
private Map<TenantEntity, NodeCategory> tenantCategoryMap = new HashMap<TenantEntity, NodeCategory>();