Encountered with JPA ManytoMany Relationship Build time errors with IntelliJ IDEA - spring-boot

I'm very new to this topic so i followed a tutorial. after following steps i got some build time errors.
I have imported javax persistence like this.
import javax.persistence.*;
Then the student model class
#Entity
#Table(name="STUDENT")
public class Student {
#Id
#GeneratedValue
private Integer studentId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(name="Enrollment", joinColumns = {#JoinColumns(name="student_id")},
inverseJoinColumns = {#JoinColumns(name="course_id")})
private List<Course> courses = new ArrayList<>();
}
The Course model class.
#Entity
#Table(name="COURSE")
public class Course {
#GeneratedValue
private Integer id;
#ManyToMany(mappedBy ="courses")
private List<Student> students = new ArrayList<>();
These are set of errors that i have got
incompatible types: javax.persistence.JoinColumns cannot be converted to javax.persistence.JoinColumn
cannot find symbol
symbol: method name()
location: #interface javax.persistence.JoinColumns
annotation #javax.persistence.JoinColumns is missing a default value for the element 'value'
Can anyone help me to get rid of this issues?
Thanks.

A #JoinTable annotation really has a joinColumns parameter, but the syntax you had used is not correct. If tables are joined by only column and inverse column you shouldn't use a #JoinColumns annotation. You have to change it in the following way:
#Entity
#Table(name="STUDENT")
public class Student {
#Id
#GeneratedValue
private Integer studentId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="Enrollment",
joinColumns = #JoinColumn(name="student_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name="course_id", referencedColumnName = "id"))
private List<Course> courses = new ArrayList<>();
}
and only if joining goes by more then one column you have to do something like this:
#Entity
#Table(name="STUDENT")
public class Student {
#Id
#GeneratedValue
private Integer studentId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="Enrollment",
joinColumns = #JoinColumns{
#JoinColumn(name="student_id", referencedColumnName = "id"),
#JoinColumn(name="another_id", referencedColumnName = "another_id")
},
inverseJoinColumns = #JoinColumn(name="course_id", referencedColumnName = "id"))
private List<Course> courses = new ArrayList<>();
}
Hope it will help

Related

#ManyToMany SpringBoot JSON 415 error can`t post to table or can`t get list(n>=1) because of infinite loop caused by relationship

I`ve been trying to create a #ManyToMany relationship between two entities (team&contest) but when i try to post to the contest controller api i get a error 415 saying that
Failed to evaluate Jackson deserialization for type [[simple type, class com.project.Contest.Contest]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot handle managed/back reference 'defaultReference': back reference type (`java.util.List<com.project.Contest.Contest>`) not compatible with managed type (com.project.Contest.Contest)
team :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "team")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#JsonBackReference
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
#ManyToMany(mappedBy = "teams", cascade = CascadeType.PERSIST)
private List<Contest> contests;
private String name;
private int wins, losses;
}
contest :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "contest")
public class Contest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToMany(cascade = CascadeType.PERSIST)
#JsonManagedReference
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
#JoinTable(
name = "team_contest",
inverseJoinColumns = #JoinColumn(name = "team_id"),
joinColumns = #JoinColumn( name = "contest_id")
)
private List<Team> teams;
#ManyToMany(cascade = CascadeType.PERSIST)
#JsonManagedReference
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
#JoinTable(
name = "contest_user",
joinColumns = #JoinColumn(name = "contest_id"),
inverseJoinColumns = #JoinColumn( name = "user_id")
)
private List<User> users;
}
i found out i can use #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id") instead of #JsonBackReference & #JsonManagedReference which helped me as it let me post to the database but then i refaced the problem that i can`t retrieve contest.teams[1] as because both objects have references to one another it creates some kind of an infinite loop as to get to the reference of the second object(contest.teams[1]) it needs to show the reference the contest.teams[0] has to the contest and soo forth. please help <3
This is the most famous bi-directional issue. To break up the loop while serialization, you can choose:
#JsonIgnore
#JsonIdentityInfo
JPA Projections
#EntityGraph
Or simply make the relationship uni-directional
thanks meridbt i was already using #JsonIdentityInfo but in the wrong place and i read a bit online and fixed my issue by doing this :
team :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "team")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class , property = "id")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany(mappedBy = "teams", cascade = CascadeType.PERSIST)
private List<Contest> contests;
private String name;
private int wins, losses;
}
contest :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "contest")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Contest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name = "team_contest",
inverseJoinColumns = #JoinColumn(name = "team_id"),
joinColumns = #JoinColumn(name = "contest_id")
)
private List<Team> teams;
#ManyToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name = "contest_user",
joinColumns = #JoinColumn(name = "contest_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private List<User> users;
}

access many to many relation in spring

