JPA many to many cascading delete - java

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.

Related

JPA, How to add the value of entity type in Map to primary key?

I have 3 entity classes like below:-
Role Entity
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany
#LazyCollection(LazyCollectionOption.FALSE)
#JoinTable(name = "roles_privileges", joinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "privilege_id", referencedColumnName = "id"))
private Set<Privilege> privileges;
// getters, setters etc
}
Privilege Entity
#Entity
public class Privilege {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToMany(mappedBy = "privileges", fetch = FetchType.EAGER)
#JsonIgnore
private Set<Role> roles;
// getters, setters etc
}
UrlsMapper Entity
#Entity(name = "urls_mapper")
#Table(name = "urls_mapper")
public class UrlsMapper {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
#Column(name = "http_method")
private HttpMethod httpMethod;
#Column(name = "path")
private String path;
#ManyToMany(fetch = FetchType.EAGER)
#MapKeyJoinColumn(name = "role_id", referencedColumnName = "id")
#JoinTable(
name = "u_r_p",
inverseJoinColumns = #JoinColumn(name = "privilege_id")
)
Map<Role, Privilege> privilegeMap;
// getters, setters etc
}
The keys, primary and foreign that get created are as below
The logs while table generation is as below:-
Hibernate: create table u_r_p (urls_mapper_id bigint not null, privilege_id bigint not null, role_id bigint not null, primary key (urls_mapper_id, role_id)) engine=InnoDB
Hibernate: alter table u_r_p add constraint FKgd7gd9f9ded1s28swdudqs0ro foreign key (privilege_id) references Privilege (id)
Hibernate: alter table u_r_p add constraint FKrryprkx4j60lyjti16eysn5g5 foreign key (role_id) references Role (id)
Hibernate: alter table u_r_p add constraint FKfkthdnoca59a18ba96183p7ov foreign key (urls_mapper_id) references urls_mapper (id)
And I just want to know how can I add the privilege_id also into the JoinTable u_r_p and if there can be other best options for this. Manually doing in the database is a obvious alternate, but i wanted to know the hbm2ddl.auto based solution, so that code manages it itself
I don't think you've modeled your concepts properly. You have a ManyToMany between Role and Priviledge but what makes UrlMapper an entity? You have a Map<Role, Privilege> field in UrlMapper but that is the purpose of the join table so there should be no need to duplicate that. Instead it seems to be that HttpMethod and Path are attributes of the relationship.
However, I might also note that you seem to be expecting there be a Role/Privilege join for many different HttpMethod/Path combinations. This seems incredibly fine grained and an operations nightmare, but whatever. Anyway, what you seem to be saying is you want unique combinations of Role/Privilege/HttpMethod/Path so you should just make a entity for that and the table represents your set. Make a Permission entity that holds a unique Role/Privilege/HttpMethod/Path. Role, Privilege, HttpMethod, and even Path are essentially enumerations so you should have a table for each for each of them with ManyToOne mappings in the Permission entity. You could add bidirectional OneToMany mappings in each of the lookup tables but I'm not sure I see a need for that. It's up to you.
I assume Privilege would be {allow, deny} but it seems like less of a tangle if you assume deny unless a Role/HttpMethod/Path permission specifically exists. If that's the case then I would leave out the Privilege entity. Anyway, just a thought. Hope this helps.

Duplication with CascadeType

