ManyToOne realtionship in Java Spring Boot - java

Well, I have a class Feeding.java and a class User.java
One Feeding should have one User but a User can have many Feedings.
This is my Feeding.java class:
#Getter
#Setter
#EqualsAndHashCode
#NoArgsConstructor
#Entity
#Table(name = "tblFeeding")
public class Feeding {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idFeeding;
private LocalDateTime dateFeeding;
private double amountFeeding;
private String foodFeeding;
#ManyToOne
#JoinColumn(name = "id_user")
private User user;
}
This is my User.java class:
#Entity
#Getter
#Setter
#Table(name = "tbl_User")
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idUser;
private String nameUser;
private String email;
private String passwordUser;
The problem at the moment is that I can save feeding object with an userId that doesn't exist and this shouldn't be possible.

A foreign constraint is missing. If you have created the table with Hibernate ORM it shouldn't happen but if you've created the table in a different way, it's possible that the foreign constraint has not been created.
You would need to run a SQL query similar to this one:
ALTER TABLE Feeding
ADD CONSTRAINT id_user_fk
FOREIGN KEY (id_user) REFERENCES User;
Note that the query might be different, you need to check the exact query for your database.
This query is from the Hibernate ORM documentation example for many-to-one associations.

Related

Hibernate JPA Mapping Multiple Entities of the Same Type

I have a one-to-many relationship with Customer and Address, but I'm not sure how to represent this with JPA. I don't want use #OneToMany for the AddressEntity's in CustomerEntity because I want to avoid wrapping it in a Collection.
I'm wondering what annotation or even other strategies I can use to maintain the relationship where one customer will, for simplicity, always have two addresses. Any suggestions are appreciated!
Address Entity
#Data
#NoArgsConstructor
#Entity(name = "address")
public class AddressEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#?
private CustomerEntity customer;
}
Customer Entity
#Data
#NoArgsConstructor
#Entity(name = "customer")
public class CustomerEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#?
private AddressEntity shippingAddress;
#?
private AddressEntity billingAddress;
}
For the case when an address can belong different customers.
#Entity
public class AddressEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
#Entity
public class CustomerEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private AddressEntity shippingAddress;
#ManyToOne
private AddressEntity billingAddress;
}
if each customer has unique address, better to store the addresses in the same customer record.
You can create class EmbeddedAddress and use #Embedded and #Embeddable annotations.
For your exact scenario, I think you could go for #PostLoad.
The steps would be:
use #OneToMany annotation to load the addresses into a collection
annotate both shippingAddress and billingAddress with #Transient
create a public method annotated with #PostLoad
initialise your 2 transient fields (you need to have at least an enum to discriminate between the addresses)
Why would the steps above work?
PostLoad is invoked after an entity is loaded from the database
the fields need to be transient, because they are not mapped to database columns
A relevant example can be found here.
While the approach above would solve your problem, it adds some degree of verbosity in your JPA entities. I would suggest to go for #OneToMany and make sure you add an enum in AddressEntity to check if an address is for shipping or billing.
Also, given that you mentioned that there is a one-to-many relationship between a customer and an address, then there is a many-to-one relationship between an address and a customer. The annotation to use in the AddressEntity class is #ManyToOne

Hibernate throws "NULL not allowed for column" as the JPA OneToOne relation FK is missing in the SQL INSERT when using #MapsId

