I have the following situations with multiple OneToOne reletanships:
#Table(name = "User")
public class User {
#OneToOne(mappedBy = "settingColumnName")
private Settings setting;
}
#Table(name = "Account")
public class Account {
#OneToOne(mappedBy = "settingColumnName")
private Settings setting;
}
#Table(name = "Settings")
public class Settings{
#OneToOne()
#JoinColumn(name = "userColumnName")
private User user;
#OneToOne()
#JoinColumn(name = "accountColumnName")
private Account account;
}
Now, the issue here is that I have to create and save each model independently, because they are created as a result of StreamEvent capturing. Also, Hibernate will create automatically userColumnName and accountColumnName. What I would really need to do is to have something this:
Is this possible to implement with Hibernate? Could someone provide an example?
Do
#JoinColumn(name="userColumnName", insertable=false,updatable=false),
#JoinColumn(name="accountColumnName", insertable=false,updatable=false),
And Add two more fields in Settings Entity for these tow column and Map with same Column
Related
I have a doubt about how the modeling of my entity would be. Come on, I have a table in the database that serves to save documents from my system, this table has the columns id, fk_id (element foreign key), fk_table (entity name) and file_name (stores the name of my file) .
I did a lot of research before posting my question here, but I didn't find anything related to it, what would my entities, user, patient and doctor?
DB:
id
fk_id
fk_table
file_name
1
21
user
test1.jpg
2
32
doctor
test2.pdf
3
61
user
test10.pdf
4
100
patient
test5.jpg
Class:
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String LastName;
// What would a one-to-many relationship look like?
}
public class patient{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
// What would a one-to-many relationship look like?
}
You can use #Where. But be aware that #Where is a Hibernate annotation. It's not in the JPA standard.
For example in the User entity: (I assume that your table is mapped to an entity called Document)
#Where( clause = "fk_table = 'user'")
#JoinColumn(name = "fk_id")
#OneToMany
private List<Document> documents = new ArrayList<>( );
The following is based only on standard JPA annotations. The idea is to create an inheritance hierarchy for the documents table. The base is:
#Entity
#Table(name = "XX_DOCUMENT")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "fk_table")
public abstract class BaseDocument {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#Column(name = "file_name")
private String fileName;
}
Here we define that all entities extending this will go to the same table, with the fk_table column to discriminate. The entities extending it are defined as follows:
#Entity
#DiscriminatorValue("doctor")
public class DoctorDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Doctor doctor;
}
#Entity
#DiscriminatorValue("patient")
public class PatientDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Patient patient;
}
// and so on
The interesting thing is that we are reusing the column fk_id to point to the right table. From a small experiment, Hibernate seems to not have problems with it. I would suggest that you manage the DB creation another way just to be safe.
The Doctor, Patient etc need not have a common base class, e.g.:
#Entity
#Table(name = "XX_DOCTOR")
public class Doctor {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "doctor")
private Collection<DoctorDocument> documents = new ArrayList<>();
// any doctor-specific fields
}
#Entity
#Table(name = "XX_PATIENT")
public class Patient {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "patient")
private Collection<PatientDocument> documents = new ArrayList<>();
// any patient-specific fields
}
// and so on
You can read a (doctor, patient, ...)'s documents from the relevant collection. You can even query BaseDocument instances based on any criteria.
You can even go ahead and do more fabcy stuff with the Java code. E.g. define an interface HasDocuments:
public interface HasDocuments<D extends BaseDocument> {
Collection<D> getDocuments();
}
Doctor, Patient, ..., implements this, so they can all be treated the same way.
I have an entity User, that can have exactly one Company. I have a Company, that can be assigned to multiple User objects.
Currently if I want to persist a User, I need to get the Company (as it may exist without any User being assigned to it) and assign it. Further more I have to add the User manually to the Company using Company#addUser. Afterwards I save run CompanyRepository.save(company) (which should suffice to persist the User, too, I think, because I am using cascade = CascadeType.PERSIST).
Is there a way to say, that if I get the User and assign a Company to it, the "back-reference" is dealt with automatically? Or do I always have to get the Company and use Company#addUser to add that reference?
My entities look like this (I omitted more properties and reduced it to the most important properties and methods):
Company.java
package com.portal.user.persistence;
(imports omitted)
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder (toBuilder = true)
#Entity
#Table (name = "companies")
public class Company {
#Id
#GeneratedValue (generator = "uuid")
#GenericGenerator (name = "uuid", strategy = "uuid2")
#Column (name = "id")
private String id;
#Column (name = "ucid")
private String ucid;
#OneToMany (fetch = FetchType.LAZY, mappedBy = "company", cascade = CascadeType.PERSIST)
private List<User> users;
public void addUser(#NonNull User user) {
if (users == null) {
users = new ArrayList<>();
}
users.add(user);
}
public void removeUser(#NonNull User user) {
users.remove(user);
}
}
User.java
package com.portal.user.persistence;
(imports omitted)
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder (toBuilder = true)
#Entity
#Table (name = "users")
public class User {
#Id
#GeneratedValue (generator = "uuid")
#GenericGenerator (name = "uuid", strategy = "uuid2")
#Column (name = "id")
private String id;
#ManyToOne (cascade = CascadeType.PERSIST)
private Company company;
}
There are a lot of answers to your question, based on the implementation you would like to achieve.
The first way is to remove the #OneToMany relation in Company and the user list. In this way you would only have to manage one side of the relation, and when you need to search for all users in a company you could use a custom query performing a left join on users and companies tables.
The second way, keeping both side of the relation, is to implement a method 'setCompany' inside the User class like the following:
public void setCompany(Company c){
c.addUser(this);
this.company = c;
}
However in my experience, the first solution fits better since less relations will lead to a lot less work to do later on, especially regarding DTO conversion and deletion of elements from the DB.
I have 2 entities:
#Data
#Entity
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Table(name = "source_company")
public class SourceCompany {
#Id
#EqualsAndHashCode.Include
private UUID id;
private String name;
#OneToMany( mappedBy = "company")
private final Set<SourceUser> users = new HashSet<>();
#Column(name = "version")
#Version
private Long version;
}
#Data
#Entity
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Table(name = "source_user")
public class SourceUser {
#Id
#EqualsAndHashCode.Include
private UUID id;
private String name;
#Column(name = "version")
#Version
private Long version;
//ref
#ManyToOne
#JoinColumn(name = "fk_source_company")
private SourceCompany company;
}
Is it correct to save in this way (only 2 save)?
#Test
public void testSourceUserSave() {
SourceCompany sourceCompany= new SourceCompany();
sourceCompany.setName("xxx");
sourceCompany.setId(UUID.fromString("2bf05cbc-d530-11eb-b8bc-0242ac130003"));
SourceUser sourceUser= new SourceUser();
sourceUser.setName("dev-team");
sourceUser.setId(UUID.fromString("4bede7a0-d530-11eb-b8bc-0242ac130003"));
sourceUser.setCompany(sourceCompany);
sourceCompany.getUsers().add(sourceUser);
sourceCompanyRepository.save(sourceCompany);
sourceUserRepository.save(sourceUser);
assertNotNull(sourceUser);
assertEquals(sourceUser.getCompany().getId(), sourceCompany.getId());
assertEquals(sourceCompany.getUsers().stream().findFirst().get().getId(), sourceUser.getId());
}
or I need to save the user (without company) and the company (without user) and after that to update the user with a save and the company (without save because is not the owner) like this (3 save):
#Test
public void testSourceUserSave() {
SourceCompany sourceCompany= new SourceCompany();
sourceCompany.setName("xxx");
sourceCompany.setId(UUID.fromString("2bf05cbc-d530-11eb-b8bc-0242ac130003"));
SourceUser sourceUser= new SourceUser();
sourceUser.setName("dev-team");
sourceUser.setId(UUID.fromString("4bede7a0-d530-11eb-b8bc-0242ac130003"));
sourceUserRepository.save(sourceUser);
sourceCompanyRepository.save(sourceCompany);
sourceUser.setCompany(sourceCompany);
sourceCompany.getUsers().add(sourceUser);
sourceUserRepository.save(sourceUser);
assertNotNull(sourceUser);
assertEquals(sourceUser.getCompany().getId(), sourceCompany.getId());
assertEquals(sourceCompany.getUsers().stream().findFirst().get().getId(), sourceUser.getId());
}
It seems, looking in the db, that the first way works, so in future can I update only the owner side (I mean update and save) and so can I update the not-owner side only in the object without save it again?
Thanks in advance
You usually tend to save only one of the objects. This can be done adding the
#ManyToOne(cascade = CascadeType.PERSIST)
to the mapping annotation. This makes sure that the nested entities get persisted too
You would need to do just:
SourceCompany sourceCompany= new SourceCompany();
sourceCompany.setName("xxx");
sourceCompany.setId(UUID.fromString("2bf05cbc-d530-11eb-b8bc-0242ac130003"));
SourceUser sourceUser= new SourceUser();
sourceUser.setName("dev-team");
sourceUser.setId(UUID.fromString("4bede7a0-d530-11eb-b8bc-0242ac130003"));
sourceUser.setCompany(sourceCompany);
sourceUserRepository.save(sourceUser);
One more thing to note is that the .save method actually returns an entity itself. That entity is the persisted entity just created. Basically if you manage everything within a single transactional method any modification to the persisted entity within that method (transaction) will be applied without calling any save, merge or update method
I suggest reading about the #Transactional annotation
I'm working on an apartment management software and I'm having an issue.
There is two entities I have :
#Entity
public class Tenant extends AbstractEntity { //AbstractEntity contains the id
#Column(nullable = false)
private int number;
#OneToOne
private Apartment apartment;
}
and
#Entity
public class Apartment extends AbstractEntity { //AbstractEntity contains the id
#Column(nullable = false)
private int number;
#OneToOne
private Tenant tenant;
}
But when I do
EntityManager em = emProvider.get();
em.getTransaction().begin();
em.merge(apartment);
em.flush();
em.getTransaction().commit();
It only save the Tenant into the Apartment but I would like it also update the Apartment into the Tenant.
Do I really need to set the apartment field into the tenant or there is a way to fix it simply?
Thanks
Cordially,
Baskwo
You need to declare CascadeType.ALL in your Apartment entity. See sample
#OneToOne(cascade = CascadeType.ALL)
private Tenant tenant;
CascadeType.ALL is for all CRUD operation. Adjust CascadeType depends on your application needs.
I have two persistence entity: User and UserDetail. They have one-to-one relationship. I use hibernate annotations. But I am getting in my database several objects of user information for one same user. Apparently my knowledge of Hibernate annotations are not so good to solve this problem.
User class:
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "PASSWORD")
private String password;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private UserDetail userDetail;
// setters and getters
}
UserDetail class:
#Entity
#Table(name = "USER_DETAIL")
public class UserDetail {
#OneToOne
#JoinColumn(name = "USER_ID")
private User user;
// other fields
}
I use this in my code as follows:
UserDetail userDetail = new UserDetail();
userDetail.setInfo(info);
userDetail.setUser(seventhUser);
hibernateTemplate.saveOrUpdate(userDetail);
And everything works properly. Here's what my table USER_DETAIL:
But when I try to change user information, I get an incorrect behavior. I get following table after I again set user information:
UserDetail newUserDetail = new UserDetail();
newUserDetail.setInfo(newInfo);
newUserDetail.setUser(seventhUser);
hibernateTemplate.saveOrUpdate(newUserDetail);
Why the same two objects of information correspond to one user?? I have One-To-One relationship. How can I avoid this? What am I doing wrong?
If you want to modify an existing UserDetail, then you must set its ID, or get it from the session and modify it. Else, Hibernate thinks it's a new one that must be saved, since it doesn't have any ID.
UserDetail existingUserDetail = session.get(UserDetail.class, theUserDetailId);
existingUserDetail.setInfo(newInfo);
To make sure you don't save two UserDetail instances for the same user, you should add a unique constraint on the USER_ID column of the UserDetail database table.