How to write a query to find which seats are booked and which are not - spring

I am using Spring JPA
I have 3 entities
1- Event
2- Seat
3- Reservation
Event has one to many relation with Seat and Reservation
Reservation has a one to many relation with Seat
(Event will have the seats created after its creation and then it will be assigned to the particular event)
(When users make reservations, each reservation can have multiple seats for the particular event)
What I've done so far:
The Event class
#Entity
#Data
#NoArgsConstructor #AllArgsConstructor
public class Event {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
#OneToMany(mappedBy = "event", fetch = FetchType.LAZY)
private List<Seat> seats;
#OneToMany(mappedBy = "event", fetch = FetchType.LAZY)
private List<Reservation> reservations;
}
The Seat class
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Seat {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
private String seatCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "event_seats")
private Event event;
#ManyToOne
#JoinTable(name = "seat_reservation")
private Reservation reservation;
}
The Reservation class
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Reservation {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
#ManyToOne
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "event_reservations")
private Event event;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "reservation")
private Collection<Seat> seats;
}
**Is the tables and relations design correct?
When I create a reservation and add seats to it, the linking table (reservation_seats) doesn't get updated.
And how to write a query to determine which Event seats are booked and which are not?**

There is more than one way to do this using JPA depending of your knowledge.
Options Like Native Query, JPQL or JPA Criteria.
The native query would be like this:
With reservation:
SELECT * FROM seat s WHERE s.id IN (SELECT seat_id FROM seat_reservation)
With no reservation:
SELECT * FROM seat s WHERE s.id NOT IN (SELECT seat_id FROM seat_reservation)
There may be other ways to do this, but I think it's enough.

Related

JPQL query / JPA / Spring boot best practice of updating many to many table

I have a user table and a city table and I have a connecting table users_cities (columns: id, user_id, city_id). A user can follow multiple cities.
From the client side i send an array of cityIds. Some might be new some might still be selected and some might have been deselected.
What is a best practice to update the users_cities table with the data? Should I just delete everything for that particular userId and insert the new array or ... ?~
Also, how does one delete and repsectively insert the data in bulk, for a many to many reference?!
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String email;
private String password;
private Boolean isGuest;
#ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles;
#ManyToOne()
#JoinColumn(name = "country_following_id")
private Country countryFollowing;
#ManyToMany()
private Set<City> citiesFollowing;
#ManyToMany()
private Set<Offer> offersFollowing;
}
and
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "cities")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#NonNull
private Long id;
private String countryCode;
private String name;
#Latitude
private Double latitude;
#Longitude
private Double longitude;
}
Should I just delete everything for that particular userId and insert the new array
Since the relation connecting users and cities does not have an identity of its own, that sounds reasonable
Also, how does one delete and repsectively insert the data in bulk, for a many to many reference?
Just clear the User.citiesFollowing collection and populate it anew (hint: since you have cityIds at the ready, you can use EntityManager.getReference() to load the cities)

How to send POST request with Many-to-many relationship in Spring?

I'm trying to add a order with equipment list, here's my entities:
the order entity
#Entity #Table(name = "orders") public class Order extends Ticket{
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private Set<OrderEquipment> orderEquipments = new HashSet<>();}
the equipment entity
#Entity #Table(name = "equipments") public class Equipment extends DateAudit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 30)
private String name;
#NotNull
private Long nbr_piece ;
#OneToMany(mappedBy = "equipment", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<OrderEquipment> orderEquipments = new HashSet<>();}
and the order_equipment entity
#Entity #Table(name = "order_equipment") public class OrderEquipment extends DateAudit { #Id
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
#Id
#ManyToOne
#JoinColumn(name = "equipment_id")
private Equipment equipment;
#NotBlank
#Column(name = "quantity")
private Long quantity;}
here is the add function in the orderController
#PostMapping("/orders")
public Order createOrder(#Valid #RequestBody Order Order){
Order.setObservateurEmail(Order.getObservateurEmail());
Order.setObject(Order.getObject());
Order.setDescription(Order.getDescription());
return orderRepository.save(Order);
}
I have seen a mistakes there, lemme try to help you. Since you issue is not clear, please lemme know if it does/does not work:
You have two bidirectional mappings there:
Order (with ALL cascade) <-> OrderEquipment
Equipment (with ALL cascade) <-> OrderEquipment
You are using #JoinColumn for both of them, even though they are bidirectional. Please take a look at this. You should always use the mappedBy attribute when defining bidirectional relationships.
Now, you are receiving an Order object from a POST request, making changes to 3 attributes and then saving it. Since the mapping between the Order and OrderEquipment have the CascadeType.ALL attribute, any save on the Order object will save all OrderEquipment children associated. If the Order object you are receiving already have OrderEquipment children, your method will also save/update them.
Your POST mapping looks good to me, just take care with your table relationship definitions.
Take a look at this answer to check how a lits of entities should be formatted on a JSON POST.

