Hibernate/Postgres: duplicate key value violates unique constraint - java

I have problem with adding users role during registration request
This is my DB diagram:
And I have the following classes: first is entity role:
package application.model;
import lombok.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.persistence.*;
import java.util.List;
#NamedQueries({
#NamedQuery(name = User.GET_USERS, query = User.QUERY_GET_USERS),
})
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "users")
public class User {
public static final String GET_USERS = "User.get_users";
public static final String QUERY_GET_USERS = "select u from User u";
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
public int id;
#NotNull
#NotEmpty
#Column(name="firstname")
private String firstname;
#NotNull
#Column(name="lastname")
private String lastname;
#NotNull
#Column(name="email")
private String email;
#NotNull
#Column(name="password")
private String password;
#JoinTable
#OneToMany
private List<Role> roles;
}
second entity is Role:
package application.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "role")
public class Role {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public int id;
#NotNull
#Column(name="name")
private String name;
}
So, i have 3 roles in my application, when I register new user with post request:
{
"firstname": "imasdie5",
"lastname": "nazasdwisko5",
"email": "masdil",
"password": "pass",
"roles": [
{ "id":2 }
]
}
First user is registered correctly, but when i send second request with same roles.id=2, i have:
ERROR: duplicate key value violates unique constraint "uk_d9najy24fium4vkivgwjuf0hw"
Detail: Key (roles_id)=(2) already exists.
In dbeaver table users_role have constraint uk_d9najy24fium4vkivgwjuf0hw with type UNIQUE_KEY, so that's the problem, but how to change type to non-unique? Many users may have same role, so it is necessary for me
All tables are generated with hibernate.
Table users_role is not entity in my application, maybe it should be entity?
Do You have any advice what I should change to add one role for many users?

Try to use #ManyToMany annotation
#JoinTable
#ManyToMany
private List<Role> roles;

Related

One to many - many to one relationship in Spring JPA

I have this Coupon Class
package Kinn.College.CouponManagementSystem.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table (name = "companies")
public class Company {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column (name = "company_id")
private int id; // id of the company
#Column (name = "company_name")
private String name; // name of the company
#Column (name = "company_email")
private String email; // email of the company
#Column (name = "company_password")
private String password; // password of the company
#OneToMany(mappedBy = "company_id", cascade = CascadeType.ALL)
private List<Coupon> coupons;
}
and I have this company class
package Kinn.College.CouponManagementSystem.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table (name = "companies")
public class Company {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column (name = "company_id")
private int id; // id of the company
#Column (name = "company_name")
private String name; // name of the company
#Column (name = "company_email")
private String email; // email of the company
#Column (name = "company_password")
private String password; // password of the company
#OneToMany(mappedBy = "company_id", cascade = CascadeType.ALL)
private List<Coupon> coupons;
}
for some reason, the one to many - many to one relationship creates an error when I try to get a company from the DB with a list of coupons.
I'm using this syntax to get a copmany from DB.
{
Company company = companyService.getCompanyById(1);
System.out.println("Got company by id: " + company);
}
if I'm removing the list of coupons from every company, it works just fine.
This is the error message;
2023-01-16T11:59:33.266+02:00 ERROR 16808 --- [ main] j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA EntityManagerFactory: Collection 'Kinn.College.CouponManagementSystem.entities.Company.coupons' is 'mappedBy' a property named 'company_id' which does not exist in the target entity 'Kinn.College.CouponManagementSystem.entities.Coupon'
I've tried asking multiple people know Spring very well and all of them say it looks okay, that it should work.
mapped_by reference the property or the referenced class, not the column name:
#OneToMany(mappedBy = "id", cascade = CascadeType.ALL)
private List<Coupon> coupons;
BTW: Take care of java naming conventions. Package names should only contains lower case character
I am assuming that a company can relate to many coupons.
So in Coupon class, you can have a Company member which represents the company it belongs to :
#ManyToOne
#JoinColumn(name = "company_id")
private Company company;
Here, company_id would become the foreign key that points to primary key of company table
And in Company class, you can have List of coupons that it owns:
#OneToMany(mappedBy = "company", orphanRemoval = true)
private List<Coupon> coupons = new ArrayList<>();
The Company entity should have a field to point to all its coupons as shown below:
#OneToMany(mappedBy="company")
private Set<Coupon> coupons;
The mappedBy field is the instance variable name we'll use in Coupon entity to point to the associated Company.
The Coupon entity should have a field to point to all its coupons like:
#ManyToOne
#JoinColumn(name="company_id", nullable=false)
private Company company;
The name field refers to the name of the foreign key in the Coupon table that points to the Company table.
You can check this article https://www.baeldung.com/hibernate-one-to-many.

