Hibernate one-to-one mapping on another entity acting as primary key - java

So consider the following 2 tables:
table: User
id (pk)
...
and table UserProfile:
UserProfile
user_id(pk, and fk from User.id. fk is named profile_user_fk)
...
given this tables, I have the entity classes:
#Entity
#Table(name="User")
public class User implements Serializable {
private int id;
private UserProfile profile;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, unique = true)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#OneToOne(mappedBy = "user")
public UserProfile getProfie() {
return profile;
}
public void setProfile(UserProfile p) {
profile = p;
}
...
}
And the User class:
#Entity
#Table(name="UserProfile")
public class UserProfile implements Serializable {
private User user;
#OneToOne
#PrimaryKeyJoinColumn(name="profile_user_fk")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
...
}
I don't want to add an extra column in UserProfile because I think this column is meaningless. User.id should be sufficient to indicate the identity of UserProfile records.
So I supposed the #OneToOne annotations will tell hibernate user is a pk and also fk and should refer to User.id for its own id; however executing the code shows:
org.hibernate.AnnotationException: No identifier specified for entity: xxx.UserProfile
Apparently what I thought was wrong - but I've no idea what can I do to fix it without altering the schema.
Any helps please. Thanks!

The error
No identifier specified for entity: xxx.UserProfile
says
In your Entity class (UserProfile), you have not defined a primary key. You must specify
either #Id annotation or an #EmbeddedId annotation. Bcoz, every class defined as Entity
with #Entity annotation, needs an #Id or #EmbeddedId property.
Modify your UserProfile class as below :-
#Entity
#Table(name="UserProfile")
public class UserProfile implements Serializable {
private long uProfileId;
private User user;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "uProfileId", nullable = false, unique = true)
public long getUProfileId() {
return uProfileId;
}
public void setUProfileId(long uProfileId) {
this.uProfileId = uProfileId;
}
#OneToOne
#PrimaryKeyJoinColumn(name="profile_user_fk")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
...
}
Read: #Entity & #Id and #GeneratedValue Annotations
Entity Class without Primary Key :
If you don't want to add primary key in UserProfile table then you can use #Embedded & #Embeddable annotations or <component> tag in xml mapping. For more explanation on this look at below posts:-
Using <component> or #Embedded & #Embeddable annotations
Hibernate and no PK
Hibernate/persistence without #Id

Related

How to save entities in table with unique constraint

I work with Spring Boot and have 2 PostgreSQL tables: USERS and CITIES. FOREIGN KEY (USERS.city_id) REFERENCES CITIES (id). CITIES has an unique constraint for city name field. I receive an object in the #PostMapping method of the controller and try to save it via service layer. All is fine while I don't send an object with same city name field, and I don't know how to solve it. Postman JSON example:
*1st attempt*
{
"name": "JHON",
"city": {
"name": **"MOSCOW"**
}
} ---> ALL OK
*2nd attempt*
{
"name": "TOM",
"city": {
"name": **"MOSCOW"**
}
}--->**org.postgresql.util.PSQLException: ERROR: Duplicate key value violates unique constraint "cities_name_key"
Details: The key "(name) = (MOSCOW)" already exists.**
Entity:
#Entity
#Table(name = "cities")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
}
#Entity
#Data
#Accessors(chain = true)
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "city_id")
private City city;
}
Tables:
cities
id SERIAL PRIMARY KEY NOT NULL,
name TEXT UNIQUE
users
id SERIAL PRIMARY KEY NOT NULL,
name VARCHAR(10),
city_id INT,
CONSTRAINT users_cities_id_fk FOREIGN KEY (city_id) REFERENCES cities (id)
and Service
#Service
#RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public User getUser(Long id){
return userRepository.findById(id).get();
}
public User saveUser(User user){
return userRepository.save(user);
}
}
Ok, I found an answer for question.
So if you have fields with unique constraints in the your tables, you have to define a constructor inside the entity for the primary key field. And don't hope that Lombok will do it. It means that annotation like #AllArgsConstructor does not help for this case.
#Entity
#Table(name = "cities")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
//add this *********************
public City(Long id) {
this.id = id;
}
}

