Hibernate Envers: Initializing ListProxy of related objects - java

I've two entities: User and UserGroup. Relation between them is #ManyToMany and I'm using envers for auditing these entities, class level #Audited annotation is placed on both of them. However, when I try to execute this query:
AuditReader reader = AuditReaderFactory.get(em);
AuditQuery query = reader.createQuery().forRevisionsOfEntity(User.class, false, true);
Returnted user entities have "org.hibernate.envers.entities.mapper.relation.lazy.proxy.ListProxy" collections of user groups with size equal to zero. Calling size() method on these list proxies doesn't initialize them. Any help will be appreciated.

The problem was the following: I started auditing of entities when there already were users and usergroups in the database. Let's say I was modifying some user's groups. This modification caused in addition of corresponding rows in User_AUD and User_UserGroup_AUD tables, but UserGroup_AUD table was still empty. Later when I was querying for revisions of User entity, it wasn't able to find related UserGroup entities since there was no record in UserGroup_AUD table about those user groups.

Related

Hibernate attempts to delete parent entity in unidirectional ManyToOne relationship

I have a simple unidirectional ManyToOne relationship on an entity, which unfortunately is defined in a schema I cannot change.
It is defined as follows
#Entity
#Table(name="Profile")
...
public class Profile{
#ManyToOne
#JoinColumn(name="usr_id", nullable=false, updatable=false)
private User usr;
...
and all is well. The relationship is enforced with a foreign key in the db, hence the nullable = false and updatable = false. There is no mention of the profiles in user.
When I try to delete a Profile, hibernate also tries to delete the User entity, which is parent to other relationships and therefore fails. I have no CascadeType annotations anywhere.
My intent is to have a simple reference to the user using this profile in the usr field. This is a unidirectional relationship. The user entity should not be affected whenever I delete a profile.
This appers to be achievable when the usr field may be dereferenced before delete (I can see in the hibernate generated sql that hibernate attempts to set the field to null before deletion) - however that fails because of the foreign key.
Is what I'm trying to do achievable? If so, how?
(I'm using spring data on top of hibernate, if that is relevant.)
further Infos: I have tried optional=false, and it leads to the delete the parent entity behaviour. I have tried all fitting combinations of CascadeTypes, #OnDelete with NO_ACTION (still tries to delete the user) and defining a reverse but owned by user relationship - no success so far. On top of that, I tried the search function ;), which lead me to the conclusion that this is just my problem. If I missed an answered question, I'd appreciate a pointer in the right direction. Thanks.
Do you have some kind of other non-nullable association #ManyToOne or #OneToOne to the Profile entity? You can debug into the deletion process by setting a break point in e.g. the JDBC driver in e.g. Connection.prepareStatement and go down the stack frames to the cascading part to figure out why this cascading happens.
If all that doesn't help, please create a reproducing test case and submit an issue to the Hibernate issue tracker.

Spring Data JPA join 2 tables

I have 2 tables in MySQL database: user and user_additional_details with columns described below.
User
id (auto increment)
userId (unique)
first name
last name
phone
email
User Additional Details
id (auto increment)
userId (matches userId in User)
personalPhone
personalEmail
Table user_additional_details contains 0 or 1 row for each userId in user table.
However, database does not have a foreign key constraint defined. Ideally, columns from user_additional_details should have been added as nullable columns in user table, but that was not done for some unknown reason. Now I need to define the entity for following query.
select user.userId, user.phone, user_a_d.personalPhone
from user
join user_additional_details as user_a_d
on user.userId = user_additional_details.userId
I tried defining JPA entities for the tables, but not able to figure out how to create an entity that uses columns from different tables.
It seems like the SecondaryTable annotation is what you are looking for
Specifies a secondary table for the annotated entity class. Specifying
one or more secondary tables indicates that the data for the entity
class is stored across multiple tables.
Here you find a detailed example of how to use it - http://www.thejavageek.com/2014/09/18/jpa-secondarytable-annotation-example/
Create UserEntity (with all the columns from User table) and UserAdditionalDetailsEntity(with all the columns from user_additional_details table). I assume you are aware how to create JPA entities and map them to database table.
I hope you would have create entity manager factory object in your spring configuration file. With the help of that create entity manager object .
Once EntutyManager Object is created:
Query q= em.createQuery("select user.userId, user.phone, userDetails.personalPhone
from UserEntity user
join UserAdditionalDetailsEntity as userDetails
on user.userId = userDetails.userId");
List<Object[]> resultList= q.getResultList();
Once you get resultList you can iterate over the list of object array and get data.
Each index of the resultList will contain the object array representing one row
Keep in mind that field name mentioned in query should be same as the one mentioned in your JPA Entites.

Update record in many to many relation by Hibernate

I have two classes, Document class and Role class and a #ManyToMany relationship between them.
I tried to get a document then i get all that roles and fill the set of the document with them.
Now when i update the set of the document (insert new roles or remove existing roles) and then update the document object by session.update(doc), the Hibernate does not do any insert or delete statement into the #ManyToMany table, it only updates the document and the roles records.
Note: the lazy attribute is "lazy = true" in both tables.
The problem was i am using the bidirectional #ManyToMany relation (that mean the document have set of role and the role have set of document) and in this way when i create a document and adding some roles to it, even that i have to add this document to each role in the set.
when i use the unidirectional #ManyToMany relation(that mean just one side have a set of the other class) i just fill the document set with some roles.
this article is so helpful
https://howtoprogramwithjava.com/hibernate-manytomany-unidirectional-bidirectional/

Hibernate Lazy Loaded Entites Id

Question, am currently using hibernate and I was wondering if there was any way to get the value of a lazy loaded entities id without hitting the DB again? For example we currently have an entity called group that has a ManyToOne relationship with another entity Organization.
when we call a simple repository method
groupsRepository.findByUser(id) it returns a list of groups based on given user. The query looks like this
select
groups0_.PersonId as PersonId1_118_1_,
groups0_.GroupId as GroupId2_8_1_,
groups1_.GroupID as GroupID1_74_0_,
groups1_.CreatedAt as CreatedA2_74_0_,
groups1_.CreatedBy as CreatedB3_74_0_,
groups1_.Description as Descript4_74_0_,
groups1_.Name as Name5_74_0_,
groups1_.OrganizationID as Organiza9_74_0_,
groups1_.Role as Role6_74_0_,
groups1_.Status as Status10_74_0_,
groups1_.TouchedAt as TouchedA7_74_0_,
groups1_.TouchedBy as TouchedB8_74_0_
from
Groupsofpeople groups0_
inner join
groups groups1_
on groups0_.GroupId=groups1_.GroupID
where
groups0_.PersonId=?
Later on I need to see if the groups org is equal to another org which I do by comparing the OrganizaitonId's, a value that has already been fetched from the db. But every time I do this I have fetch the org from the db, is there anyway to prevent that so that the org attached to group will be prepopulated with it's id since it is already being fetched anyway? Or is this just a concession I have to make if I am using hibernate?

Hibernate and criteria that return the same items multiple times

I've "Student" and "Class" in relation many to many. In between there is a linking table.
If I want to retrieve all the students with HQL in the following way, everything is fine:
Query queryObject = getSession().createQuery("from Student");
return queryObject.list();
With the above code if there are three students I get a list of three students.
If I use criterias, however, it's fine only as long as there are no associations in the linking table.
Criteria crit = getSession().createCriteria(Student.getClass());
return crit.list();
With the second code, I get three results only when the linking table is empty. However when I add an association, I get 6 results. Looking at the log, Hibernate generates multiple selects. How is it possible?
Can someone explain why does this happen? How can I fix the criteria in order to return the three records only once?
EDIT
In the Student class I mapped in this way:
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(
name="room_student_rel"
, joinColumns={
#JoinColumn(name="id_student")
}
, inverseJoinColumns={
#JoinColumn(name="id_room")
}
)
private List<Room> rooms;
In the room (class) I mapped in this way:
#OneToMany(fetch=FetchType.EAGER, mappedBy="room")
#Fetch(FetchMode.SELECT)
private List<RoomStudent> roomStudents;
To expand on my previous comment, Hibernate will try to select using an outer join when the FetchType is set to EAGER.
See 2.2.5.5 at the following:
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/
For the implications of this see here:
Hibernate Criteria returns children multiple times with FetchType.EAGER
Now you can also specify a FetchMode (on your criteria.setFetchMode("assocFiled",FetchMode.LAZY) ) to set it to something other than JOIN or you can use something like criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); to filter the duplicates.
Ideally you should avoid EAGER on your entity mappings and where eager fetching is required enable it explicitly on entity load using some hint or via FetchProfile and then deal with the duplicates if required.

Categories