On my OneToOne relation I am getting:
NULL not allowed for column "USER_ID"; SQL statement:
insert into customer_order (id, enabled, orden_id) values (null, ?, ?).
It is actually null because is not present in the INSERT query. However, the value USER is filled in customerOder entity when perfom save(customerOrder).
#Getter
#SuperBuilder
#MappedSuperclass
#NoArgsConstructor
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public abstract class AbstractEntity {
#Id
#EqualsAndHashCode.Include
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Setter
#Default
#Column(columnDefinition = "BOOLEAN DEFAULT 'true'", nullable = false)
private Boolean enabled = Boolean.TRUE;
}
#Getter
#Setter
#Entity
#Table(name = "customer_order")
#SuperBuilder
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
public class CustomerOrderEntity extends AbstractEntity {
#OneToOne(fetch = FetchType.LAZY)
#MapsId("id")
private UserEntity user;
//other values
}
#Data
#Entity
#Table(name = "user")
#SuperBuilder
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
public class UserEntity extends AbstractEntity {
#NaturalId
#EqualsAndHashCode.Include
#Column(length = 28, unique = true, nullable = false)
private String uuid;
//other values
}
I expect the customerOrder to be persisted in database with the filled data.
Using #MapsId allows you to use the child table Primary Key as a Foreign Key to the parent table Primary Key.
If you enable the hbm2ddl tool, you will see that the customer_order table will not contain the user_id column.
However, since you generated the database schema previously and you have a customer_order table with a dedicated user_id column, then you need to remove #MapsId:
#OneToOne(fetch = FetchType.LAZY)
private UserEntity user;
This way, the user association will use the user_id Foreign Key column.
Your code works exactly as you specified.
If you have a shared key (you use #MapsId), Hibernate won’t use a separate column for the foreign key. This is why the insert query does not contain user_id column.
On top if that, id in CustomerOrderEntity is on one hand auto-generated (as defined in the superclass), and on the other maps id of another entity. These are conflicting requirements.

OneToOne relationship, keep only foreign key

I am trying to establish a OneToOne relationship between two entities (PartnerDetails and JWTData. How ever, I only want to store the primary key of PartnerDetails entity in JWTData, not the whole object, like this.
#Entity
#Data
#Table(name = "partner_details")
public class PartnerDetails {
#Id
#Column(name = "partner_id")
private String partnerId;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "partnerId")
private JWTData jwtData;
}
#Entity
#Data
#Table(name = "jwt_data")
#NoArgsConstructor
public class JWTData {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(targetEntity = PartnerDetails.class)
#JoinColumn(name = "partner_id", foreignKey = #ForeignKey(name = "fk_jwt_partnerdetails_partnerid"))
private String partnerId;
#NotBlank
private String secret;
}
But after fetching the JWTData using repository, Hibernate cannot convert the String to a PartnerDetails. Can this be done using any other way?
If you just add PartnerDetails to JWTData then JPA will know to use only the id. JPA is an Object Oriented framework so you should reference objects unless you specifically want a field. JPA handles the details for you. Note that in this configuration JWTData in the "owning" entity because of the mappedBy annotation, therefore only setting the partnerDetails field in a JWTData instance will persist the relationship to the database. The jwtData field in PartnerDetails is for query results only and makes for a Bidirectional instead of a Unidirectional mapping. Also, because of this, having a CascadeType setting generally only makes sense on the owning entity since it is the one handling the database updates and deletes.
When playing around with JPA be sure to turn on the SQL output so that you know what is actually happening.
#Entity
#Data
#Table(name = "partner_details")
public class PartnerDetails {
#Id
#Column(name = "partner_id")
private String partnerId;
#OneToOne(mappedBy = "partnerDetails")
private JWTData jwtData;
#Entity
#Data
#Table(name = "jwt_data")
#NoArgsConstructor
public class JWTData {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// even though it looks like the entire class it's only saving the id to the database.
#OneToOne
private PartnerDetails partnerDetails;

Hibernate. Several foreign keys in one table

Have next tables structure in SQL schema :Clients, Employees, Orders.
And 3 Entity classes in java code accordingly : Client, Employee, Order.
Both primary id fields from Clients and Employees are in Orders table as foreign keys.
Question is how it should be displayed in java code?
As I understand here it should be done smth like adding Set field to Clients and Employees annotated with #OneToMany.
But what should be done in Order Entity and maybe I have to add any additional annotations except #OneToMany?
I think you have some misconceptions about the relational mapping of Hibernate.
If in fact your Orders table have foreign keys of Clients and Employees, then the annotation you are looking for is #ManyToOne
#OneToMany annotation is used when your entity have multiple records referenced by the targeted entity, while #ManyToOne is used when your entity have only one record referencing the targeted entity.
For example:
Orders entity have one reference from Clients and one reference from Employees entities.
In this case, Orders entity could be mapped by the following way:
#Entity
#Table(name = "Orders")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Client client;
#ManyToOne
private Employee employee;
//getters and setters
}
#Entity
#Table(name = "Clients")
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String attribute1;
//getters and setters
}
#Entity
#Table(name = "Employees")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String attribute1;
//getters and setters
}
With the example given above you should be able to make your schema work fine with Hibernate, but for the sake of understanding, let's imagine a scenario where you would need to get all the Orders from a Client, of course you could do it with a query selecting only the Orders inside the Client table, however Hibernate offers the #OneToMany annotation which will give you the possibility to access all the Orders from a Client without the need of a separate query, only by mapping! Let's see an example:
#Entity
#Table(name = "Orders")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Client client;
//getters and setters
}
#Entity
#Table(name = "Clients")
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String attribute1;
#OneToMany(mappedBy = "client")
private List<Order> orders;
//getters and setters
}
In this example you should be able to get all the Orders from a Client just by calling the get of the "orders" attribute. Please, note that on the #OneToMany mapping we have specified the "mappedBy" attribute as "client", it was needed because we have a bidirectional mapping between Client and Order, in a simple usage of #OneToMany you would not need this mapping.
Important: When working with #OneToMany mapping you would eventually face some lazy fetching problems, in this case I highly recommend you to take a look at this question:
Solve “failed to lazily initialize a collection of role” exception
Also, I think you should start reading more about Hibernate to understand about it's basic concepts, please, check this other question about #OneToMany and #ManyToOne annotations on Hibernate:
Hibernate/JPA ManyToOne vs OneToMany

Issue in Mapping for Hibernate JPA

I have a datamodel, which has:
a User entity which has a few fields specific to 2 users in the application
another entity UserDetails, which contains details specific to one particular type of user in the application besides the fields in User entity
Both entities share the same primary key. I am new to JPA. What kind of mappings should be there between the two?
#Entity
class User{
#Id
#Column(name="USER_ID")
private int id;
}
#Entity
class UserDetails{
#Id
#OneToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_ID")
private User user;
...
}
The above mapping gives issues on fetching UserDetails for a particular User.
It requires that both Entities share the same primary key USER_ID.
You didn't mention the issues with the above mapping. It looks OK, but I would use a separate primary key for UserDetails table.
#Entity
class UserDetails{
#Id
private int id;
#OneToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_ID")
private User user;
...
}
Also, it is a good practise to use bidirectional relationships, for eaiser navigation i.e. getting user details from User, you would just use user.getUserDetails(); so in User class:
#Entity
class User{
#Id
#Column(name="USER_ID")
private int id;
#OneToOne(mappedBy = "user")
private UserDetails userDetails;
}
In this case use OneToOne relationship. But make sure your database table UserDetailshas foreign key relationship to User table. Use below code to implement it using JPA and Hibernate.
#Entity
class User{
#Id
#Column(name="USER_ID")
private int id;
// getters and setters
}
#Entity
class UserDetails{
#Id
#Column(name="USER_DETAILS_ID")
private int userDetailsId;
#OneToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID")
private User user;
// getters and setters
}

Categories