I got a many to many relationship between two tables. I mapping this using the annotation #ManyToMany.
Table1 ---> Relational Table ---> Table 2
Using hibernate i don't have to create any entity for the relationship table so I have.
Entity 1(Table 1) ---> Entity 2(Table 2)
But my problem is that i have another table and i must do a relationship between this 3th table and the relation table between the previous and i don't have any entity for do the relation.
Table 3 ---> Relational Table
I mean this 3th table got a foreign key with the relational table that i used before...
How can i accomplishment this? Sorry for my english
Thanks
here is an example.
Imagine we have Products and Categories
You have 2 entities Product and Category
Using the hibernate code-first approach, the entities will be like this:
#Entity
#Table(name = "products")
public class Product implements Serializable {
private Long id;
....
#ManyToMany
#JoinTable(name = "categories_products",
joinColumns = #JoinColumn(name = "category_id",
referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "product_id",
referencedColumnName = "id"))
private Set<Category> categories;
...
And here is the code for the code
public class Category {
private Long id;
private String name;
...
}
By using the annotation #JoinTable a third table will be created with the following constraints. It will have category_id which will point to Categories id and product_id referring to Products id.
Related
In my database, I have a column called "specialist_id". Based on the value of another column in the same table, it is either the id of a user entity (references some user table), or it is the id of a specialist entity (references specialist table). My question is, how can I do this in hibernate mapping? For example, I want to do something like below.....Is it possible?
#ManyToOne(fetch = FetchType.LAZY)
#Fetch (FetchMode.SELECT)
#JoinColumn(name = "SPECIALIST_ID")
private ApplicationUser specialist;
#ManyToOne(fetch = FetchType.LAZY)
#Fetch (FetchMode.SELECT)
#JoinColumn(name = "SPECIALIST_ID")
private DivSpecialist divSpecialist;
A. I have a entity EntityA like below:
Table - EntityA(id long PK, name varchar)
#Entity #Table
EntityA{
#Id
long id;
String name;
}
B. Based on this I want to fetch data in below class through JPA(using unidirectional mapping):
#Entity #Table
EntityMap{
long id;
#OneToOne
EntityA entity;
#OneToMany
List<EntityA> mappedEntity;
}
C. To make it work for now I have created an entity like below:
Table - entity_map(id long pk, source_entity_id long FK-EntityA_id, target_entity_id long FK-EntityA_id)
#Entity #Table
EntityMap{
#Id
long id;
#OneToOne
#JoinColumn(name = "source_entity_id")
EntityA sourceEntity;
#ManyToOne
#JoinColumn(name = "target_entity_id")
EntityA targetEntity;
}
This is working as expected but I need entity explained in #B. Any suggestion?
A #OneToMany relationship can be archieved by
#JoinColumn(name = "entity_map_id")
#OneToMany
List<EntityA> mappedEntity;
In your EntityA table you need a column entity_map_id and a foreign key (entity_map_id) references EntityMap(id) constraint.
If you cannot change the EntityA table you need a #JoinTable to perform the mapping:
#JoinTable(name = "JOIN_TABLE", joinColumns = {#JoinColumn(name = "MY_ENTITY_MAP_FK")}, inverseJoinColumns = {#JoinColumn(name = "ENTITY_A_FK")})
#OneToMany
List<EntityA> mappedEntity;
The join table contains two columns ENTITY_A_FK and MY_ENTITY_MAP_FK:
create table JOIN_TABLE(
ENTITY_A_FK integer not null,
MY_ENTITY_MAP_FK integer not null
);
I have a relation between Accommodation and Booking classes, and also I set a foreign key in booking table.
ALTER TABLE `project`.`booking`
ADD INDEX `fk_accId_fk_idx` (`accommodation` ASC);
ALTER TABLE `project`.`booking`
ADD CONSTRAINT `fk_accId_fk`
FOREIGN KEY (`accommodation`)
REFERENCES `project`.`accommodation` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
Accommodation class:
#Entity
....
public class Accommodation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private BigInteger id;
#OneToMany(mappedBy = "accommodation", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
#JsonManagedReference
private List < Booking > bookings;
......getters setters
}
Booking class:
#Entity
public class Booking implements Serializable {
......
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "bookings", nullable = true)
#JsonBackReference
private Accommodation accommodation;
....getters setters
}
When I execute a query for listing accommodations, I get unknown column in field list error.
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'bookings7_.bookings' in 'field list'
Even I set the relation and define the foreign key in table, what is the reason that I get this error?
Try to define your join-table mapping manually in JPA. Drop your schema and let JPA create your tables:
Accommodation class
#OneToMany(mappedBy = "accommodation", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
#JsonManagedReference
private List < Booking > bookings;
Booking class
#ManyToOne(fetch = FetchType.EAGER)
#JoinTable(name = "accommodation_booking_join_table",
joinColumns = {#JoinColumn(name="booking_id")},
inverseJoinColumns = #JoinColumn(name = "accommodation_id"))
#JsonBackReference
private Accommodation accommodation;
Try changing your column names to lower case in your db and entity class.
I had a situation like that, and I solved it by changing the field's position on the query. Looks like it's a MySQL bug, like (or the same as) the one mentioned on this post:
https://bugs.mysql.com/bug.php?id=1689
The description of this MySQL bug mentioned a similar workaround solution: "I found that by swapping that field's position in the table with another field that it worked OK."
I have a RECIPE table that has OneToMany relationship with the INGREDIENT table because a single recipe can have many ingredients. The issue is that if a user deletes an ingredient (which sets all fields (ingredient_id and ingredient) to NULL by frontend), then the row containing relationship of both the tables RECIPE_INGREDIENT is deleted but the row in the Ingredient table still exists. Can't we tell Hibernate to delete that rows also?
Oracle table
create table recipe(id number primary key,
name varchar2(25) unique);
create table ingredient(ingredient_id number(4) primary key,
ingredient varchar2(40));
create table recipe_ingredient(recipe_id number(4),
ingredient_id number(4),
constraint recipe_fk foreign key(recipe_id)
references recipe(recipe_id),
constraint ingredient_fk foreign
key(ingredient_id) references
ingredient(ingredient_id));
Ingredient and Recipe POJO
#Entity
#Table(name = "ingredient", uniqueConstraints={
#UniqueConstraint(columnNames="INGREDIENT_ID")
})
public class Ingredient implements Serializable {
#Id
#Column(name = "INGREDIENT_ID", unique=true, nullable=false)
#SequenceGenerator(name="seq_ingredient", sequenceName="seq_ingredient")
#GeneratedValue(strategy=GenerationType.AUTO, generator="seq_ingredient")
private Integer ingredientId;
#Column(name = "INGREDIENT")
private String ingredient;
/*#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="RECIPE_ID")
private Recipe recipe;*/
//getter and setters
#Entity
#Table(name = "recipe")
public class Recipe implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinTable(name = "recipe_ingredient", joinColumns = { #JoinColumn(name = "recipe_id") }, inverseJoinColumns = { #JoinColumn(name = "ingredient_id") })
private List<Ingredient> ingredients;
//getters and setter
}
DAO Code
public class RecipeDaoImpl implements RecipeDao {
public void addRecipe(Recipe recipe) {
getSession().saveOrUpdate(recipe);
}
}
Log that shows that the row in INGREDIENT table still exists whereas Hibernate is just deleting row from 'RECIPE_INGREDIENT' table.
Please see following that ingredient_id with null is deleted. In both cases, it is updating ingredient.recipe_id as NULL.
Received following from frontend:
RecipeController - Recipe[recipeId=126,name=Sandwich,ingredients=[Ingredient[ingredientId=270,ingredient=Salt],[ingredientId=<null>,quantity=<null>]]]
Hibernate: update RECIPE set NAME=? where RECIPE_ID=?
Hibernate: update ingredient set INGREDIENT=? where INGREDIENT_ID=?
Hibernate: delete from recipe_ingredient where recipe_id=?
Hibernate: insert into recipe_ingredient (recipe_id, ingredient_id) values (?, ?)
So the database table has,
INDREDIENT
INGREDIENT_ID INGREDIENT
271 Salt
272 Sugar
RECIPE_INDGREDIENT
RECIPE_ID INDREDIENT_ID
126 271
I solved this issue by adding insertable = false, updatable = false as attributes to #JoinColumn
Like this:
#JoinColumn(name="RECIPE_ID", insertable = false, updatable = false)
Orphan Removal in Relationships
When a target entity in one-to-one or one-to-many relationship is removed from the relationship, it is often desirable to cascade the remove operation to the target entity. Such target entities are considered “orphans,” and the orphanRemoval attribute can be used to specify that orphaned entities should be removed.
For example, if an RECIPE has many INGREDIENT and one of them is removed from the list, the removed INGREDIENT is considered an orphan. If orphanRemoval is set to true, the line item entity will be deleted when the INGREDIENT is removed from the list.
The orphanRemoval attribute in #OneToMany and #oneToOne takes a Boolean value and is by default false.
The following example will cascade the remove operation to the orphaned INGREDIENT entity when it is removed from the relationship:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval="true")
#JoinColumn(name="RECIPE_ID")
private List<Ingredient> ingredients;
To Add more
orphanRemoval and DELETE CASCADE.
orphanRemoval is an entirely ORM-specific thing. It marks "child" entity to be removed when it's no longer referenced from the "parent" entity, e.g. when you remove the child entity from the corresponding collection of the parent entity.
ON DELETE CASCADE is a database-specific thing, it deletes the "child" row in the database when the "parent" row is deleted.
I've just started with JEE6 using netbeans IDE, and now i'm learning JPA with toplink.
I'm using Netbeans IDE and mySQL.
I created 2 very simple tables, with a many to many relationship:
table student
int id PK
varchar name
table teacher
int id PK
varchar name
//the mapping
table studentTeacher
int studentID FK
int teacherID FK
The studentTeacher table maps Students id to Teachers id, with cascade on delete and update.
I've used the 'generate entities from database' netbeans feature, and it works fine:
I can add a Student object to my student table, and i can add a Teacher object to my teacher table.
The problem is, how can i create a 'studentTeacher' entity and persist it? Or it's already done by the IDE?
Here is the auto-generated Annotations from Netbeans:
//Student Entity annotation generated by Netbeans 7.0.1
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Integer id;
#Size(max = 40)
#Column(name = "name")
private String name;
#JoinTable(name = "studentTeacher", joinColumns =
{
#JoinColumn(name = "studentID", referencedColumnName = "id")
}, inverseJoinColumns =
{
#JoinColumn(name = "teacherID", referencedColumnName = "id")
})
#ManyToMany
private Collection<Teacher> teacherCollection;
Thanks!
Properly speaking, it's already done by the JPA implementation, in your case: TopLink. You don't need a StudentTeacher entity as it isn't an entity by itself, it's just an auxiliary table needed by the Entity-Relational scheme in order to model your many-to-many relationship.
If your relation between Student and Teacher had additional attributes apart from the foreign keys of both entities, then you will need an additional entity but you already existent entities (Student and Teacher) will no longer have a many-to-many relationship, but a one-to-many/many-to-one with that intermediary StudentTeacher entity.
My advice, if your model is as simple as in your example, leave as it is.