I'm a little bit confused right now. I have three tables: specialPrice, partner, product.
In the specialPrice I can store discount for products which will available for the selected partner. So it has only three columns. Partner and product table and they row is referenced from the specialPrice table.
These three tables are represented as entities in my Java application as well. Here is my problem: if I want to store one specialPrice I got the following exception:
Caused by: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: 0.
the error message is helpful, I have to use CascadeType, okay. BUT if I use the CascadeType.ALL the specialPrice will be created (so no more java.lang.IllegalStateException), but in the product and in the partner table the selected partner and the product will be duplicated... I don't understand how is this possible?
specialPrice Entity:
#Basic(optional = false)
#NotNull
#Column(name = "DISCOUNT_RATE")
private int discountRate;
#JsonBackReference
#JoinColumn(name = "PRODUCT_ID", referencedColumnName = "ID")
#ManyToOne(optional = false)
private Product productId;
#JsonBackReference
#JoinColumn(name = "PARTNER_ID", referencedColumnName = "ID")
#ManyToOne(optional = false)
private Partner partnerId;
reference from partner Entity:
#JsonIgnore
#OneToMany(mappedBy = "partnerId")
private Collection<SpecialPrice> specialPriceCollection;
(same for the product)
Using EclipseLink (JPA 2.1)
Can someone help me, what am I doing wrong? I don't want to duplicate the selected partner and product...
Thank you!
I understand Partner and Product are both pre-existing entities?
In such case, try the following code in your service method:
specialPrice.setPartner(partnerRepository.getOne(specialPrice.getPartnerId().getId());
specialPrice.setProduct(productRepository.getOne(specialPrice.getProductId().getId());
specialPriceRepository.save(specialPrice);
As a side note, using CascadeType.ALL with #ManyToOne is almost never a good idea.

Hibernate mapping, entity with many to many relationship table

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.

How to model in JPA a "default selection" from a One-to-Many Relationship

I have a data model in which a Person may be known by one or more Names (such as the case of a woman who has changed her name after marriage). The model distinguishes the single Name that is being actively used.
The general relationship between Person and Name is modeled as usual: a bidirectional #OneToMany relationship in the Person entity and the corresponding #ManyToOne relationship in the Name entity classes.
To specify the "active or primary name" I have thought that I could model this as a unidirectional #OneToOne relationship from Person to Name. The mappings in the entity classes would look like this:
public class Person {
#Id #GeneratedValue(strategy = GenerationType.Identity)
private Long pers_id;
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "name_id", nullable = false)
private Name uniPrimaryName;
:
:
#OneToMany(mappedBy = "owningSidePerson", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Name> mappedSideNames;
:
:
}
And the Name entity class:
public class Name
#Id #GeneratedValue(strategy = GenerationType.Identity)
private Long name_id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinTable (name = "XREF_NAMES_PERSON",
joinColumns = #JoinColumn(name = "name_id", nullable = false),
inverseJoinColumns = #JoinColumn(name = "pers_id", nullable = false))
private Person owningSidePerson;
:
:
}
The advantage of this model is that it lets me access the primary Name as a simple property access from Person. It should also be easier to maintain. The downside relates to serializing Person entities that have circular references, but this problem is present whether the unidirectional relationship is present or not.
An alternative model would be to add an active boolean attribute to the Name entity and use it to indicate which entity in the relationship was the active one. The downsides are that maintaining the attribute would be less straightforward and obtaining the active Name would require a separate database query.
Before I invested time in this design, I wanted to ask if anyone has tried this before. I have concerns about an object model which uses an entity object that would be present in a unidirectional and a bidirectional relationship at the same time.
My suggestion is to just leave the person's relationship to name (mappedSideNames) and delete the relationship mapped by uniPrimaryName attribute to identify whether the name is primary or not u can create a named query for this purpose, but you will have to create a way to differentiate the primary names as you quoted by setting a flag.

Unwanted behavior – Hibernate deletes child elements annotated with #ElementCollection when parent element is updated

everyone.
I am have Customer and Service tables in one to many relation - one customer can have no or many services. The Service table has a customer_id column which is a foreign key referencing the primary key of the Customer table.
When retrieving customers from the database I need to get only the IDs of the related services and that is why I decided to use the #ElementCollection annotation.
My mapping for the Customer is as follows:
#Entity
#Table(name = "customer")
public class CustomerEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(nullable = false)
private String name;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "service", joinColumns = #JoinColumn(name = "customer_id", updatable = false, insertable = true))
#Column(name = "id", updatable = false, insertable = true)
private Set<Integer> serviceIds;
}
Everything works perfect when I get data from the database. The problem is when I try to update a customer in the database. Upon update Hibernate deletes all the Service table rows which reference the updated customer if the serviceIds member of the customer entity was set to null or an empty Set. I would like to avoid this behavior. I would like this serviceIds member to be read only for Hibernate and to be ignored when the customer is updated in the database - i.e. I want to update only the customer table and nothing else. Is it possible to achieve this using ElementCollection?
By the way I tried the following mapping and the update of the Customer does not lead to any deletions in the Service table even if I set the serviceIds to null or an empty Set.
#OneToMany
#JoinColumn(name = "customer_id", referencedColumnName = "id", updatable = false, insertable = false)
private Set<ServiceEntity> serviceIds;
Thank You for Your help.
When modifying - even - a single element in an #ElementCollection, Hibernate will delete the old collection (the one persisted in DB) and then insert the new image of the collection. The question was already asked here:
Hibernate - #ElementCollection - Strange delete/insert behavior
In order to avoid this problem you can:
Use a List of Integer with the #OrderColumn annotation as described in the above link.
Create an Entity class wrapping your Integer (+ #Generated #Id).
Best regards

Categories