Spring Boot one to many unidirectional - spring

I have the entity Project and entity Cluster.
A Project can have multiple Clusters.
I don't want a third table to save this relationship. Just the Project ID saved to the Cluster.
This is my project Entity:
#Entity
#Table(name = "Project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String projectName;
#OneToMany
#JoinTable(name = "cluster")
private Set<Cluster> clusters;
}
This is my Cluster entity
#Entity
#Table(name = "Cluster")
public class Cluster {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String team;
private String concept;
}
This gives me the error: must have same number of columns as the referenced primary key .
How could I fix this? I don't see how to fix this.

Use #JoinColumn instead of #JoinTable
public class Project {
//...
#OneToMany
#JoinColumn(name="PROJECT_ID", referencedColumnName="id")
private Set<Cluster> clusters;
and add PROJECT_ID column to Cluster entity.
public class Cluster {
//...
#Column(name = "PROJECT_ID")
private Integer projectId;

Correct me if I'm wrong but, as far as I know about Software Engineering, what you want CAN'T be done: you can't store a relationship nowhere but in a third table. Lists, sets, maps, and so on MUST be stored that way.
Otherwise, and in your case, you'd have Project's properties replicated for each cluster of the relationship, and that's not desirable.

Related

Why should we use #Enumerated in JPA?

I have an enum named difficulty and I marked it with #Entity and it has a one to one relationship with the recipe class. I ran my program in spring boot and it ran without any problem. My question is, why do we have to use #Enumerated? What's wrong with the way I went?
Enum:
#Entity
public enum Difficulty {
EASY,MODERATE,HARD;
#OneToOne
private Recipe recipe;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
}
Recipe class:
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne
private Difficulty difficulty;
}
with #Enumareted:
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne
#Enumerated(value=EnumType.STRING)
private Difficulty difficulty;
}
If we go with #Enumrated, we have to remove #Entity from Difficulty(I wrote Recipe which corrected), which I didn't write.
Enum are not associations (although you can create a colletion of enums with #ElementCollection, but it's a different scenario), so you don't need the #OneToOne if you use #Enumerated. The mapping should be:
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Enumerated(STRING)
private Difficulty difficulty;
}
enum Difficulty {EASY,MODERATE, HARD;}
The value will be stored as a column in the table representing Recipe.
A one-to-one association means that every time you save a recipe, you are also creating a new Difficulty. You are using enums, so I don't think that's what you want (are you sure your mapping actually works?).
Without using enums, a mapping that would make sense, would be:
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne
private Difficulty difficulty;
}
#Entity
public class Difficulty {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long difficultyId;
private String level;
}
or, if you want to use enum and associations:
#Entity
public class Difficulty {
enum Level {EASY, MEDIUM, HARD;}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long difficultyId;
#Enumerated(STRING)
private Level level;
}
Having a different entity means that the difficulties are stored as rows in a separate table Difficulty. Because it's a many-to-one association, you can associate different recipes to the same difficulty.
It also means that Hibernate will need a join or a separate query to load the difficulty. I don't see the benefit if you are only interested in the name of the difficulty.
Ultimately, it depends on your use case and how you want the data to appear in the database.
But if you can use enum and there is only one level per recipe, mapping it as a column seems a simpler solution.

How to generate an entity from another entity JPA - Spring boot

I have a spring boot JPA project with an entity called Customers and another one CustomerReports
#Entity
public class Customers {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String Name;
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
private Reports reports;
//getter and setters..etc
}
#Entity
public class CustomerReports {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private BigDecimal monthlyPayment;
//done
#JsonIgnore
#OneToOne(mappedBy = "reports")
private Customers customers;
//constructors, getters...etc.
}
I want whenever I insert a Customer, a report to also be generated for that customer. The column "monthlyPayment" in reports is also generated through a reference from another table so I don't want to insert those columns manually if that makes sense.
Is there a way to do that? I'm not sure what to google so it would be great if anyone can give me an idea
If I understand your question properly, you can derive CustomerReports entity based on Customers via simple java utility method & then call save if you are using jparepository :
CustomerReports customerReports=reportUtil(customerEntity);
jpaRepository.save(customerEntity);
jpaRepository.save(customerReports);
...
private CustomerReports reportUtil(Customers customerEntity){
/*Derive values for CustomerReports based on Customers & return*/
}
Or if you don't want to do by this way then check if your underlying database support triggers which you can use for inserting data into CustomerReports while doing insert to Customers