Java set only displays 1st element

I have the following code, where I need to populate a set with multiple objects that are instances of the same class (User). The problem is, I only get the first object when I log.
user = User.builder()
.id(1L)
.username("2397047")
.nickname("test1")
.build();
anotherUser = User.builder()
.id(2L)
.username("23971948")
.nickname("test2")
.build();
Set<User> userSet = new HashSet<>();
userSet.add(user);
userSet.add(anotherUser);
System.out.println("User set from test " + userSet);
This code produces the following output
User set from test [User(id=1, nickname=test1, username= 2397047, password=null, roles=null, groups=null)]
Why am I unable to get the entire collection?
This is my User class
package com.chama.chamaservice.user;
import com.chama.chamaservice.BaseEntity;
import com.chama.chamaservice.Views;
import com.chama.chamaservice.config.ApplicationUserRole;
import com.chama.chamaservice.group.Group;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.*;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
public class User extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(Views.Public.class)
private Long id;
#JsonView(Views.Public.class)
private String nickname;
#JsonView(Views.Public.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
private Set<ApplicationUserRole> roles;
#JsonIgnore
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
private Set<Group> groups = new HashSet<>();
}
In the User class, #Data annotation which will implement #Getter, #Setter, #ToString method.
It will print all values in the Set.
Found an answer, although it may not be the optimal solution. I annotated the User class with #EqualsAndHashCode

Hibernate/JPA: foreign key = null

below is my 2 entities:
package dev.proj.project.application.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.List;
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "user")
public class User {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
public int id;
#NotNull
#Column(name="firstname")
private String firstname;
#NotNull
#Column(name="lastname")
private String lastname;
#NotNull
#Column(name="email")
private String email;
#NotNull
#Column(name="password")
private String password;
#JsonIgnore
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<Address> address;
}
package dev.proj.project.application.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.List;
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "address")
public class Address {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private int id;
#NotNull
#Column(name="street")
private String street;
#NotNull
#Column(name="house")
private String house;
#NotNull
#Column(name="flat")
private String flat;
#NotNull
#Column(name="code")
private String code;
#NotNull
#Column(name="city")
private String city;
#ManyToOne(optional = false)
#JoinColumn(name = "user_id",nullable = false,updatable = true,insertable = true)
private User user;
}
I want to create User->Address One-to-many relationship, but if I made post request to /address:
{
"street": "ulicanewnew",
"house": "5",
"flat": "2",
"code": "20-001",
"city": "warsaw",
"user_id": 1
}
user_id is null in my address table:
Why foreign key in address table is always null?
Do you see what is wrong here?
With direct sql inserts to db - everything works fine
I changed request to:
{
"street": "dyr",
"house": "5",
"flat": "2",
"code": "20-001",
"city": "warsaw",
"user": {
"id": 50
}
}
and it works, but question for now: how to use
"user_id" : 50
in my request, instead of
"user": {
"id": 50
}
?
Try adding below changes to your address entity.
#JoinColumn(name = "user_id")
#ManyToOne(targetEntity = User.class, fetch = FetchType.LAZY)
private User user;
import java.util.List;
import java.util.Set;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
#Entity
#Table(name = "customer")
public class Customer {
...
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<DatabaseTable> databaseTables;
...
}
...
...
import org.hibernate.validator.constraints.Length;
import javax.persistence.*;
#Entity
#Table(name = "databasetable")
public class DatabaseTable {
...
#ManyToOne
#JoinColumn(name = "user_idFK")
private Customer customer;
....
}
Something like that works for me.

