How can I use #JoinTable to join three tables?
I have three tables:
user:
id, name
post:
id, date
company:
id, name
I want to create a new table with the following columns:
user_id, post_id, company_id.
I used:
#JoinTable(name = "new_table", joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "post_id"))
However, I am not sure how to add the third column.
You must not will use #JoinTable annotation. The #JoinTable annotation is used only to #ManyToMany relationship.
You need create a new Entity with three field, and each field must has the #ManyToOne and #JoinColumn annotation.
For Instance:
#Entity
#Table(name = "table_name")
class NewEntity {
//Id and anothers fields
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "post_id")
private Post post;
#ManyToOne
#JoinColumn(name = "company_id")
private Company company;
//getters and setters
}
Related
Initial Situation:
I have these two entities, GroceriesList and Product.
GroceriesList:
A GroceriesList can have several products.
#Entity
#Table(name = "lists")
public class GroceriesList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "list_id")
private Long listId;
#Column(name = "list_name")
private String listName;
#ManyToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "lists_products",
joinColumns = #JoinColumn(name = "product_id", referencedColumnName = "list_id"),
inverseJoinColumns = #JoinColumn(name = "list_id", referencedColumnName = "product_id")
)
private Set<Product> products;
}
Products:
A Product can be allocated to several GroceriesLists
#Entity
#Table(name = "products")
public class Product {
public enum Category {
Dairy,
Fruit,
Vegetable,
Meat,
Grains
}
// Product ID Column, GenerationType.Identity refers to auto incr in Postgres
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id")
private Long productId;
#Column(name = "product_name")
private String productName;
#Column(name = "product_vendor")
private String productVendor;
#Enumerated(EnumType.STRING)
#Column(name = "product_category")
private Category productCategory;
#Column(name = "product_storedquantity")
private Integer productStoredQuantity;
#JsonIgnore
#ManyToMany(mappedBy = "products")
private List<GroceriesList> groceriesLists;
}
The entities and relationships are stored in three different tables in Postgres(One for Lists, one for products, and one mapping table for the relationships).
The mapping table:
Mapping Table "lists_products"
A sample Product:
Sample Product with Id=4
The problem: I'm trying to create new lists, and that works. However, as you can spot from the Mapping Table Image, Hibernate inserts the IDs into the wrong columns. The list_id in the mapping table is currently getting the product_id, and the product_id in the mapping table is getting the list_id.
I have tried to change the order of column names & references in the #JoinTable annotation in the GroceriesList Entity. However, that throws a mapping Error. I assume my error lies somewhere in that annotation. Interestingly, Hibernate uses the correct SQL-Query. What am I missing here?
After consulting fladdimir's solution, it gave me an idea and I solved my issue. The problem lied within the #JoinColumn annotation, where I thought the "name = ..." property refers to the column name in the database.
#ManyToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "lists_products",
joinColumns = #JoinColumn(name = "product_id", referencedColumnName = "list_id"),
inverseJoinColumns = #JoinColumn(name = "list_id", referencedColumnName = "product_id")
)
private Set<Product> products;
However, that property refers to the declared variable inside the Entity in Spring Boot, i.e. listId
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "list_id")
private Long listId;
So, the working implementation of the #JoinTable Annotation should look like the following, where "name = ..." uses that variable name:
#ManyToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "lists_products",
joinColumns = #JoinColumn(name = "listId", referencedColumnName = "list_id"),
inverseJoinColumns = #JoinColumn(name = "productId", referencedColumnName = "product_id")
)
private Set<Product> products;
I have entity Recipe:
public class Recipe extends BaseEntity{
// other fields
#Column(name = "ingredients")
#ManyToMany
#JoinTable(name = "ingredients_recipes",
joinColumns = {#JoinColumn(name = "recipe_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "ingredient_id", referencedColumnName = "id")})
private List<Ingredient> ingredients;
Ingredient:
public class Ingredient extends BaseEntity {
private String name;
#Column(name = "recipes")
#ManyToMany(mappedBy = "ingredients", fetch = FetchType.LAZY)
private List<Recipe> recipes;
I need to select all recipes that contain all or less ingredients from a List. I tried
Set<Recipe> findAllByIngredientsIn(List<Ingredient> ingredientList);
but it checks that the recipe has each ingredient individually. If I give only salt in ingredientList, it return recipes with salt and peper(or other ingredients), but I need to get recipes that only have salt. Is there a jpa repository method that I can use, or I need only a custom query?
Is there a jpa repository method that I can use, or I need only a custom query?
No you'll have to come up with your own query and use it in a #Query annotation.
I want to join these 3 Tables.
Here you see my Person Entity
#Entity
#Table(name = "Person", schema = "public")
public class PatientEntity {
#Id
#Column(name = "id")
private Long id;
#Column(name = "lastname")
private String name;
#OneToMany
#JoinTable(name = "person_contact", joinColumns = { #JoinColumn(name = "person_id") }, inverseJoinColumns = { #JoinColumn(referencedColumnName = "id") })
#Column(name = "contact")
private Set<ContactEntity> contacts;
//Getter Setters
And here is my contact entity:
#Entity
#Table(name="contact",schema="public")
public class ContactEntity {
#Id
#Column(name="id")
private Long id;
#Column(name="phone")
private String phone;
//Getter Setters
I just read the Persons from the Table with findById with a Spring JPARepository, but there is no Contact mapped. There is no error during my HTTP request, but instead of a Contact there is null and this error message:
com.sun.jdi.InvocationException occurred invoking method.
The business case is, that every Person can have one or more contact. Is it possible to make it with JPA Annotations or do I need to map it by myself with a JPQL? Or should I create an Entity for the middle table? (person_contact)
The Database is a PostgreSQL Database.
There is this notification too in the Log:
ERROR: column contacts0_.contacts_id does not exist
Perhaps you meant to reference the column "contacts0_.contact_id".
Position: 306
Your #JoinTable has incorrect #JoinColumn specifications and corresponds to the following ddl.
create table person_contact (person_id bigint not null, contacts_id bigint not null, primary key (person_id, contacts_id))
To map your db structure, use following (note removed #Column annotation)
#OneToMany
#JoinTable(name = "person_contact", joinColumns =
{
#JoinColumn(name = "person_id", referencedColumnName = "id"),
},
inverseJoinColumns = {
#JoinColumn(name = "contact_id", referencedColumnName = "id")
})
private Set<ContactEntity> contacts;
I also encourage you to read https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ and reconsider a db structure without a join table (depending on your load and the effort to make this db change)
How to make Bridge table using annotation using Hibernate/JPA configuration.
1: BookModel
2: UserModel
now I have to create a bridge table by these two by fields
book_id and user_id
You are trying to implement Many to Many relationship between your entities. So for this, If you have list of Books in User model, then you can annotate the list as following:
public class UserModel {
#ManyToMany(cascade = CascadeType.REMOVE)
#JoinTable(name = "book_user_table", joinColumns = { #JoinColumn(name = "book_id") }, inverseJoinColumns = { #JoinColumn(name = "user_id") })
private List<BookModel> books;
//Getters and setters
}
and in BookModel, if you have list of users, then you need to use #mappedBy() annotation like following:
#ManyToMany(mappedBy = "books")
private List<UserModel> users;
This will generate the 3rd table, which will have the name book_user_table, with your required columns. See this for detailed explanation: https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html
From what I understand from your quesrtion is, you want to know how to map book and user so that there is many to many association between these entities.
If so, you need to specify #ManyToMany on both the associations and make one of them as inverse and the other end with #JoinTable. An example mapping here. Snippet below.
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "Employee_Project",
joinColumns = { #JoinColumn(name = "employee_id") },
inverseJoinColumns = { #JoinColumn(name = "project_id") }
)
Set<Project> projects = new HashSet<>();
On the inverse end,
#ManyToMany(mappedBy = "projects")
private Set<Employee> employees;
I'm writing a service with JPA and Postgres db. I have a class named Student:
public class Student {
#id
private String id;
private String firstName;
private String lastName;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "student_phone", joinColumns = #JoinColumn(name = "student_id"), inverseJoinColumns = #JoinColumn(name = "phone_id"))
private Set<Phone>;
// Setter and Getter
}
And a Phone class:
public class Phone {
#id
private String id;
private String number;
// Setter and Getter
}
Now there will be three tables in my db, following are columns in them:
student: id, first_name, last_name
phone: id, number
student_phone: student_id, phone_id
Now every time I query a student from the db, the result contains the corresponding phones. Can I just get the content in student table? Some times I don't want to extend the phone information of a student.
Use lazy loading
#OneToMany(fetch=FetchType.LAZY,
or
write native queries
#OneToMany is default lazy fetch
I assume that you use REST service then:
try this if you use XML in JAXB with #XmlTransient:
#XmlTransient
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "student_phone", joinColumns = #JoinColumn(name = "student_id"), inverseJoinColumns = #JoinColumn(name = "phone_id"))
private Set<Phone>;
or if you use json you can add this #JsonIgnore