Two tables connected via Primary Key

I have read about the use of #MapsId and #PrimaryKeyJoinColumn annotations, which sounds like a great options. I have two tables (UserList and UserInformation) which have a child, parent relationship, respectively; both classes below are abbreviated to just include the relevant columns. UserInformation's primary key value is always null and does not take the value of its parent column.
User Class
#Entity
#Data
#Table(name = "user_list")
public class UserList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// List of foreign keys connecting different entities
#OneToOne(mappedBy = "user")
#MapsId("id")
private UserInformation userInfo;
}
UserInformation Class
#Entity
#Data
#Table(name = "user_information")
public class UserInformation implements Serializable {
#Id
private Integer userId;
#OneToOne
private UserList user;
}
I would prefer to not use an intermediary class if possible. I'm not tied to MapsId or even this implementation if there is a better solution.
Thanks!
The question is not very clear to me, but I think you could improve the following in the modeling of the entity:
The #column annotation can only be omitted when the class parameter is called exactly the same as the database column, taking into account the table name nomenclature, could it be that the column is user_id ?, if so the id parameter should be :
#Id
#column(name="USER_ID")
private Integer userId;
In the user entity being id, it will match the DB ID field so the #column annotation is not necessary

Usual field as foreign key

I have two tables. I want to make between them relationship, but the thing is that the child table connects to an attribute in a parent node, which is not a PK. How can I assign a non-PK field as a FK for a table?
Here are the tables. User Information:
#Entity
#Table(name="user")
public class userinformation implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="USER_ID")
private int uID;
#Column(name="LIB_ID")
private String libID;
//Other attributes
}
Lib Information
#Entity
#Table(name="libinfo")
public class Auth {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="AUTH_ID")
private int authID;
#Column(name="LIB_ID")
private String lib_ID;
//Other attributes
}
They both should be linked through libID (surely unique). Any idea how to implement it correctly?
Given:
class User {
#Column(name = "lib_id")
private String libID;
}
you must map the Auth entity as:
class Auth {
#JoinColumn(name = "lib_id", referencedColumnName = "lib_id")
#ManyToOne
private User user;
}
Basically, referencedColumnName is used to inform the JPA provider that it should use a column other than the primary key column of the referenced entity (which is used by default with #ManyToOne mappings).

Fetch specific property in hibernate One-to-many relationship

I have two pojo classes with one-to-many relationship in hibernate
CustomerAccountEnduserOrderDetails.class
#Entity #Table(name="customer_account_enduser_order_details")
public class CustomerAccountEnduserOrderDetails implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Long id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "product_id", insertable = false, updatable = false)
private CustomerCmsProduct customerCmsProduct;
}
Second is CustomerCmsProduct.class
#Entity
#Table(name="customer_cms_product")
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomerCmsProduct {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private Long id;
#Column(name="name")
private String name;
#Column(name="offer_price")
private String offerPrice;
#Column(name="original_price")
private String originalPrice;
#Column(name="discount")
private String discount;
}
Here if I fetch the object of CustomerAccountEnduserOrderDetails class,then i will get the CustomerCmsProduct class also , my problem is that here i want the specific column of CustomerCmsProduct table (not all by default i am getting all) like only id and originalPrice.
How i can do like that projection here?
In the service layer or at a webservice layer( if this is a web project) Create two different classes other than #Entity as DTO(Data Transfer Objects) which helps is data transfer from one layer to the other.
public class CustomerAccountEnduserOrderDetailsPojo {
private List<CustomerCmsProductPojo> productPojoList = new ArrayList<> ();
// getter and setter
}
public class CustomerCmsProductPojo {}
Follow the below steps
Retrieve the #Entity class data by executing the query from service layer.
Iterate over all the fields and copy only required fields to pojo layer
Expose the data to other layers using service method.
This way, we can avoid changing the custom hibernate behavior as it is linked with many parameters like cache, one to many queries that are fired per iteration.
And also, do any customization that you want in this layer. Hope this is multi layered project where you have different layers which servers different purpose.

Resources