How to prevent JPA deleteByID() from deleting parent when deleting a child?

I've got a spring mvc application in which users book meeting rooms. When I try to delete a booking by using deleteById(), the user that belongs to the booking is also deleted. How do I prevent this?
Booking object:
package spe_booker.models;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.Date;
#Entity
public class Booking {
#Id
#GeneratedValue
private Long id;
#NotNull
private Date startDateTime;
#NotNull
private Date endDateTime;
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private Room room;
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private User user;
private Date creationDate;
public Booking() { }
...getters and setters...
User object:
package spe_booker.models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue
private Long id;
private String username;
String name;
private String password;
private String faculty;
private String role;
private Boolean blacklisted;
#Column(name = "enabled")
public int enabled;
#OneToMany(mappedBy = "user")
private List<Booking> bookings = new ArrayList<>();
...getters and setters...
Booking repository:
package spe_booker.Repositorys;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import spe_booker.models.Booking;
import spe_booker.models.User;
import java.util.List;
#Repository
public interface BookingRepository extends CrudRepository<Booking, Long> {
List<Booking> findAll();
List<Booking> findBookingsByUser(User user);
}
The delete call:
bookingRepository.deleteById(id);
Change the cascade of
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private User user;
to the appropriate values. Do you change a user and then save a booking and a user at the same time? Do you create bookings with users at the same time? If you can answer these questions with "no", you might just remove (cascade = CascadeType.ALL) at all. And you should ask yourself why the cascades are present in the first place!
CascadeType.ALL forces JPA to cascade all operations (persisting, removing, updating....). That means when you delete/update a booking, it willl delete/update the Room and User objects too

Auto updating of the databases creates one-directional connection on the wrong side

The problem is that when i run the application hibernate creates a column user_id in the tahograph_cards table which i don't want and it doesn't create a column tahograph_card_id in the users table which I want to.
It's a OneToMany relationship.
Here are my files:
application.properties:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/getmydrivercard
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto = update
User:
package com.getmydrivercard.getmydrivercard.models;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
#Entity
#Table(name = "users")
public class User {
private static final String EMPTY_STRING = "";
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
private UUID userId;
#NotEmpty
private String username;
#NotEmpty
private String password;
#NotEmpty
private String email;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Set<TahographCard> tahographCards;
private String comments;
protected User(){}
public User(String username, String password, String email){
setUsername(username);
setPassword(password);
setEmail(email);
setComments(EMPTY_STRING);
setTahographCards();
}
//getters and setters
}
TahographCard:
package com.getmydrivercard.getmydrivercard.models;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import java.util.UUID;
#Entity
#Table(name = "tahograph_cards")
public class TahographCard {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
private UUID tahographCardId;
#NotEmpty
private Boolean isActive;
#NotEmpty
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userId")
private User user;
protected TahographCard(){}
public TahographCard(User user){
setUser(user);
setActive(true);
}
//getters and setters
}
I tried with mappedBy and some other stuff I found on the Internet but still doesn't work. Thank you in advance.
The problem here is not the entities configuration but the database design.
You can't map a oneToMany relationship with a FK column on the one side.
For example if you have 2 Users and 4 TahographCard (2 for each user) normally it would look like this:
User
id name
1 Foo
2 Bar
Table
id user_id (fk)
1 1
2 1
2 2
2 2
The opposite with the TahographCardId on the User table is impossible

Categories

Resources