JPA how to make composite Foreign Keys from two different table part of composite Primary Key of a single table

Following is the structure of my table:
Table Campaign{
Camp_id(PK),
other columns...
}
Table User{
user_id(PK),
other columns...
}
Table Candidate{
cand_id(PK),
other columns...
}
Table Result{
user_id(PK),
camp_id(PK),
cand_id
}
try something like that:
entity TableCampaign
#Entity
#Table(name="CAMPAIGN")
public class Campaign implements Serializable {
#GeneratedValue(...)
#Id
#Column(name="CAMP_ID")
Long campId
....
}
entity User
#Entity
#Table(name="USER")
public class User implements Serializable {
#GeneratedValue(...)
#Id
#Column(name="USER_ID")
Long userId
....
}
entity Candidate
#Entity
#Table(name="CANDIDATE")
public class Candidate implements Serializable {
#GeneratedValue(...)
#Id
#Column(name="CAND_ID")
Long candId
....
}
entity Result
#Entity
#Table(name="RESULT")
public class Result implements Serializable {
#EmbeddedId
private ResultId id;
#ManyToOne
#JoinColumn(name = "CAND_ID")
private Candidate candidate;
...
}
#Embeddable
public class ResultId implements Serializable {
#ManyToOne
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne
#JoinColumn(name = "CAMP_ID")
private Campaign campaign;
}

Problem in understanding mapping in Java Spring boot

I have just started with Spring boot.
I have 3 tables User, UserNotes, and Notes.
User ( userID, EmailID)
UserNotes (UserID, NotesID)
Notes (notesID, Title, Message )
When I add an email, UserID has to be autogenerated. And when I add any note corresponding to the user, Notes Id in UserNotes table must be autogenerated and it must be added to Notes table.
I have done it in MySQL by making userID in User table as primary key and UserID in UserNotes as foreign key referencing to it. Similarly NotesID in UserNotes as primary key and notesID in Notes table as a foreign key for that.
Everything is working fine when I am using MySQL queries for this. But now I want to use Spring Data JPA for this. But I am facing difficulty in understanding how to map these relationships having multiple tables. I have tried OneToMany and ManyToOne relationship but it didn't work out
MySQL Queries for the reference
1) Create table User(userID int AUTO_INCREMENT, emailID varchar(40), PRIMARY KEY(userID));
2) Create table UserNotes(userID int, NotesID int AUTO_INCREMENT, PRIMARY KEY(NotesID), Foreign key(UserID) references User(UserID) on DELETE CASCADE);
3) Create table Notes(notesID int, title varchar(100), message varchar(500), Date date, PRIMARY KEY(notesID), FOREIGN KEY(notesID) references UserNotes(NotesID) on DELETE CASCADE);
An untested example using Hibernate, JPA, Lombok:
User entity
#Entity
#Table(name = "USER")
#SequenceGenerator(name = "userSeqId", sequenceName = "user_seq_id", allocationSize = 1)
#NoArgsConstructor
#Setter
#Getter
public class User {
#Id
#GeneratedValue(generator = "userSeqId", strategy = GenerationType.AUTO)
private Long id;
#NotBlank
#Column(name = "EMAIL", unique = true)
private String email;
}
Notes entity
#Entity
#Table(name = "NOTES")
#SequenceGenerator(name = "notesSeqId", sequenceName = "notes_seq_id", allocationSize = 1)
#NoArgsConstructor
#Setter
#Getter
public class Notes {
#Id
#GeneratedValue(generator = "notesSeqId", strategy = GenerationType.AUTO)
private Long id;
#NotBlank
#Column(name = "TITLE")
private String title;
#NotBlank
#Column(name = "MESSAGE")
private String message;
}
UserNotes entity
#Entity
#Table(name = "USER_NOTES")
#NoArgsConstructor
#Setter
#Getter
public class UserNotes {
#EmbeddedId
private UserNotesKey id;
#NotNull
#ManyToOne
#MapsId("USER_ID")
#JoinColumn(name = "USER_ID")
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
#NotNull
#ManyToOne(cascade = CascadeType.PERSIST)
#MapsId("NOTES_ID")
#JoinColumn(name = "NOTES_ID")
#OnDelete(action = OnDeleteAction.CASCADE)
private Notes notes;
}
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
#Setter
#Getter
#EqualsAndHashCode
public class UserNotesKey implements Serializable {
#Column(name = "USER_ID")
private Long userId;
#Column(name = "NOTES_ID")
private Long notesId;
}
Repositories
public interface UserRepository extends JpaRepository<User, Long> {
}
public interface NotesRepository extends JpaRepository<Notes, Long> {
}
public interface UserNotesRepository extends JpaRepository<UserNotes, UserNotesKey> {
List<UserNotes> findByUser(User user);
}
Test service
#Service
#Transactional
#RequiredArgsConstructor
public class TestService {
private final UserRepository userRepository;
private final UserNotesRepository userNotesRepository;
public User saveUser(User user) {
User newUser = new User();
user.setEmail(user.getEmail());
return userRepository.save(user);
}
public UserNotes saveNotes(User user, Notes notes) {
UserNotes userNotes = new UserNotes();
userNotes.setUser(user);
userNotes.setNotes(notes);
return userNotesRepository.save(userNotes);
}
public List<Notes> getNotes(User user) {
return userNotesRepository.findByUser(user)
.stream()
.map(UserNotes::getNotes)
.collect(Collectors.toList());
}
}