Jpa OneToOne shared primary key half works

I have SpringBoot 2.1.3 and Java 8 application. Building DB with JPA I have 3 table in one to one relationship. Suppose the tables is the follows:
#Entity
#Data //lombok
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private Address address;
}
And then:
#Entity
#Data
#Table(name = "address")
public class Address {
#Id
#Column(name = "id")
private Long id;
#OneToOne
#MapsId
private User user;
}
That's works.. and it is the best way to do (this exactly example is taken from documentation).
If I start the application the DB is created and if I tried to add entities all works well. The model created follows:
Now I want to add a Country object to my address Entities (for example) and I modified the Entities as follows:
#Entity
#Data
#Table(name = "address")
public class Address {
#Id
#Column(name = "id")
private Long id;
#OneToOne
#MapsId
private User user;
#OneToOne
#MapsId
private Country country;
}
And Country Entities:
#Entity
#Data
#Table(name = "country")
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#OneToOne(mappedBy = "country", cascade = CascadeType.ALL)
private Address address;
}
The application still starts, the DB is created and the model follows:
But if I try to save a User as follows:
User user = new User();
Address address = new Address();
Country country = new Country();
user.setAddress(address);
address.setUser(user);
address.setCountry(country);
country.setAddress(address);
userRepository.save(user);
I obtain the error:
java.sql.SQLException: Field 'country_id' doesn't have a default value
Anyway I solve the issue removing #MapsId and added #JoinColumn but I would like to understand what's wrong.
P.S.: I'm using MySQL 5.7 with InnoDB dialect (setting on application.properties)
Thanks all
It works only with one #MapsId annotation. Using two is causing that country id is not inserted:
insert into Country (id) values (?)
insert into Users (id) values (?)
insert into Address (user_id) values (?)

Register data into Many-to-Many Relation Table

