I am currently learning hibernate and created a simple database that is simulating a library:
I got 3 classes: Book, Customer, Order:
#Entity
public class Book
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private int ISBN;
private String name;
private String autorName;
private double price;
//Getters setters etc...
}
#Entity
public class Customer
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String lastName;
private String city;
//Getters setters etc...
}
#Entity
#Table(name = "TB_order")
public class Order
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private Date date;
private String status;
#OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinColumn
private List<Book> books;
#ManyToOne(cascade = CascadeType.ALL)
private Customer customer;
//Getters setters etc...
}
When I try to change some order by saveOrUpdate() (eg. change the book that customer ordered) I create new instance of Order and I pass it to saveOrUpdate() method:
Order o = new Order(Date.valueOf("2019-06-01"),"old",bookslist.subList(2,3), customerslist.get(0));
o.setId(1);
session.saveOrUpdate(o);
It does not only change values in table order, but it also create new entities in table book and table Customers. I guess it is like this because i put the list of books and customer instace created in the main method.
So what instance of Order object shall be passed into the saveOrUpdate() method to only change the values in table Order to already existing values from database?
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 have question about access to data.
I have that DB:
[country: id, country_name],
[city: id, country_id, city_name],
[address: id, shop_data_id, city_id, address_data],
[shop_data: id, data]
My relations country-city one to many, city-address one to many, address-shop_data one to one.
I'm looking for information that can I do that SQL query with ORM, or what is the best way do do it in ORM.
UPDATE shop_data
INNER JOIN country ON country.id=1
INNER JOIN city ON country.id=city.country_id
INNER JOIN address ON city.id= address.city_id
INNER JOIN shop_data ON address.shop_data_id=shop_data.id
SET shop_data.data="shop data string"
WHERE shop_data.id=address.shop_data_id
I know that in SQL I should start by shop_data, but by doing this I want to show that I want start in ORM by country entity.
I wrote entities in Hibernate with annotation
#Entity
#Table(name="country")
public class Country{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "country")
private String country;
#OneToMany(mappedBy = "country", fetch=FetchType.LAZY)
#JsonBackReference
private List<City> cities = new ArrayList<>();
// getters/setters ..
}
#Entity
#Table(name="city")
public class City{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "city")
private String city;
#OneToMany(mappedBy = "city", fetch=FetchType.LAZY)
private List<Address> adresses = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="country_id")
#JsonIgnore
#JsonManagedReference
private Country country;
// getters/setters ..
}
#Entity
#Table(name="address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "address")
private String address;
#Column(name = "district")
private String district;
#Column(name = "post_code")
private String postCode;
#OneToOne
#JoinColumn(name="shop_data_id")
private ShopData shopData;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="city_id")
private City city;
// getters/setters ..
}
#Entity
#Table(name="shop_data")
public class shopData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "data")
private String data;
// getters/setters ..
}
I know that I can use getters starting from country that returns List<ObjectType> and from there get my object. Next run function update and update right row. But in this way are done some number of queries.
Is it possible to do by Java Hiberante ORM by one query? Or which way is the best to minimize query amount? By this method I also want to update next also address data.
You should definitely read a book about JPA/Hibernate to understand what JPQL or HQL supports. You can just do joins as you do them with SQL, except for DML statement, but you don't need that. In your case a simple subquery is enough to model what you need. A possible query could look like the following:
UPDATE ShopData s
SET s.data="shop data string"
WHERE EXISTS (
SELECT 1
FROM Country c
WHERE c.id = 1 AND c.city.address.shopData.id = s.id
)
I'm really new to Hibernate and this is my first app which is enterprise level.
I'm stuck at one to many mapping. I did the mapping whole day but it doesn't give to correct table structure for me.
Here is the ER diagram I want to map
These are the Classes
Feed Class
#Entity
public class Feed {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String name;
private long quantity;
//With getters and setters
}
Feed Order Details Class
#Entity
public class FeedOrderDetail {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private double unitPrice;
private long quantity;
#OneToMany(cascade=CascadeType.MERGE)
#JoinColumn(name="feed_id")
private List<Feed> feed = new ArrayList<Feed>();
//Getters and Setters
}
after deploying app I get the following table structure
My problems are
why feed_id is in Feed table?
Should I add same feed every time I add a feed order detail? (it isn't a good idea)
I can achieve this by moving #OneToMany annotation and attributes to Feed table. but if I move it to Feed class how can I represent feed order details in JSP pages?
I'm using spring with this project also.
I would suggest to have a object reference on both sides of the relationship.
So have a reference to List<FeedOrderDetail> in Feed and have a single refernce to a Feed in your FeedOrderDetail.
Class Feed:
#Entity
public class Feed {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String name;
private long quantity;
//With getters and setters
// !new!
#OneToMany(mappedBy = "feed") // mappedBy references the fieldname in the other Java class
private List<FeedOrderDetail> details;
}
Class Detail:
#Entity
public class FeedOrderDetail {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private double unitPrice;
private long quantity;
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="feed_id")
private Feed feed; // only a single feed reference
//Getters and Setters
}
If you want to get a list of feeds using Hibernate's JPA API, then you can use this code:
TypedQuery<Feed> query = entityManager.createQuery("SELECT f FROM Feed f", Feed.class);
List<Feed> feeds = query.getResultList();
You designed it backwards. You schema says that 1 Feed instance includes M FeedOrderDetail instances.
So, in the class Feed, you should have a List<FeedOrderDetail>. But that's not what you've done. You have a List<Feed> in FeedOrderDetail.
The correct one is:
#Entity
public class Feed {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String name;
private long quantity;
//With getters and setters
#OneToMany(cascade=CascadeType.MERGE, mappedBy="feed")
private List<FeedOrderDetail> orders = new ArrayList();
}
And
#Entity
public class FeedOrderDetail {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private double unitPrice;
private long quantity;
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="feed_id")
private Feed feed;
//Getters and Setters
}
My problems are why feed_id is in Feed table?
You are right, it should not be in case your ER diagram is right.
Your ER diagram and table structure does not match, so I am trying to give you class mapping according to your ER-diagram.
Feed.java
#Entity
public class Feed {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String name;
private long quantity;
//With getters and setters
#OneToMany(fetch = FetchType.LAZY, mappedBy = "feed")
public Set<FeedOrderDetail> getFeedOrderDetail() {
return this.feedOrderDetail ;
}
}
FeedOrderDetail.java
#Entity
public class FeedOrderDetail {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private double unitPrice;
private long quantity;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FEED_ID", nullable = false)
public Feed getFeed() {
return this.feed;
}
//Getters and Setters
}
I hope it helps :)
I need to create a table EMPLOYEE_REMARK from a table EMPLOYEE.
And need to do it with Annotation Hibernate.
EMPLOYEE
EMP_ID, EMP_FNAME, EMP_LNAME
EMPLOYEE_REMARK
EMP_REMARK_ID, EMP_ID, REMARK
it will be a OnetoOne relationship i.e, for each EMP_ID there will be one REMARK. REMARK could be null.
please help me with the solution...
Can it be done by creating one class from employee and populate the EMPLOYEE_REMARK from it???
Basically here is the way of doing what you want.
Employee
#Entity
#Table(name = "EMPLOYEE")
public class Employee implements Serializable {
#Id
#Column(name = "EMP_ID")
private Long id;
#Column(name = "EMP_FNAME")
private String firstName;
#Column(name = "EMP_LNAME")
private String lastName;
#OneToOne(mappedBy = "employee", cascade = CascadeType.ALL,
orphanRemoval = true)
private EmployeeRemark employeeRemark;
public void setRemark(String remark) {
this.employeeRemark = new EmployeeRemark();
this.employeeRemark.setRemark(remark);
this.employeeRemark.setEmployee(this);
}
public String getRemark() {
return employeeRemark == null ? null : employeeRemark.getRemark();
}
//getters and setters
}
Employee Remark
#Entity
#Table(name = "EMPLOYEE_REMARK")
public class EmployeeRemark implements Serializable {
#Id
#Column(name = "EMP_REMARK_ID")
private Long id;
#OneToOne
#JoinColumn(name = "EMP_ID")
private Employee employee;
#Column(name = "REMARK")
private String remark;
//getters and setters
}
When saving employee, just call save on employee. EmployeeRemark will cascade to all operations and will be removed along with employee or if it become an orphan in other way.