I am writing an online store using Spring Boot (MVC) and hiberbate. I have an order class where I need a Сart link. But the problem is that in the database I do not have a specific Сart table, but there is a cart _products table, where the peimary key consists of two columns (as shown in the picture below!). I really need a connection in the Order class, so I decided to make a Composite Primary Key at the hibernate level (and I seem to have done it), but I can't figure out what to do next! I am stuck. Please tell me where to go? How can I solve my problem?
OrderClass:
#Entity
#Table(name = "pg_order")
public class Order {
// Fields
//
private #Id
#GeneratedValue
Long id;
private String address;
#Column(name = "phone_number")
private String phoneNumber;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "date_order")
private Date dateOrder;
#Enumerated(EnumType.STRING)
#Column(name = "order_status")
private OrderStatus orderStatus;
#Column(name = "total_cost")
private BigDecimal totalCost;
// Relationships
//
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
// #OneToMany
// #JoinColumn(name = "cart_products_pkey")
// private Cart cart;
}
Cart:
#Entity
#Table(name = "cart_products")
public class Cart {
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
static class CartId implements Serializable {
private Long orderId;
private Long drinkId;
}
// Fields
//
#EmbeddedId
private CartId cartId;
#ManyToOne(optional = false)
#JoinColumn(name = "order_id")
private Order order;
#ManyToOne(optional = false)
#JoinColumn(name = "drink_id")
private Drink drink;
private int count;
}
If you need access to the 'DRINKS' for that order. You need to change the relation to Cart from the Order class.
You have commented a relationship where ORDER just have access to one CART, since you need to access N CARTS (One to Many) you need to add a SET. Something like this:
#OneToMany
#JoinColumn(name = "cart_products_pkey")
private Set<Cart> cartProducts;
Now the ORDER has a SET of CART.
You can easily access to the CARTS of that ORDER with order.getCartProducts()
And since CART has a key to DRINK, you can easily access it.
Hope this can help you.
Related
I have a Product :
#Data
#Entity
#Table(name = "products", schema = "laboratory", catalog = "laboratory")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Product {
#Id
#GeneratedValue
private int id;
#ManyToOne(fetch = FetchType.LAZY, cascade= CascadeType.ALL)
#JoinColumn(name = "project_id")
#Transient
private Project project; // this one is for read only
#Column(name="project_id") // this will save the id in db for the project
private int projectId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="id")
private Inspection inspection;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="id")
private Information information;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="id")
private Departure departure;
private String un;
private String tc;
}
There is 3 class that this product needs in order to be a Product : Information, Inpection, Departure
All 3 of these classes are similar.
I want to link them by the Product.id witch is a #GeneratedValue AI in sql.
Here is one of the 3 class :
Information
#Data
#Entity
#Table(name = "products_informations", schema = "laboratory", catalog = "laboratory")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Information {
#Id
private int productId;
private String description;
private String model;
private int year;
private String serialNumber;
private int odometre;
private int noCrochet;
private int nbKeys;
private String localisation;
private String cemeteryPosition;
#JsonFormat(pattern = "yyyy-MM-dd")
private Date receptionDate;
}
I want, WHEN I save() the product, that the private String productId in this class to automatically take the Id from the Product class without having to do it manually in my controller.
You have the mappings backwards in your model.
By using
public class Product {
#Id
#GeneratedValue
private int id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="id")
private Information information;
You've told JPA to use the PRODUCT.ID primary key as a foreign key to the Information table; foreign keys are controlled by the relationship, so it means your ID value is pulled from the information.productId value. Opposite of what you are asking for and it means you have 4 mappings trying to set the PRODUCT.ID column value (set them different and see for yourself).
Try this instead:
public class Product {
#Id
#GeneratedValue
private int id;
#OneToOne(mappedby="product", cascade = CascadeType.ALL)
private Information information;
..
}
public class Information {
#Id
private int productId;
#MapsId
private Product product;
..
}
With this you will need to set the Information.product reference, but JPA will use that to set your productId value, using the one you set within the product.id property. You just need to set this relationship when you add an Information instance to a product. Do the same for the other relationships
I'm new at Spring Boot's JPA concept so need your help in deciding how to import just the ID of another entity, say User into HealthData entity. Following is my User entity:
#Entity
#Table(name = "user",uniqueConstraints = {#UniqueConstraint(columnNames = "email")})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#Email
#Column(nullable = false)
private String email;
private String imageUrl;
#Column(nullable = false)
private Boolean emailVerified=false;
#JsonIgnore
private String password;
#NonNull
#Enumerated(EnumType.STRING)
private AuthProvider authProvider;
private String providerId;
}
And I wish to define HealthData entity in the following manner :
#Entity
#Table(name = "HealthData",uniqueConstraints = {#UniqueConstraint(columnNames = "id")})
public class HealthData {
#Id
private Long id; //how to import id of User here?
#Column
private Double height;
#Column
private Double weight;
#Column
private int age;
...other columns
}
Now, I wish to use Id of User to this entity(kind of making parent-child relationship) . I don't want to add User class object in HealthData. I thought of using #OneToOne in HealthData but then it would add User in it. How can i just include Id from parent table in child table?
In this case, your HealthData has a reference to User, and I'm not sure why you wouldn't have mapped this as a foreign key. If you are able to do so, I'd suggest the following:
#Entity
#Table(name = "HealthData")
public class HealthData {
#Id
#OneToOne
#JoinColumn(name = "id")
private User user;
#Column
private Double height;
#Column
private Double weight;
#Column
private int age;
...other columns
}
JPA then handled setting the "ID" to the value within your user instance for you, and can persist both in the same transaction automatically. Allowing references to be marked as IDs is known as a derived ID and supported I believe since JPA 2.0.
As for efficiency, you can still lazy fetch or even not fetch the user instance. It is simple to just map the ID column as a basic using a slightly different approach:
#Entity
#Table(name = "HealthData")
public class HealthData {
#Id
private Long id;
#MapsId
#OneToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "id")
private User user;
#Column
private Double height;
#Column
private Double weight;
#Column
private int age;
...other columns
}
JPA will set both the User id as well as the healthData.id values based on what it generates for the user Id sequence when you set the healthData.user reference.
You can use getters and setters to set the value of user id in the healthdata table.
I want to get data from multiple table.
public class Student{
private int id;
private String name;
private List<Course> course;
}
public class Course{
private int id;
private String name;
private int studentId;
}
I want to fetch data from student and course table using spring data jpa and map to student object.
How can I do that in efficient way?
#Data
#NoArgsConstructor
public class Student{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#OneToMany(mappedBy="studentId",cascade=CascadeType.ALL,fetch=FetchType.Eager)
private Set<Course> course;
}
You May Use Set Instead of List.
Always Use Mapped By in OneToMany Side, If you use it manyToOne side it will create an
extra table.
You can use Fetch Type eager or lazy. By default, it is lazy with You have
to use #transactional of Lazy.
#Data
#NoArgsConstructor
public class Course{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#ManyToOne
#JoinColumn(name="studentId")
private int studentId;
}
Hope this Answer Solve your query Happy Coding!.
Note that the starting point might be wrong. I assume that a student can choose multiple courses and a course can be chosen by multiple students. So it is actually a #ManyToMany relationship but not #ManyToOne or #OneToMany.
You will definitely need a joint table to map their primary keys from two tables into the joint table.
#Entity
#Data
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#EqualsAndHashCode.Exclude
#ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinTable(
name = "courses",
joinColumns = #JoinColumn(name = "student_id"),
inverseJoinColumns = #JoinColumn(name = "course_id"))
private Set<Course> courses;
}
#Entity
#Data
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#JsonIgnore
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToMany(mappedBy = "courses",fetch = FetchType.EAGER,cascade = CascadeType.ALL)
private Set<Student> students;
}
Note all the modifications I have here.
For the data persisted into database, Long is a better choice than int. Similarly, e.g., use Boolean instead of boolean.
Think the Student as the side managing the many-to-many relationship, and Course as the target side. On the target side, use #JsonIgnore and #ToString.Exclude annotations to avoid an infinite recursion, StackOverflow or OOM.
#JsonIgnore
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToMany(mappedBy = "courses",fetch = FetchType.EAGER,cascade = CascadeType.ALL)
Use Set instead of List if a student is not supposed to select the exact same course. It ensures that one can still select 2017 fall Maths and 2018 fall Maths, while one cannot select 2017 fall Maths twice.
I am just started with hibernate 4 (annotations), I have following three model Classes with One to One and One to Many Relationship.`
#Entity
#Table(name = "USERS",uniqueConstraints = {#UniqueConstraint(columnNames={"EMAIL_ID"})})
public class UserBasicInfo implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="userId")
private Long userId;
#Column(name="EMAIL_ID")
private String emailId;
#Column(name="PASSWORD")
private String password;
#OneToMany(fetch = FetchType.LAZY, mappedBy="userBasicInfo",cascade = CascadeType.ALL)
private Set<UserDeviceInfo> userDeviceInfo;
#OneToOne(mappedBy="userBasicInfo",cascade = CascadeType.ALL)
private UserAdvancedInfo userAdvancedInfo;
///////// Getter setters
}
and
#Entity
#Table(name = "USERS_ADVANCED_DETAILS")
public class UserAdvancedInfo implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ADVANCED_INFO_ID")
private Integer advancedInfoId;
#Column(name="GENDER")
private Gender gender;
#Column(name="UNIT_OF_MEASUREMENT")
private UnitOfMeasurement unitOfMeasurement;
#Column(name="HEIGHT")
private Double height;
#OneToOne
#JoinColumn(name="userId", insertable = true, updatable = true, nullable = true)
private UserBasicInfo userBasicInfo;
//////////////////////// Getter and Setter ////////////////////////
}
On the basis of above model classes I have two different REST APIs.
In the first API, only basic info will inserted and that is working fine.
But in the second API I have to update On UserAdvancedInfo on the basis of primary key of UserBasicInfo.
I have used update, merge, saveOrUpdate, but instead of updating data these all methods inserting a new row every time.
Kindly help me out.
Thanks,
To update the entity, you should need to set primary key to UserAdvancedInfo object and set UserBasicInfo object with there primary key to UserAdvancedInfo object :
UserAdvancedInfo info = new UserAdvancedInfo();
UserBasicInfo userInfo =new UserBasicInfo();
info.setUserBasicInfo(userInfo);
Hibernate checks for primary key for updation.
Continuing with this I decided to take an approach of making a different entity and make a OneToOne relationship (I hope this is OK). Bet let me explain what I have been doing:
I have a Juez (judge) entity with these relevant fields:
#Entity
#Table(name = "jueces")
public class Juez implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id")
private Integer id;
#Version
#Column(name = "opt_lock")
private Integer version;
#Basic
#Column(name = "nombres")
private String nombres;
// Getters, setters, other properties, etc.
}
And I want another entity called RolDeJueces (judge roll) to hold a java.util.Map<Integer, Juez> in which I want the keys to be sorted (1,2,3...) and mapped to a judge so I can have a roll to make some calculations. Here is what I have until now:
#Entity
#Table(name = "rol_jueces")
public class RolDeJueces {
#Id
private Integer id;
#Version
#Column(name = "opt_lock")
Integer version;
#OneToMany //Is this ok?
#MapKeyColumn(name = "orden_juez", updatable = true)
private Map<Integer, Juez> rol;
// Getters, setters, other properties, etc.
}
How can I sort the map key and hold a reference to all my judges but only one time (a la #OneToOne)?
Also, I must be capable of reordering my judges and make the changes persistent e.g.
rolDeJueces.getRol().clear(); // Delete current order
int order = 0;
// Add judges according to new order to the roll
for(Juez juez : getNewOrder()){
RolDeJueces.getRol().put(++order, juez);
}
// save my new roll and stuff...
Is this possible?
Ok, I figured that I do not need a map for insertion order nor the stuff I wanted.
Here is the impl:
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
#Version
#Column(name = "opt_lock")
private Integer version;
#OneToMany(orphanRemoval = false)
#OrderColumn(name = "order_jueces")
private List<Juez> rol;
#OrderColumn will generate an extra column in the table used for the relationship.