I have 'Course' and 'Student' entities. They have many-to-many relation. So, i have COURSE_STUDENT(contains 'student_id' and 'course_id' columns) table. I want to register students to courses with a button.(For example; a student lists courses and click Register button to register a specific course).
When i want to create new courses, i use courseRepository and courseMapper which comes from JHipster by default.
But i don't have repository and mapper files for COURSE_STUDENT. Because it is not actually a main entity. It is created for many-to-many relation.
How can i register students to courses?
Git repo:https://github.com/canberkizgi/monolithic-mucs
My course entity:
#Entity
#Table(name = "course")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(name = "title", nullable = false)
private String title;
#Column(name = "description")
private String description;
#ManyToOne
private Instructor instructor;
#ManyToMany(fetch = FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "course_student",
joinColumns = #JoinColumn(name="courses_id", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="students_id", referencedColumnName="id"))
private Set<Student> students = new HashSet<>();
Student entity:
#Entity
#Table(name = "student")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne
#JoinColumn(unique = true)
private User user;
#ManyToMany(fetch = FetchType.EAGER,mappedBy = "students")
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Course> courses = new HashSet<>();
For example; Createcourse function with Mapper and Repository
#PostMapping("/courses")
#Timed
public ResponseEntity<CourseDTO> createCourse(#Valid #RequestBody CourseDTO courseDTO) throws URISyntaxException {
log.debug("REST request to save Course : {}", courseDTO);
if (courseDTO.getId() != null) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new course cannot already have an ID")).body(null);
}
Course course = courseMapper.toEntity(courseDTO);
course = courseRepository.save(course);
CourseDTO result = courseMapper.toDto(course);
return ResponseEntity.created(new URI("/api/courses/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
.body(result);
}
The relationship is owned by the course entity. Thats because on the student side the #ManyToMany annotation has a mappedBy attribute. This means, that the database will reflect the set in the course. You need to add students to that set to save the relationship. That change needs to be done within a transaction.
That being said it would probably be best to follow DDD here. I would create a registerTo method in the student class that would take the course as a parameter. I would then call this.courses.add(course) and course.getStudents().add(this) in that method.

How to load OneToMany Collections data in response using FetchType.LAZY?

I have created a sample jHipster sample app( url: http://jhipster.github.io/creating_an_app.html), using entity sub-generator I have created an Event entity which has OneToMany relationship with EventImages, EventTickets and EventQuestions when I retrieve all(app running in local machine, the api url: http://127.0.0.1:8080/api/events ) events I couldn't find EventImages, EventTickets and EventQuestions data in response.
Event Entity
#Entity
#Table(name = "JHI_EVENT")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Event implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
/* other fields */
#OneToMany(mappedBy = "event")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonIgnore
private Set<EventTicket> eventTickets = new HashSet<>();
#OneToMany(mappedBy = "event")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonIgnore
private Set<EventImage> eventImages = new HashSet<>();
#OneToMany(mappedBy = "event")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonIgnore
private Set<EventQuestion> eventQuestions = new HashSet<>();
/* getter and setters */
}
EventImages entity
#Entity
#Table(name = "JHI_EVENTIMAGE")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class EventImage implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "image_url")
private String imageUrl;
#ManyToOne
private Event event;
/* getters and setters */
}
similarly, EventTickets and EventQuestions entities.
After some research i found that i need to remove #JsonIgnore annotation to load OneToMany Collections data using lazy fetch, the response i got is null for EventImage, EventTicket and EventQuestions, as below.
[ {
"id": 1,
"title": "First Event",
"eventVenue": "xyz",
"startDate": "2015-05-28T10:10:00Z",
"endDate": "2015-06-20T10:10:00Z",
"eventTickets": null,
"eventImages": null,
"eventQuestions": null
} ]
Then I found I need use #JsonManagedReference and #JsonBackReference on parent/child relation, but need to use fetch = Fetch.EAGAR (I want load OneToMany Collections when I set FetchType.LAZY which is default, as an when Event entity is called).
Event entity when I used #JsonManagedReference
#Entity
#Table(name = "JHI_EVENT")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Event implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
/* other fields */
#OneToMany(mappedBy = "event", fetch = FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonManagedReference
private Set<EventTicket> eventTickets = new HashSet<>();
#OneToMany(mappedBy = "event", fetch = FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonManagedReference
private Set<EventImage> eventImages = new HashSet<>();
#OneToMany(mappedBy = "event", fetch = FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonManagedReference
private Set<EventQuestion> eventQuestions = new HashSet<>();
/* getter and setters */
}
EventImage entity when I used #JsonBackReference
#Entity
#Table(name = "JHI_EVENTIMAGE")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class EventImage implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "image_url")
private String imageUrl;
#ManyToOne
#JsonBackReference
private Event event;
/* getters and setters */
}
How to load OneToMany Collections lazily i.e. EventImages, EventTickets and EventQuestions in response when Event entity requested i.e http://127.0.0.1:8080/api/events REST call is made.
Thanks
I don't actually understand your question, but I try to explain how JPA works.
As you mentioned, using JPA you can either load collections LAZILY or EAGERLY.
LAZY means that you do not load the collection from the beginning. The collection is only loaded when you access the collection (this only works in the same transaction where the entity is loaded or attached).
EAGER means that the collection is loaded from the beginning (as the entity itself is loaded).
So if you want to provide the collection by the REST service, then you have to load the collections during the transaction.
This can be done in several ways:
One way is to define the FetchType of the collection to EAGER
Another way is to LAZY load the collection and after loading the entity, access the collection (for example by calling size() -> event.getEventImages().size();)
Another way is to load the entity and the collection with a JPQL-Query (SELECT e FROM Event JOIN FETCH e.eventImages ...)
There are even more ways to achieve this depending on the JPA implementation you are using
So, if I understood your question rigth, then you could Annotate your Spring-Data-DAO-Find-Method with
#Query("SELECT e FROM Event JOIN FETCH e.eventTickets, JOIN FETCH e.eventImages, JOIN FETCH e.eventQuestions")

Resources