Creating subcategories in kotlin spring boot - spring-boot

I need to implement categories and subcategories within my entities. Here's what I have so far and think it should be:
StockCategory.kt
#Entity
#Table(name = "table_categories")
data class StockCategory(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
val id: Long? = null,
#ManyToOne
#JoinColumn(name = "parentid")
val parent: StockCategory? = null,
#ManyToMany(mappedBy = "categories")
var stockItems: MutableList<StockItem> = mutableListOf(),
#OneToMany(mappedBy = "parent")
var childCategories: MutableList<StockCategory> = mutableListOf(),
)
StockItem.kt
#Entity
#Table(name = "table_stock")
data class StockItem(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "stock_item_id")
val id: Long? = null,
#Column(name = "stock_item_name")
var name: String = "New Item",
...
#ManyToMany
#JoinColumn(name = "item_category", referencedColumnName = "category_id")
var categories: MutableList<StockCategory> = mutableListOf(),
...
)
Now at the moment, this looks to be correct... At the very least Spring Boot is not complaining.
However, in terms of what to do next, I'm not sure. I know I need to implement a JpaRepository, of which I have the current:
StockCategoryRepository.kt
interface StockCategoryRepository: JpaRepository<StockCategory, Long> {
}
I also need to implement the relevant methods in my service class.
What exactly do I need to do next in order to get this to work and be able to use the information later on? Please also ELI5 too as although I have a decent amount of knowledge on this, I'm still not where I would like to be when it comes to this.
A few background bits if it makes it easier for you.
I'm using H2 as my database, Spring Boot and Kotlin as my language.

Related

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

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",

Constructor takes only ID of referenced entity, but getter returns the entity itself - possible?

Let's assume, I've a simple relationship of Student and Teacher Entities
I'm using Spring Boot with kotlin-jpa plugin, so data classes should work fine:
data class Student(
#Id
#GeneratedValue(strategy = IDENTITY)
val id: Long = null,
#OneToOne(fetch = FetchType.LAZY)
var responsibleTeacher: Teacher,
// ... other props
)
data class Teacher(
#Id
#GeneratedValue(strategy = IDENTITY)
val id: Long = null,
val name: String,
// ... other props
)
My problem: To construct an instance of Student, I always need an instance of the (already persisted) Teacher as well. As I only have the ID of the teacher at hand, I first need to obtain the full Teacher entity from the database and then pass it to the constructor of Student:
val responsibleTeacher = getTeacherFromDB(teacherId)
val student = Student(responsibleTeacher)
What I would like to to, is to pass only the Teacher ID in the constructor, but still being able to query the full Teacher entity from Student when calling the getter/property.
data class Student(
#Id
#GeneratedValue(strategy = IDENTITY)
val id: Long = null,
#Column(name = "responsible_teacher_id")
private var responsibleTeacherId: Long,
// ... other props
// pseudo-code, doesn't work!
// Should query the [Teacher] Entity by [responsibleTeacherId], specified in constructor
#OneToOne(fetch = LAZY)
var responsibleTeacher:Teacher
)
I was messing around with this for almost a day but couldn't find any working solution. Is there any?
For this purpose you can use a proxy that you can retrieve by calling entityManager.getReference(Teacher.class, teacherId)
Use #JoinColumn annotation
#Column(name = "responsible_teacher_id")
private var responsibleTeacherId: Long,
#JoinColumn(name = "responsible_teacher_id")
#OneToOne(fetch = LAZY, insertable = false, updatable = false)
var responsibleTeacher: Teacher

How to make IDs non-sequencial?

I have an entity
#Entity
data class Person (
#Id #GeneratedValue
val id: Long
)
But I noticed the values for id are consecutive. Is there a way to let Spring Boot make them more random?
You can create a custom identifier generator and use it.
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
#GenericGenerator(
name = "seq",
strategy = "com.java.generators.SequenceIdGenerator",
parameters = {...})
private String id;
...
}
Here you should create com.java.generators.SequenceIdGenerator by own
A good tutorial about this

OneToMany does not return values saved from other entity

I have entity structure:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = { CascadeType.ALL })
List<UserAgreement> userAgreements= new ArrayList<>();
}
#Entity
#Table(name = "user_agreements")
public class UserAgreement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "agreement_id")
private Agreement agreement;
}
#Entity
#Table(name = "agreements")
public class Agreement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "agreement", cascade = { CascadeType.ALL })
List<UserAgreement> userAgreements = new ArrayList<>();
}
I am using Spring Boot with JpaRepository. When I use AgreementRepository extends JpaRepository<Agreement, Long> to save Agreement and related UserAgreement, it works well and cascades necessary fields to DB:
agreement.getUserAgreements().add(new UserAgreement(user, agreement, status));
agreementRepository.save(agreement);
However, after save, if try to retrieve user.getActiveUserAgreements(), I get empty list. It does not refresh.
How to force User entity to get List<UserAgreement> which was saved from other side?
From the Wikibooks: OneToMany
The relationship is bi-directional so, as the application updates one
side of the relationship, the other side should also get updated, and
be in sync. In JPA, as in Java in general it is the responsibility of
the application, or the object model to maintain relationships. If
your application adds to one side of a relationship, then it must add
to the other side.
That means you need to assign the UserAgreement to the User when you create the relation.
It looks like many-to-many association. You might probably drop UserAgreement class. Anyway, to support it you have to write helper methods addAgreement(), removeAgreement() etc. See more details here https://vladmihalcea.com/the-best-way-to-use-the-manytomany-annotation-with-jpa-and-hibernate/

Bidirectional ManyToMany infinite recursion

I am using spring boot and spring data rest and have two entities, Exam and Subject which are defined as follows:
public class Exam {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="exam_id")
Integer examId;
#Column(name="exam_name")
String examName;
#ManyToMany(mappedBy = "exams")
Set<Subject> subjects = new HashSet<>(0);
}
public class Subject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "subject_id")
Integer subjectId;
#Column(name = "subject_name")
String subjectName;
#ManyToMany
#JoinTable(
name = "subject_exam",
joinColumns = {#JoinColumn(name = "subject_id", updatable = false, nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "exam_id", updatable = false, nullable = false)}
)
Set<Exam> exams = new HashSet<>(0);
}
Now I have defined projection for subject as follow:
#Projection(name="detail", types={Subject.class})
public interface SubjectDetailProjection {
Integer getSubjectId();
String getSubjectName();
Set<ExamDetailProjection> getExams();
}
Detail projection for exam has also been defined in the same manner.
Now I am getting infinite recursion when calling api for this projection. How can I avoid this issue?
You have to delete, in your ExamDetailProjection, your referente to Subject.
You must to define one package of projections to your Subject and another package of projections to your Exams.
The exam projection will have a subject projection without reference to exam and the same in the other direction.

Resources