JPA retrieve entities relationships

I am trying to retrieve the list of all entities containing relations that references the specified entity.
"Role" Entity :
#Entity
#Table(name="Role")
#Access(AccessType.PROPERTY)
public class Role implements Serializable {
private String description;
private Long roleId;
#Column(name = "role_id")
#Id
public Long getRoleId() {}
#Column(name = "description")
public String getDescrition() {}
}
"User" Entity :
#Entity
#Table(name="users")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long userId;
private String name;
private Role role;
#Column(name = "user_id")
#Id
public Long getUserId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
public String getName() {}
}
"Group" Entity :
#Entity
#Table(name="groups")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long groupId;
private String description;
private Role role;
#Column(name = "group_id")
#Id
public Long getGroupId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
public String getDescription() {}
}
I need a that works like this :
List<Class<?>> entities = getEntityReferences(Role.class);
foreach (Class<?> entity : entities )
System.out.println(entity.getName());
The output would be :
User
Group
I think JPA use something like this for the bean validations or cascade mechanics but I can't find a simple way to achieve this.
The only way I found by now is to iterate through all the annotations of all the entities (returned by "entityManagerFactory.getMetamodel().getEntities()") to look for relations (ManyToOne, OneToOne ect.). It seems a bit tedious, I'm sure there's a better way to do it...
Thank you all...

Remove redundant column for composite key in Hibernate

Hibernate creates empty "ID" column in case of code like in this post.
How tune it to not create "ID" column ("ID" is exact name of created column) or this can not be changed?
#Entity
#Table(name = "CATEGORY_RELATIONS")
public class CategoryRelations implements Serializable {
private CategoryRelationsPrimaryKey id;
#Id
#Column(name = "CATEGORY_RELATIONS_CATEGORY_ID")
private String categoryId;
#Id
#Column(name = "CATEGORY_RELATIONS_PARENT_ID")
private String parentId;
//getters and setters
#Entity
#IdClass(CategoryRelationsPrimaryKey.class)
public class CategoryRelationsPrimaryKey implements Serializable {
protected long categoryId;
protected long parentId;
//euqals, hashCode
}
}
1) #IdClass should stand at entity, not at composite id class;
2) If you already marked id properties by #Id, no separate id property is required:
#Entity
#Table(name = "CATEGORY_RELATIONS")
#IdClass(CategoryRelationsPrimaryKey.class)
public class CategoryRelations implements Serializable {
#Id
#Column(name = "CATEGORY_RELATIONS_CATEGORY_ID")
private String categoryId;
#Id
#Column(name = "CATEGORY_RELATIONS_PARENT_ID")
private String parentId;
//...
}
public class CategoryRelationsPrimaryKey implements Serializable {
protected String categoryId;
protected String parentId;
// ...
}
If you need some property named id, make it transient to avoid mapping to a DB table column.

Categories

Resources