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.
Related
I'm using Spring JPA and MySQL as the database. I have trouble with self-referencing its own entity.
I know the code below would do self-referencing, but it actually creates a new table to do so.
#Getter
#Entity
#NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member extends BaseTimeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "member_id")
private Long id;
#ManyToMany
#JoinColumn(name = "followings_id")
private List<Member> followings = new ArrayList<Member>();
#ManyToMany
#JoinColumn(name = "followers_id")
private List<Member> followers = new ArrayList<Member>();
#ManyToMany
#JoinColumn(name = "blocked_id")
private List<Member> blocked = new ArrayList<Member>();
...
}
Question: I'm wondering if I can do this in a single table(which would be the member table) without creating a new table to do many-to-many self-referencing.
It is possible,
Instead of using the #ManyToMany annotation, you can use the #OneToMany and #ManyToOne annotations to create the self-referencing relationship
#Getter
#Entity
#NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member extends BaseTimeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "member_id")
private Long id;
#OneToMany(mappedBy = "follower")
private List<Follow> followings = new ArrayList<>();
#OneToMany(mappedBy = "following")
private List<Follow> followers = new ArrayList<>();
#OneToMany(mappedBy = "blocker")
private List<Block> blocked = new ArrayList<>();
}
#Entity
#Getter
#NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Follow {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "follower_id")
private Member follower;
#ManyToOne
#JoinColumn(name = "following_id")
private Member following;
}
#Entity
#Getter
#NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Block {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "blocker_id")
private Member blocker;
#ManyToOne
#JoinColumn(name = "blocked_id")
private Member blocked;
}
Now Follow and Block entities represent the many-to-many relationships between Member entities and follower and following properties in the Follow entity represent the two sides of the many-to-many relationship, and the same is for blocked and blocker.
I am trying to use Hibernate to remove an entity however I get an error: Cannot delete or update a parent row: a foreign key constraint fails
The setup is that I have an abstract class A and two classes (B and C) which extend A. B contains a list of C's (unidirectional relationship). And there is a function to delete A by its ID.
Note: Stuff has been removed for brevity.
#Entity
public class B extends A {
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
joinColumns = #JoinColumn(name = "B_A_id"),
inverseJoinColumns = #JoinColumn(name = "C_A_id"))
List<C> cList;
}
#Entity
public class C extends A {
(no reference to B)
}
The issue is that when the deleteAByFixedId is called where A is a C, it tries to delete the C before it deletes the B which references it and therefore I get a foreign key constraint failure.
What am I doing wrong?
The answer will still be updated.
Links:
The best way to use the #ManyToMany annotation with JPA and Hibernate
Hibernate Inheritance Mapping
#ManyToMany
Unidirectional example:
User.java
#Entity
public class User {
#Id
#GeneratedValue
#Column(name = "user_id")
private long id;
...
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
public void addRoles(Role role) {
roles.add(role);
}
public void removeRoles(Role role) {
roles.remove(role);
}
}
Role.java
#Entity
public class Role {
#Id
#GeneratedValue
#Column(name = "role_id")
private int id;
#Column(name = "role")
private String role;
}
Bidirectional example:
Trader.java:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#ToString(exclude = "stockmarkets")
#Table(name = "trader")
public class Trader {
#Id
#GeneratedValue
#Column(name = "trader_id")
private Long id;
#Column(name = "trader_name")
private String traderName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "TRADER_STOCKMARKET",
joinColumns = { #JoinColumn(name = "trader_id") },
inverseJoinColumns = { #JoinColumn(name = "stockmarket_id") })
private Set<Stockmarket> stockmarkets = new HashSet<>();
/*
We need to add methods below to make everything work correctly
*/
public void addStockmarket(Stockmarket stockmarket) {
stockmarkets.add(stockmarket);
stockmarket.getTraders().add(this);
}
public void removeStockmarket(Stockmarket stockmarket) {
stockmarkets.remove(stockmarket);
stockmarket.getTraders().remove(this);
}
}
Stockmarket.java
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#ToString(exclude = "traders")
#Table(name = "stockmarket")
public class Stockmarket{
#Id
#GeneratedValue
#Column(name = "stockmarket_id")
private Long id;
#Column(name = "stockmarket_name")
private String stockmarketName;
#ManyToMany(mappedBy="stockmarkets")
private Set<Trader> traders = new HashSet<>();
/*
We need to add methods below to make everything work correctly
*/
public void addTrader(Trader trader) {
traders.add(trader);
trader.getStockmarkets().add(this);
}
public void removeTrader(Trader trader) {
traders.remove(trader);
trader.getStockmarkets().remove(this);
}
}
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;
...
}
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)
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