One to many - many to one relationship in Spring JPA - java

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.

Related

How to retrieve ONE column from another table with Foreign key using Hibernate / JPA

I would like to use the Foreign key "MODEL_ID" to retrieve just one column "MODEL_NAME" from the TT_CARS table,
I tried the following code, that works but it returns the whole CARS object.
#JoinColumn(name = "MODEL_ID", referencedColumnName = "ID")
#ManyToOne(fetch = FetchType.EAGER)
private CARS cars;
Also I tried the code below, its also not working
#SecondaryTable(name = "TT_CARS", pkJoinColumns = #PrimaryKeyJoinColumn(name = "ID", referencedColumnName="MODEL_ID"))
Is there other way to retieve just the column (MODEL_NAME) using hibernate and JPA??
remarks: The modelName should be part of the Options class.
my code
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
#Entity
#Table(name = "TT_OPTIONS")
public class Options {
#Id
#Column(name = "ID")
private String id;
#NotNull
#Column(name = "DESCRIPTION", nullable = false)
private String description;
#Column(name = "MODEL_ID") // Foreign key
private Long modelId;
#Column(name = "MODEL_NAME", table = "TT_CARS") // this is the column name I would like to retrieve from the TT_CARS table
private String modelName;
// getters and setters
}
You can use #Formula. It is read-only calculated column that can be retrieved by the custom subquery. It does not present in the target table.
Defines a formula (derived value) which is a SQL fragment that acts as
a #Column alternative in most cases. Represents read-only state.
Example:
#Entity
#Table(name = "TT_OPTIONS")
public class Options {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "DESCRIPTION", nullable = false)
private String description;
#Column(name = "MODEL_ID")
private Long modelId;
#Formula("(select TT_CARS.MODEL_NAME from TT_CARS where TT_CARS.ID = MODEL_ID)")
private String modelNameFormula;
}
#Entity
#Table(name = "TT_CARS")
public class Cars {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "MODEL_NAME")
private String modelName;
}
Hibernate generated native query:
select
options0_.id as id1_4_0_,
options0_.description as descript2_4_0_,
options0_.model_id as model_id3_4_0_,
(select
TT_CARS.MODEL_NAME
from
TT_CARS
where
TT_CARS.ID = options0_.MODEL_ID) as formula1_0_
from
tt_options options0_
where
options0_.id=?
#SecondaryTable designed for #OneToOne relationship to map multiple tables to the same entity. It will not work for the #ManyToOne relationship.

Java Spring Data JPA Composite ID with foreign key

I am making a Spring web service to learn more about it and I am currently mapping the database. I have a table that has a composite ID, where one of the ID's is a foreign key to another table (ManytoOne).
Creditors
Creditor_Invoices
ID
Creditor_ID
name
Invoice_ID
As anywhere you buy something they use their own way of making ID's it has a composite ID like this.
My Current code:
Serializable class CInvoiceId:
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
import java.util.Objects;
#Embeddable
public class CInvoiceId implements Serializable {
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
private Creditor cInvoiceCreditorId;
#Column(name = "invoice_id", nullable = false)
private String cInvoiceId;
public CInvoiceId(Creditor creditor, String cInvoiceId){
this.cInvoiceCreditorId = creditor;
this.cInvoiceId = cInvoiceId;
}
//Setters, Getters, Equals and Hash
}
My Creditor class
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int creditorId;
#Column(name = "name",nullable = false)
private String creditorName;
#OneToMany(mappedBy = "cInvoiceCreditorId")
private List<CInvoice> cInvoices;
}
My CInvoice class:
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId cInvoiceID;
}
When I start it to try and test it I get the error that it can not find the mapped by from the creditor class, but I don't know what I should map it to as the ID is now made in the CInvoiceId class. What should it be?
Regards
Dany
You can use "derived identities" to map these classes:
Creditor:
#Entity
#Table(name = "creditors")
public class Creditor {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name",nullable = false)
private String name;
#OneToMany(mappedBy = "creditor")
private List<CInvoice> invoices;
}
CInvoiceId:
#Embeddable
public class CInvoiceId implements Serializable {
#Column(name = "invoice_id", nullable = false)
private String invoiceID;
private int creditorID; // corresponds to PK type of Creditor
// ...
}
CInvoice:
#Entity
#Table(name = "c_invoices")
public class CInvoice {
#EmbeddedId
private CInvoiceId id;
#MapsId("creditorID") // maps creditorID attribute of embedded id
#ManyToOne
#JoinColumn(name = "creditors_id", nullable = false)
Creditor creditor;
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.

Hibernate/Postgres: duplicate key value violates unique constraint

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;

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

Extra column being created when I use same column name for two different tables connected through ManyToOne relationship

I have SpringBoot project with Spring Data. Extra column being created when I use the same column name for 2 tables mapped using ManyToOne Relation. I have Address table with column name 'id' and AddressType table with column 'id'. When I start the Spring Boot application with this, I see an extra column (address_id) created on Address table.
Address.java
package com.springtesting.model;
import lombok.Data;
import javax.persistence.*;
#Entity
#Data
#Table(name = "address")
public class Address
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "street_name")
private String streetName;
#Column(name = "apartment")
private String apartment;
#Column(name = "city")
private String city;
#Column(name = "state")
private String state;
#Column(name = "country")
private String country;
#Column(name = "zip_code")
private String zipCode;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address_type_id")
private AddressType addressType;
public Address()
{
}
public Address(String streetName, String apartment, String city, String state, String country, String zipCode, AddressType addressType)
{
this.streetName = streetName;
this.apartment = apartment;
this.city = city;
this.state = state;
this.country = country;
this.zipCode = zipCode;
this.addressType = addressType;
}
}
and
AddressType.java
package com.springtesting.model;
import lombok.Data;
import lombok.NonNull;
import javax.persistence.*;
#Entity
#Data
#Table(name = "address_type")
public class AddressType
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "type")
#NonNull
private String type;
public AddressType()
{
}
public AddressType(String type)
{
}
}
when I start the Spring Boot application, I see extra column (address_id)
Table Structure on Mysql
I'm not sure this will fix your issue, but try changing the #GeneratedValue(strategy = GenerationType.AUTO) to #GeneratedValue(strategy = GenerationType.IDENTITY), or give it a "native" GenericGenerator, as discussed here. Apparently GenerationType.AUTO does not play nice with MySQL from Hibernate 5+.
A couple of other notes:
You probably don't want to use Lombok's #NonNull, instead just use #Column(name = "type", nullable = false). In fact if the POJO is created through the default constructor, Lombok's #NonNull wouldn't do anything anyway since it's annotated over a field.
You don't need the cascade = CascadeType.ALL above the AddressType since you probably don't want to accidentally update/delete/or create new AddressTypes through the Address entity. Just annotating it #ManyToOne should be enough.

Categories

Resources