OneToOne bidirectional relationship with the same entity - java

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

Many to many jpa relationship creating duplicated data in entity with new IDs

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.

SpringBoot Test JPA got "no session" exception when FetchType=Lazy

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?

How to set value for all OneToMany entities?

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?

Is there a way to use #OneToMany and #ManyToMany to handle polymorphism in hibernate

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

JoinTable in JPA for 3 different entities

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>();

Categories