I have a class called Tag:
#Entity
#Table(name = "tags")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
...
}
And a class called Post
#Entity
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "post_tags",
joinColumns = { #JoinColumn(name = "post_id") },
inverseJoinColumns = { #JoinColumn(name = "tag_id") })
private Set<Tag> tags = new HashSet<>();
...
}
It creates another table named post_tags.
How can I write a Controller to access that table as it is not similar a repository?
Is there more easy and convenient way to implement ManyToMany relationship ?
My pom.xml
You don't need to access that relation table manually. You can load load all Tag entities, and then load all the referenced Post entities.
The relation table is enterily managed by your ORM frameork.
But, if you still want to access the relation table, you can use native queries in your Spring Data JPA repository, e.g.
#Query(value="select post_id, tag_id from post_tags", nativeQuery=true)
List<PostTag> loadPostTags();
PostTag class is not a jpa-managed entity and must match the structue of the returned table:
public class PostTag {
private long postId;
private long tagId;
// getter, setter
}
Use this way
#Entity
#Table(name = "tags")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "post_tags",
joinColumns = { #JoinColumn(name = "id") },
inverseJoinColumns = { #JoinColumn(name = "post_id") })
private Set<Post> posts = new HashSet<>();
...
}
#Entity
#Table(name = "posts")
public class Post {
#Id
#Column(name = "post_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long postId;
...
}

How to link two tables by third?

I have three tables:
1) book: id (primary), name
2) shop: code (unique, not primary), name
3) book_shop: book_id, shop_id (code), price
I want to get shops in book like
book.getShop();
How to link this entities?
I tried:
#Data
#NoArgsConstructor
#Entity
#Table(name = "book", schema = "example")
#EntityListeners(AuditingEntityListener.class)
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BookShop> bookShop;
}
.
#Data
#NoArgsConstructor
#Entity
#Table(name = "shop", schema = "example")
#EntityListeners(AuditingEntityListener.class)
public class Shop {
#Id
private int code;
private String name;
#OneToMany(mappedBy = "shop", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BookShop> bookShop;
}
.
#Data
#NoArgsConstructor
#Entity
#Table(name = "book_shop", schema = "example")
public class BookShop implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Id
#ManyToOne
#JoinColumn(name = "book_id")
private Book book;
#Id
#ManyToOne
#JoinColumn(name = "shop_id")
private Shop shop;
#Column(name = "price")
private int fromDate;
}
This code return empty set: Book book = bookRepostiory.getById(1).get().getBookShop()
Try the many to many mapping implement like as below remove your book_shop table,
add this code to shop entity,
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinTable(name = "book_shop",
joinColumns = {#JoinColumn(name = "book_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "shop_id", nullable = false)})
private Set<Book> bookList = null;
add this code to book entity,
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL,
mappedBy ="bookList")
private Set<Shop> shopList=null;
if any issue inform!!
I would suggest, first - initialize the set in the entity
private Set<BookShop> bookShop = new HashSet<>();
Second, add fetch = FetchType.EAGER to your association, for e.g.
#OneToMany(fetch = FetchType.EAGER, mappedBy = "book", cascade = CascadeType.ALL)

3-way ManyToMany relationship in Hibernate

I have 3 tables: dealers, brands and services. The tables are joined using table dealer_brand_service.
Image of table relationships
Dealer has many brands
Brand has many services
Dealer:
#Entity
#Table(name = "dealers")
#Data
public class Dealer extends BaseModel {
#Column(name = "name")
private String name;
#ManyToMany
#JoinTable(name = "brand_services_dealer",
joinColumns = #JoinColumn(name = "dealer_id"),
inverseJoinColumns = #JoinColumn(name = "brand_id"))
private Set<VehicleBrand> brands;
}
VehicleBrand:
#Entity
#Table(name = "brands")
#Data
public class VehicleBrand extends BaseModel {
#Column(name = "name")
private String name;
#ManyToMany
#JoinTable(name = "brand_services_dealer",
joinColumns = #JoinColumn(name = "brand_id"),
inverseJoinColumns = #JoinColumn(name = "service_id"))
private Set<VehicleService> services;
}
VehicleService
#Entity
#Table(name = "services")
#Data
public class VehicleService extends BaseModel {
#Column(name = "name")
private String name;
}
I believe this is not the greatest approach on associations, so I would like to hear your suggestions how should I associate this entities the more proper way. Thank you very much.

Hibernate - map a row to either of two subclasses

I have a Superclass
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "entity_type", discriminatorType = DiscriminatorType.STRING)
#Table(name = "my_super")
public abstract class MySuper{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "super_id")
private Long id;
}
and two subclasses
#Entity
#DiscriminatorValue("sub1")
public class Sub1 extends MySuper {}
#Entity
#DiscriminatorValue("sub2")
public class Sub2 extends MySuper {}
Now if another class has both of these subclasses in it, is it possible to instantiate one of them through the same join table - same row ??
For instance:
#Entity
#Table(name = "caller")
public class Caller{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "caller_id")
Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Set<Sub1> sub1s;
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
Sub2 sub2;
}
I keep getting this error when trying to instantiate a Caller Object:
org.springframework.orm.hibernate3.HibernateSystemException: could not set a field value by reflection setter of my.test.Caller.sub2; nested exception is org.hibernate.PropertyAccessException: could not set a field value by reflection setter of my.test.Caller.sub2
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:679)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:102)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:368)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
nested exception is org.hibernate.PropertyAccessException: could not set a field value by reflection setter
i think this two line shouldnt be same
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Set<Sub1> sub1s;
--
#JoinTable(name = "caller_super", joinColumns = #JoinColumn(name = "caller_id"), inverseJoinColumns = #JoinColumn(name = "super_id"))
private Sub2 sub2;
because they are not same entity and their join table should be different. For ManyToMany relationship type is Sub1 but if you try to put them same table hibernate will try to put Sub2 to sub1s . But it is not vaild. Try to change your join table. For ManyToOne relationship.

Resources