Spring Boot many to many relation - How to get an additional property inside the join table as a property from another entity - spring

Im new in Java Spring and I'm stuck at this point here for a while:
I have an entity model like this:
Entity Relation now
A channellist contains many channels. A channel can be assigned to many channellists. I have setup my code that this works fine. Now I would like to add a property "sort" to my channels, so I would be able to order the channels for every specific channellist in a way I would like to. From the database relation model I know I have to store this information in the joining table "Channellist_Channel".
MY PROBLEM IS: I dont understand how I'm able asign this property to my Entity "Channel" so that every Channel has a "sort"-value depending on the context of the channellist. I read for this case, I have to add a new Entity which will represent the join table "Channellist_Channel" and add the property "sort" there. But the puzzle in my head is just not complete to do it :/
Here are my two entitys Channellist and Channel
CHANNELLIST:
#Entity
#Table(name = "channellist", schema = "stream")
public class Channellist {
#Id
#Column(name = "ID")
private int id;
#Column(name = "DISPLAYNAME")
private String displayName;
#ManyToMany
#JoinTable(
schema = "stream",
name = "Channellist_Channel",
joinColumns = #JoinColumn(name = "channellist_id"),
inverseJoinColumns = #JoinColumn(name = "channel_id")
)
private Set<Channel> channels;
//Constructors...
//Getter Setter...
CHANNEL:
#Entity
#Table(name = "channel", schema = "stream")
public class Channel {
#Id
#Column(name = "id")
private String id;
#Column(name = "display_name")
private String displayName;
#Column(name = "type")
private int type;
#Column(name = "logo_url")
private String logoUrl;
#Column(name = "stream_url")
private String streamUrl;
#OneToMany(mappedBy = "channelId", fetch = FetchType.LAZY)
#OrderBy(value = "timeStart ASC")
private Set<ChannelEpg> programs;
#JsonIgnore
#ManyToMany
#JoinTable(
schema = "stream",
name = "Channellist_Channel",
joinColumns = #JoinColumn(name = "channel_id"),
inverseJoinColumns = #JoinColumn(name = "channellist_id")
)
private Set<Channellist> channellists;
//Constructors...
//Getters Setters...
My JSON Response from "GetAllChannellists: Please Ignore the TAG "programs" under "channels". Its not relevant for my problem.
JSON RESPONSE:
[
{"id":1,
"displayName":"Standard",
"channels":[
{"id":"344143862749137509158c22d1606ad5",
"displayName":"KiKa",
"type":0,
"logoUrl":"https://example.de/test/kika.png",
"streamUrl":"https://example.de/test/kika.m3u8",
"programs":[
{"channelId":"344143862749137509158c22d1606ad5",
"timeStart":"2022-08-09T11:30:00.000+00:00",
"timeEnd":"2022-08-09T11:40:00.000+00:00",
"title":"logo!",
"subTitle":null,
"description":"a verry long description, no one cares"},
{"channelId":"344143862749137509158c22d1606ad5",
"timeStart":"2022-08-09T11:40:00.000+00:00",
"timeEnd":"2022-08-09T12:10:00.000+00:00",
"title":"Tiere bis unters Dach",
"subTitle":"Jojo, der Held",
"description":"another long description, no one cares"},
[...]
{"id":2,
"displayName":"Deluxe",
"channels":[
[...]
My goal is it to make it look like this:
[
{"id":1,
"displayName":"Standard",
"channels":[
{"id":"344143862749137509158c22d1606ad5",
"displayName":"KiKa",
"sort":21,
"type":0,
"logoUrl":"https://example.de/test/kika.png",
"streamUrl":"https://example.de/test/kika.m3u8",

Related

Spring Data JPA, change to one attribute of Many To Many entity is wrongly being shown on all other entities that share it

When I make changes to one attribute of an entity, it also somehow gets changed for every other entity that uses that entity. I have three entities as you can see below.
Students and courses need to have a many-to-many relationship between them and the course needs to have a one-to-many relationship with course lectures.
When I make changes to courses or course lectures that belong to a specific student by doing #Transactional student.getCourse().get(0).setTitle("whatever"), those changes are also reflected in other students who share the same course. I need help with this, thank you
The student class
public class Student {
#Id
#SequenceGenerator(
name = "student_sequence",
sequenceName = "student_sequence",
allocationSize=1
)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "student_sequence")
private Long studentId;
private String fullName;
#Column(name = "email_address", nullable = false)
private String email;
private String username;
private String password;
#ManyToMany(mappedBy = "students", fetch = FetchType.EAGER)
private List<Course> courses ;
public void addCourse(Course course) {
if (courses == null) {
courses = new ArrayList<>();
}
courses.add(course);
}
Course Class
public class Course {
#Id
#SequenceGenerator(
name = "course_sequence",
sequenceName = "course_sequence",
allocationSize = 1
)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "course_sequence")
private Long courseId;
private String title;
private double courseRating = 0;
private LocalDateTime createdAt = LocalDateTime.now();
private double completedProgress = 0;
#Embedded
private CourseInformation courseInformation;
#OneToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinColumn(name = "course_id", referencedColumnName = "courseId")
private List<CourseLecture> courseLectures;
#ManyToMany(
cascade = CascadeType.MERGE,
fetch = FetchType.LAZY
)
#JoinTable(
name = "student_course_mapping",
joinColumns = #JoinColumn(
name = "course_id",
referencedColumnName = "courseId"
),
inverseJoinColumns = #JoinColumn(
name = "student_id",
referencedColumnName = "studentId"
)
)
#ToString.Exclude
private List<Student> students;
There is no relationship mapping in the CourseLecture class.
This is not wrong, but just the way JPA works.
Technically it works, because they all reference the same instance as JPA guarantees to always return the same instance for a given class and id in single session.
If you don't want that you'd have to do the work either in different sessions, or you have to change your data model, so that each student has their own course. Of course this would be a strange model.
Update based on your comment:
Looks like indeed you need a different model, instead of Student -N-M-> Course you need something like Student -1-N-> Attendance -N-1-> Course, making the mapping table of your relationship into an entity and allowing it to store extra data that is specific to Student AND Course

Problem with relation #ManyToOne in Spring Boot, Hibernate, JPA

I have classes -> Country and City.
I wanna create works request, that when I call to get all countries, I will get all countries, with cities.
When I call to get all cities, I will get all cities with only countries from Country model.
I wanna add new cities with relation to countries.
My Country model class:
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#NotEmpty(message = "Name of country is mandatory.")
#Column(unique = true)
private String nameOfCountry;
#NotBlank(message = "Name of capital is mandatory.")
private String capital;
#Max(value = 17098242L, message = "Maximum value for population = 10000000000")
private Long surface;
private String countryCode;
private String telephoneCode;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "country_Id", updatable = false, insertable = false)
private List<CityModelDao> cityModelDao;
My City model class:
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#NotEmpty(message = "Name of country is mandatory.")
#NotNull
#Column(nullable = false, unique = true)
private String nameOfCity;
I know that I don't have here #ManyToOne, but I still do it wrong and now I haven't got more ideas.
My response from get countries:
And this is it what i want.
But when i call to get cities my response is:
Unfortunately I havent got information about country.
In db I have in cities information about fk from country:
Could you help me to do works relation? I ve tried something like:
CityModel:
#ManyToOne()
#JoinColumn(name = "country_Id")
private CountryModelDao countryId;
CountryModel:
#OneToMany(mappedBy = "countryId", orphanRemoval = true, cascade = CascadeType.PERSIST)
private List<CityModelDao> cityModelDao;
But it was wrong. And when I tried with above relation city, I got error.
Could You tell me how to do correct #ManyToOne in this case? What I do wrong?
Thanks
The most simplistic bi-directional OneToMany relationship model should be:
#OneToMany(mappedBy = "countryId")
private List<CityModelDao> cityModelDao;
You set Country as the owner of the relationship Country - City;
You expect an attribute 'countryId' in the CityModelDao;
#ManyToOne
#JoinColumn(name = "country_id")
private CountryModelDao countryId;
You will populate with data based on a join operation that will be executed on the column country_id from the CityModelDao table.
Of course, afterwards, you can enrich the annotations with orphan removal, cascade type etc.
LE:
You are using this via REST and you need to avoid the infinite loop.
Please update the relations to:
#JsonManagedReference
#OneToMany(mappedBy = "countryId")
private List<CityModelDao> cityModelDao;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "country_id")
private CountryModelDao countryId;

How to define a field for loading multiple images in spring boot model

I am developing an application for vehicle stock tracking system using spring boot, angular and mysql. Multiple images of the vehicle will be loaded from the interface. Normally, when there is only one image, I define a field of type byte [] with #lob annotation. But how can I keep it in the database when more than one image comes in. I think a relational structure is required but I couldn't.
public class User extends BaseEntity{
#Column(name = "TC_NUM", unique = true)
#NotNull
private String tcNum;
#Column(name = "EMAIL", unique = true)
#NotNull
private String email;
#Column(name = "USERNAME", unique = true)
#NotNull
private String username;
#Column(name = "PASSWORD")
#NotNull
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_ROLES",
joinColumns = #JoinColumn(name = "USER_ID"), inverseJoinColumns = #JoinColumn(name = "ROLE_ID"))
#NotNull
private Set<Role> roles;
}
Yes, you need to have One to Many relation.
Add another db table and entity for vehicle images. Let's say we called it VehicleImage:
#Entity
#Table(name="vehicle_image")
public class VehicleImage{
#Id
private Long id;
#Lob
#Column(name = "image", columnDefinition="BLOB")
private byte[] image;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
public VehicleImage() {}
// getters and setters
}
And add mapping to your User class like that:
public class User extends BaseEntity{
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<VehicleImage> vehicleImages;
public void addVehicleImage(VehicleImage vehicleImage) {
vehicleImages.add(vehicleImage);
vehicleImage.setUser(this);
}
public void removeVehicleImage(VehicleImage vehicleImage) {
vehicleImages.remove(vehicleImage);
vehicleImage.setUser(null);
}
//rest of your class
}
As you can see I've also added two utility methods to User class. For details, see this great post by Vlad Mihalcea -> https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
Now you can persist your images like that:
User user = new User();
user.addVehicleImage(
new VehicleImage (imageBytesArray)
);
entityManager.persist(user);

JPA repository findBy foreign key for ManyToMany relationship

I have the following relationship in the data base
Database Diagram
And these are the entities
Apartment.java
#Entity
#Table(name = "apartment")
#Data
public class Apartment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_apartment")
private long id;
#ManyToMany
#JoinTable(
name = "apartment_facility",
joinColumns = #JoinColumn(name = "id_apartment"),
inverseJoinColumns = #JoinColumn(name = "id_facility"))
#Column(name = "city")
private String city;
#Column(name = "country")
private String country;
}
Facility.java
public class Facility {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_facility")
private long id;
#Column(name = "name")
private String name;
}
My question is how can I create a findBy method in JpaRepository in order to find all Apartments having the required facilities
I'm thinking of the result of this request:
api/apartments/findByFacilities?facilities=gym,pool,parking
I have tried to do a query but couldn't quite get it. Also tried using Jpa as following
List<Apartment> findByFacilities(#RequestParam("facilities") List<String> facilities);
But I'm getting the following error. Is there a walkaround?
Failed to convert from type [java.lang.String] to type [java.lang.Long]
In your first code block List <Facility> facilities = new ArrayList<>(); I guess you forgot to add the code line.
When I look at your link below, I understand that you want to search by the name of the facility.
api/apartments/findByFacilities?facilities=gym,pool,parking
For this, your code in Repository should be as follows:
List<Apartment> findDistinctByFacilitiesNameIn(List<String> facilities);
If we don't add distinct supported keyword here, each record repeats as many as the facilities it contains.
If you want to search differently like, not like, etc., See the supported keywords inside method names table here: Query Creation document.

Spring MVC - loading data from database

I have two entities which are in many to many relation and I can't load Set <Category> categories. These fields are filled in the database.
#Entity
#Table(name="Product")
public class Product {
#Id
#GeneratedValue
private int idProduct;
private String status;
private String name;
#ManyToMany(fetch = FetchType.EAGER, mappedBy= "products")
private Set <Category> categories;
}
#Entity
#Table(name="Category")
public class Category {
#Id
#GeneratedValue
private int idCategory;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
private Set <Product> products;
}
This returns nothing int the view and the loop doesn't rotate even once.
<c:forEach items="${product.categories}" var="items">
<p>${items.name}</p>
</c:forEach>
I join the schema. Could someone write what to do to make it work, please?
enter image description here
This not works.
#Entity
#Table(name="Category")
public class Category {
#Id
#GeneratedValue
private int idCategory;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "Product_Category", joinColumns = {
#JoinColumn(name = "Category_idCategory", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "Product_idProduct",
nullable = false, updatable = false) })
private Set <Product> product;
Hibernate infers the sql that it needs to create from the annotation on the objects. Your product entity is telling hibernate to get information about the SQL join from the Category entity. This is from the mappedBy clause in the #ManyToMany annotation.
When it goes to the Category entity, it doesn't find what it needs, and it just gives an empty set.
Most #ManyToMany annotations are done with a join table. Here is a sample join table annotation
#JoinTable(name = "product_to_category", joinColumns = {
#JoinColumn(name = "category_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "product_id",
nullable = false, updatable = false) })
Depending on your schema, you might need to adjust the above annotation to get it working. It will give you a good start.

Resources