I have a case with Many to Many relationship
Following is the Book class with isbn as unique identifier
#Entity
#Table(name = "book")
public class Book implements Serializable {
private static final long serialVersionUID = -4643154384069203197L;
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "isbn")
private String isbn;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "book_author",
joinColumns = { #JoinColumn(name = "bookId", referencedColumnName = "id") },
inverseJoinColumns = { #JoinColumn(name = "authorId", referencedColumnName = "id") })
private Set<Author> authors = new HashSet<>();
// Getters and setters
// hashcode and equals using only isbn
Following is the Author class with email as unique identifier
#Entity
#Table(name = "author")
public class Author implements Serializable{
private static final long serialVersionUID = -6907306347041383886L;
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "email")
private String email;
#Column(name = "book")
#ManyToMany(mappedBy = "authors")
private Set<Book> books = new HashSet<>();
//getters and setters
// hashcode and equals using only "email"
I am able to achieve unique Book, as the Set checks the isbn and removes all the duplicates.
Since every Book can have many authors and vice versa I tried with Many to Many rekationship.
The problem comes with the unique email, Every book has an individual authors Set. How can I achevie author with unique email in the author table?
Help would be appreciated. Thanks in Advance
Change you #Column(name="email") into #Column(unique = true)
name="email"is redundant since by default it will grab the name of the attribute which is anyways email
Related
I am working on a project, trying to create an AudioBook website. I am stuck on this part and i cant find answers on the internet.
The problem is when i get user, and list of favorite books, each book has list of user rating, from all of the users. And also there is a list of UserRatings that contains all of ratings from that user. I dont want neither.
Basically, is there a way to make it so when i do a search for list of books, every book object contains UserRating(One user rating) from a specific User that is logged in?
#Entity
#Table(name = "user_rating")
public class user_rating {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Integer Id;
#Column
private String rating;
#ManyToOne
#JoinColumn(name = "user_id")
private users user;
#ManyToOne
#JoinColumn(name = "book_id")
private books book;
#Entity
#Table(name = "users")
public class users {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column
private String name;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "date_of_creation")
private java.sql.Date dateOfCreation;
#Column
private String password;
#JsonManagedReference
#ManyToMany
#JoinTable(
name = "favourites",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "book_id", referencedColumnName = "id")
)
private Set<books> favourites = new HashSet<>();
#OneToMany(mappedBy ="user")
private List<user_rating> UserRating = new ArrayList<>();
#Entity
#Table(name = "books")
public class books {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column
private String name;
#Column
private String description;
#Column(name = "date_of_creation")
private java.sql.Date date_of_creation;
#Column
private String text_file;
#JsonBackReference
#ManyToMany(mappedBy = "favourites")
private Set<users> users = new HashSet<>();
#JsonManagedReference
#ManyToMany()
#JoinTable(
name = "book_tags",
joinColumns = #JoinColumn(name = "book_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id")
)
private List<tags> tags = new ArrayList<>();
#OneToOne
#PrimaryKeyJoinColumn
private audio_file audioFile;
#OneToMany(mappedBy = "book")
private List<user_rating> UserRatings = new ArrayList<>();
I have already a user model.
Now I have created a movie model, my requirement is that whenever any existing user is going to add any movie, at that time user_id and movie_id will be store in the movie_added_by table.
Here user model needs to map one to many to movie_added_by and similarly, the movie will be mapped to movie_added_by.
For better understanding, you can refer to the DB diagram.
I really don't know how can I do by using hibernate annotation
The user model is like this:
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
}
The movie model is like this:
#Getter
#Setter
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
}
You probably want to create a #ManyToMany relationship between the entities. There are 2 ways of doing it (with intermediary table created explicitly or by Hibernate.
In simple approach your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#ManyToMany(cascade = CascadeType.Persist)
#JoinTable(name="user_movie",
joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="movie_id)})
private Set<Movie> movies = new HashSet<>();
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#ManyToMany(cascade = CascadeType.Persist, mappedBy = "movies" //field from the user class responsible for mapping)
private Set<User> users = new HashSet<>()
}
So basically here you tell Hibernate to create an intermediary table and keep there correlated id's of those 2 entities. Couple of other notes here:
a) you might want to change the id variable type from Integer to Long in case your entities grow;
b) If you have annotated a column with #Id, you don't have to use unique=true and nullable = false in the column annotation;
c) remember about implementing no-args constructor;
d) remember to exclude relationship fileds from the equals(), hashCode() and the toString() methods;
There is another way, where you explicitly create a model for the table keeping relationships. This might become handy, when it turns out that You need to keep more data in the 'relationship table'. In that case, Your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#OnetToMany(cascade = CascadeType.PERSIST, mappedBy = "user")
private Set<AddedMovie> addedMovies = new HashSet<>()
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "movie")
private Set<AddedMovie> moviesAddedByUser = new HashSet<>();
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Entity
public class AddedMovie{
#Id
#GeneratedValue
private Long id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "movie_id")
private Movie movie;
// sine this entity has now its own lifecycle, you can add more fields here
private Integer rating;
private LocalDateTime movieAddedOn;
}
I am facing a hibernate problem in updainting the join table in one to many mapping with hibernate. Below are my two entity class and join table entity class.
ArticleCategoryMap.java
#Entity
#Table(name = "ARTICLECATEGORYMAP")
public class ArticleCategoryMap {
private static final long serialVersionUID = -5653708523600543988L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column ( name = "id")
Long id;
#ManyToOne(targetEntity = Article.class, fetch = FetchType.EAGER, optional = true, cascade = CascadeType.PERSIST)
#JoinColumn(name = "ARTICLE_ID", nullable = true, insertable = true, updatable = true)
private Article article;
#ManyToOne(targetEntity = Category.class, fetch = FetchType.EAGER, optional = true, cascade = CascadeType.PERSIST)
#JoinColumn(name = "CATEGORY_ID", nullable = true, insertable = true, updatable = true)
private Category category;
//setter and getter
}
Article.java
#Entity
#Table(name = "ARTICLE")
public class Article {
private long id;
private String title;
private String description;
private String keywords;
private String content;
#Id
#GeneratedValue
#Column(name = "ARTICLE_ID")
public long getId() {
return id;
}
//setter and getter
}
Category.java
#Entity
#Table(name = "CATEGORY")
public class Category {
private long id;
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(
name = "ARTICLECATEGORYMAP",
joinColumns = #JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = #JoinColumn(name = "ARTICLE_ID")
)
#CollectionId(
columns = #Column(name="id"),
type=#Type(type="long"),
generator = "sequence"
)
private Collection<Article> articles;
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
public long getId() {
return id;
}
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(
name = "ARTICLECATEGORYMAP",
joinColumns = #JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = #JoinColumn(name = "ARTICLE_ID")
)
#CollectionId(
columns = #Column(name="id"),
type=#Type(type="long"),
generator = "sequence"
)
// setter an getter
}
Now suppose first time I have 2 elements in article table which is mapping to one entry of the category table. so the join table will look something like
Now due to some reason, I want to update the entry where the article entry will map to a new category ID. So the final DB should look like
So My problem Is how can I update this join table.
If you want one to many relationship (1 category have many articles and 1 article to 1 category) you dont need a join table.
The entity classes should look like that:
Category Entity:
Contains a Set of articles:
#Entity
#Table(name = "CATEGORY")
public class Category {
private long id;
private String name;
#OneToMany(mappedBy="category")
private Set<Article> articles;
......
}
Article Entity:
#Entity
#Table(name = "ARTICLE")
public class Article {
#ManyToOne
#JoinColumn(name="id", nullable=false)
private Category category;
private long id;
private String title;
private String description;
private String keywords;
private String content;
.......
}
For more details take a look at hibernate-one-to-many. Hope this helps.
Also move annotation from methods to fields. This:
private long id;
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
public long getId() {
return id;
}
Should be:
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
private long id;
public long getId() {
return id;
}
Many to many relationship:
At your database you have 3 tables:
CATEGORY
ARTICLE
ARTICLECATEGORYMAP (join table)
For many to many relationship entities would be:
Category Entity:
#Entity
#Table(name = "CATEGORY")
public class Category {
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
private long id;
private String name;
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "ARTICLECATEGORYMAP",
joinColumns = { #JoinColumn(name = "CATEGORY_ID") },
inverseJoinColumns = { #JoinColumn(name = "ARTICLE_ID") }
)
Set<Article > articles = new HashSet<>();
.....
}
Article Entity:
#Entity
#Table(name = "ARTICLE")
public class Article {
#Id
#GeneratedValue
#Column(name = "ARTICLE_ID")
private long id;
private String title;
private String description;
private String keywords;
private String content;
#ManyToMany(mappedBy = "articles")
private Set<Category> categories = new HashSet<>();
.......
}
For more info take a look at many-to-many ralationship
I have a scenerio where one staff can belong to multiple organisation and for each organisation he can have different role. How can i map this in jpa?
Staff.java
public class Staff {
#ManyToMany
#JoinTable(name="STAFF_ORGANIZATION",joinColumns=#JoinColumn(name="staff_id"),inverseJoinColumns=#JoinColumn(name="organization_id"))
private Set<Organization> organizations;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
}
Organization.java
public class Organization {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column
private String OrganizationName;
#ManyToMany(mappedBy="organizations")
private Set<Staff> staff;
}
StaffRoles.java
public class StaffRoles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column
#Enumerated(EnumType.ORDINAL)
private Roles roles;
public enum Roles {
USER(100), ADMIN(200);
private int values;
Roles(int values) {
this.values = values;
}
public int getValues() {
return values;
}
}
Can anyone please help me in mapping the roles to the staff. So many staff can belong to many organisation and for each organisation he can have different role.
Any help will be highly appreciated!
Althought this question is not written clearly I will answer your question based on what I have understood.
Below is an ER-diagram for how your tables might look like.
Now you just need to create the classes needed.
User.java
Organization.java
UserOrganization.java
UserRole.java
Connect the right instance variables now via ManyToMany and OneToOne to achieve your goal.
EDIT:
After the question has been updated with more specific information, I can aid more in this answer. First make a StaffOrganizationRoles class that will sit between StaffOrganization and Roles. Next, make StaffOrganization sit between Staff and Organization, which means that instead of ManyToMany it will be ManyToOne from Staff -> StaffOrganization, and ManyToOne from Organization > StaffOrganization.
public class StaffOrganizationRoles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "stafforganization_id", unique = false, nullable = false)
private StaffOrganization user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "role_id", unique = false, nullable = false)
private Role role;
}
Staff organization class:
public class StaffOrganization {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "staff_id", unique = false, nullable = false)
private Staff staff;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "organization_id", unique = false, nullable = false)
private Organization organization;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "staffOrganization")
private Set<StaffOrganizationRoles> staffOrganizationRoles = new HashSet<>(0);
}
I've edited the picture above to represent the new ER-diagram.
Hope this will help you now.
I have 4 entities: Play, Actor, Play-representation and Category.
Each play belongs to a category and play-representation associates a play with a theater and a number of actors at a given time.
Here are the entities:
#Entity
#Table(name = "category")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy="category")
private List<Play> playList = new ArrayList<Play>();
#Entity
#Table(name = "actor")
public class Actor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "description")
private String description;
#Column(name = "profile_picture")
private String profilePicturePath;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "play_representation_category",
joinColumns = {#JoinColumn(name = "actor_id")},
inverseJoinColumns = {#JoinColumn(name = "play_representation_id")})
private Set<PlayRepresentation> playRepresentations = new HashSet<>(0);
#Entity
#Table(name = "play")
public class Play {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#NotNull
#Column(name = "name")
private String name;
#NotNull
#Column(name = "description")
private String description;
#Column(name = "image_paths")
private String imagePaths;
#NotNull
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Category category;
#Entity
#Table(name = "play_representation")
public class PlayRepresentation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "play_id")
private Play play;
#OneToOne
#JoinColumn(name = "theater_id")
private Theater theater;
#Column(name = "date")
private Timestamp airingDate;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "play_representation_category",
joinColumns = {#JoinColumn(name = "play_representation_id", nullable=false)},
inverseJoinColumns = {#JoinColumn(name = "actor_id", nullable=false)})
private Set<Actor> actors = new HashSet<>(0);
The issue I'm having is that hibernate is trying to find a relationship between play_representation and category! I've been trying to persist the relationship for the plays but it seems I got that wrong and can't figure out the best way to do it...It's a postgresql db by the way.
I am still learning, so if you have any other tips regarding the code I've shared, please let me know!
Edit: error is:
org.postgresql.util.PSQLException: ERROR: relation "play_representation_category" does not exist
Position: 281
I didn't need a mappedBy, it was actually a typo - I wrote play_representation_category instead of play_representation_actors. Pretty stupid, huh? At least I finally found it :)