I am using Hibernate 3.5.4 version as Orm I have two tables which have many to one relationship , Like Table 'Book' can have many 'Authors' associated with It.
#OneToMany(mappedBy = "key.bookId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<BookAuthor> getAuthors() {
return authors;
}
But we use soft delete for deleting the association (we maintain a column named isDeleted) , i want to fetch the entity based on isDeleted check if its 1 it should not be loaded , else if 0 load it.
Is it possible by modifying this current fetching strategy to provide above support or there is another better solution that can be applied please let me know.
Have a look at the #Filter or #Where Annotation.
As far as I know this is the usual way to restrict collection fetching.
Related
On a bidirectional relationship beetwen two entities (a ControlTable made up of ControlSteps), i'm simply trying by different ways to request a ControlTable by knowing the collection ControlSteps of it. I know that it's not recommended to have this bidirectionnal mapping but i need to know each childs of a parent, and the parent for each child.
I configured it like this in ControlTable class:
#OneToMany(mappedBy = "controlTable",cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Fetch(FetchMode.JOIN)
private Set<ControlStep> controlSteps;
And like this for ControlStep class :
#ManyToOne(optional=false, fetch=FetchType.LAZY)
#JoinColumn(name="ctrl_table_id", referencedColumnName = "id")
private ControlTable controlTable;
When i use the default JPA query findAll(), it's not working to get the list of ControlTables (or only one) because it's requesting recursively the parent in the child's parent (infinite response).
In another way, itried to put all in LAZY loading, with an HQL query fetching the childs, but the result is the same.
Do you have any idea of how to get these collections without problems?
Thank you very much by advance
Found it. The problem was Spring Data Rest and JSON transformation, for more details :
Infinite Recursion with Jackson JSON and Hibernate JPA issue
I am using hibernate with JPA annotations for relationship mapping.
I have three entities in my code User Group & User_Group
User & Group are in a ManyToMany relationship.
User_Group is a kinda bridge table but with some additional fields. So here is the modified mapping code.
User
#Entity
#Table(name = "USERS")
public class User {
#OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups
}
Group
#Entity
#Table(name = "GROUPS")
public class Group {
#OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups
}
UserGroup
#Entity
#Table(name = "USERS_GROUPS")
public class UserGroup {
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GROUP_ID")
private Group group;
}
When I set the user & group object to the usergroup & save it.
User user = new User("tommy", "ymmot", "tommy#gmail.com");
Group group = new Group("Coders");
UserGroup userGroup = new UserGroup();
userGroup.setGroup(group);
userGroup.setUser(user);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());
session.save(userGroup);
Things work fine. With CascadeType.ALL the group object & user object are updated too. But when I delete the userGroup object. The child object are deleted too.
Deletion of child objects is a strict no no.
There is no CascadeType.SAVE-UPDATE in JPA, which just does save or update but no delete. How do I achieve this.
If I remove the CascadeType.ALL from the mapping the child objects don't get updated & I need them to be updated.
SAVE_UPDATE is for save(), update(), and saveOrUpdate(), which are 3 Hibernate-proprietary methods. JPA only has persist() and merge(). So, if you want to use cascading on Hibernate-proprietary methods, you'll need to use Hibernate-proprietary annotations. In this case, Cascade.
Or you could stop using the Hibernate Session, and use the standard JPA API instead.
CascadeType.ALL includes CascadeType.REMOVE too.
The solution is to use all CascadeType.* you need except CascadeType.REMOVE, like so:
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}))
in your UserGroup definitions.
It's almost always a code smell when propagating from child to parent entity, it should be the other way round.
From Cascading best practices:
Cascading only makes sense only for Parent – Child associations (the
Parent entity state transition being cascaded to its Child entities).
Cascading from Child to Parent is not very useful and usually, it’s a
mapping code smell.
From Hibernate best practices:
Avoid cascade remove for huge relationships
Most developers (myself included) get a little nervous when they see a
CascadeType.REMOVE definition for a relationship. It tells Hibernate
to also delete the related entities when it deletes this one. There is
always the fear that the related entity also uses cascade remove for
some of its relationships and that Hibernate might delete more
database records than intended. During all the years I’ve worked with
Hibernate, this has never happened to me, and I don’t think it’s a
real issue. But cascade remove makes it incredibly hard to understand
what exactly happens if you delete an entity. And that’s something you
should always avoid. If you have a closer look at how Hibernate
deletes the related entities, you will find another reason to avoid
it. Hibernate performs 2 SQL statements for each related entity: 1
SELECT statement to fetch the entity from the database and 1 DELETE
statement to remove it. This might be OK, if there are only 1 or 2
related entities but creates performance issues if there are large
numbers of them.
Given a Hibernate/JPA entity with cascading set to ALL for a related entity:
#Entity
public class Entity {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "entity")
private Set<RelatedEntities> relatedEntities;
}
Is it possible to temporarily turn off the cascading, e.g. to allow Entity to be persisted without also persisting its relatedEntities?
No, it is not possible to do it, and at least according to my modest opinion, it would not be a good thing to do so either. When other developers look at the mappings and the code that does persist/merge/delete... they would expect the cascades to be applied and introduce the unexpected behavior if they oversee that the cascades are temporarily disabled somewhere else for the code they are about to change.
However, you can map to the same table a new entity class which does not have the fields that are cascaded. Then just use that entity in situations in which you don't want the cascades to be applied.
You can't temporarily disable cascading (to my knowledge, at least), but since you use Hibernate you can insert new entity using HQL
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert ).executeUpdate();
There is always a "manual" solution where you remember relatedEntities in a variable for later use, and set null value as its value on Entity instance before persisting it.
#Entity
public class EUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToMany(fetch=FetchType.EAGER)
private List<UserRole> roles;
}
when doing the following action
EUser approveUser = (EUser) userService.getOne(2);
approveUser.getRoles().clear();
userService.update(approveUser);
System.out.println(approveUser.getRoles().size());
it says the size is zero but when i go the db in the EUser_UserRole table i see the value still present. How to solve this??
also in the EUser_UserRole it says
This table does not contain a unique column. Grid edit, checkbox, Edit, Copy and Delete features are not available
how can i delete add edit delete manually??
Cascading is indeed a way to let Hibernate do the removal and if I see the posted code, that is most likely what is asked for. But since the question is about manually deleting while cascading is more automatic deletion, I have to add the suggestions to:
use EntityManager.remove()
invoke a JPQL delete query
Which more fit more the description of "manual" deletion.
se CascadeType. Reference
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<UserRole> roles;
Update :
orphanRemoval attribute can use JPA 2.x version. You have to find out the deleted UserRole data by comparing old rolesList and new rolesList`
orphanRemoval attribute does not support in ManyToMany mapping.
Do not use a cascade for ManyToMany relationships. This can result in an undesired rippling deletion over a wide entity cluster easier than one might hope.
If you want to clear the relationship (delete rows from the join table) for a single user to their roles, you will need to clear the relationship fields on both sides, meaning clearing the List of UserRole in EUser and removing the current EUser from the lists in the respective UserRole instances.
EDIT:
You are not deleting any entities from the database when clearing the lists of related entities. The only result will be that some rows in the join table will be deleted and after the next fetch/refresh, your EUser and UserRole instances will no longer be related.
If you want to remove the UserRoles DB entries, you can do so after removing the relationships to
I two entities a User and a Place witch are bound with many to many association.
When I try to get all the places for a given user thought the getter method, an emtpy list is returned but the user is bound to the place in the database and if I change the default fetching strategy to eager I can see all the places just fine.
I am using MySQL for the persistance.
The annotations used are:
for the User entity:
#ManyToMany
#JoinTable(name= "USER_PLACE",
joinColumns = {#JoinColumn(name="USER_ID")},
inverseJoinColumns = {#JoinColumn(name="PLACE_ID")})
private List<Place> places = new ArrayList<Place>();
and for the Place entity:
#ManyToMany(mappedBy = "places")
private List<User> users = new ArrayList<User>(0);
What can it be the cause of this?
To me, it looks like a Mapping issue in Your domain-model. Do the provided entities also map somewhere else? You might run into crazy joins with other tables. Could you provide all relevant entities? Also, the SQL statements generated by Hibernate for User.getPlaces() would be helpful.