I am using GSON to serialize my hibernate entity.
#Entity
#Table(name = "ACCOUNT", uniqueConstraints = {
#UniqueConstraint(columnNames = { "SECONDARY_LOGINID", "COUNTRY_CODE" }, name = "SECONDAY_LOGIN_UNIQUE") })
#NamedQuery(name = "Account.findAll", query = "SELECT a FROM Account a")
#DynamicUpdate
public class Account extends BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
#OneToMany(mappedBy = "accountIdfk", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Address> addresses;
#OneToMany(mappedBy = "accountIdfk", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Contact> contacts;
}
While converting entity to json, GSON is trying to fetch all the unfetched object. Is there a way we can ignore to fetch untouched entities and only serialize the fields which have been fetched?
everyone i have a question with using annotation #OneToMany/#ManyToOne; is it possible to create one user model with two sets of subjects in this model (conducted for the teacher and attending the student) instead of creating separate student and teacher models? I wrote such a code but when I want to get data about item and user, hibernate crashes the "Stack overflow" error.I will add that I use H2 Database.
User Entity:
#Entity
public class User{
#OneToMany(
mappedBy = "student",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Set<Item> items = new HashSet<>();
#OneToMany(mappedBy = "teacher",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Set<Item> carriedItems= new HashSet<>();
}
//id and other data
Item entity:
#Entity
public class Item{
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "student_id", nullable = false)
private User student;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "teacher_id", nullable = false)
private User teacher;
}
//id and other data
Thanks for help #Leviand
Based on your comments, looks like your code and logic needs some rework.
First, use instead of Item pojo two dedicated classes:
#Entity
#Table(name = Studend) // I'm guessing a table name
public class Student{
#JoinColumn(name = "student_id", nullable = false)
private User user;
}
#Entity
#Table(name = Teacher) // I'm guessing a table name
public class Teacher{
#JoinColumn(name = "teacher_id", nullable = false)
private User user;
}
Then since an User can only be connected to a single Teacher or Student, refact that in something like:
#Entity
#Table(name = User) // I'm guessing a table name
public class User{
#OneToOne(
mappedBy = "student",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Student student;
#OneToMany(mappedBy = "teacher",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Teacher teacher;
}
Hope this helps :)
Lately my project encountered a really odd issue with JPA findOne(id) returning a proxy object instead of a full object.
Here is the scenario. Consider the entities and their connections shown below.
#Table(name = "HOUSE")
#Entity
#EqualsAndHashCode
#Setter
#ReadPermission(expression = "user has rights for template snapshots AND has filter")
public class HouseEntity extends VersionedEntity {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "house", fetch = FetchType.LAZY, orphanRemoval = true)
public List<RoomEntity> getRooms() {
return rooms;
}
#OneToMany(cascade = CascadeType.ALL, mappedBy = "template", fetch = FetchType.LAZY, orphanRemoval = true)
public List<TableEntity> getTables() {
return tables;
}
}
#Entity
#Table(name = "ROOMS")
public class Room {
#ManyToOne(fetch = FetchType.LAZY)
public HouseEntity getHouse() {
return house;
}
#OneToMany(mappedBy = "room", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
public List<TableEntity> getTables() {
return tables;
}
}
#Entity
#Table(name = "TABLES")
public class TableEntity{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "HOUSE_ID")
public HouseEntity getHouse() {
return template;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ROOM_ID")
public RoomEntity geRoom() {
return room;
}
As you can see, House has Tables and Rooms, Rooms also has Tables, and each child entity has a connection to its parents.
Add a table to the HouseEntity
Remove the table from the HouseEntity immediately after.
For 1, the houseRepository.findById gets my HouseEntity wrapped in proxy, as if it is lazy loaded.
The weird part is that if I do:
Add a table to a RoomEntity, which is a child of HouseEntity.
Remove the table from RoomEntity.
Then houseRepository.findById returns the HouseEntity without the proxy.
My question here is, why would this happen? Why would the findById method return a proxyed entity in this case? I need to have access to the normal entity without the proxy directly, even if the proxy has the entity populated in the target.
I have such entities with inheritance
#MappedSuperclass
public class PostEntity {
...
#ManyToOne
#JoinColumn(name = "user_id")
private UserEntity author;
}
#Entity
#Table(name = "answers")
public class AnswerEntity extends PostEntity {}
#Entity
#Table(name = "users")
public class UserEntity {
...
#OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private List<AnswerEntity> answers;
}
during compilation, he throws me away
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.example.demo.jpa.entity.AnswerEntity.author in com.example.demo.jpa.entity.UserEntity.answers
I do not know why he does not see the author field during mapping.
It is not supported by JPA, at least not by Hibernate. You can either have:
#OneToMany(mappedBy = "author")
private List<PostEntity> answers;
Or move UserEntity author field to AnswerEntity.
I have a Many-to-Many relationship between Thread and Participant through a ThreadParticipant entity (because the association has an additional field). I have the following mapping.
Thread entity
#Entity
#Table(name = "thread")
public class Thread {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "thread", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Collection<ThreadParticipant> threadParticipants = new HashSet<>();
// Getters and setters
}
Participant entity
#Entity
#Table(name = "participant")
public class Participant {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private int id;
#ManyToOne(fetch = FetchType.LAZY, optional = true, targetEntity = Account.class, cascade = { CascadeType.PERSIST })
#JoinColumn(name = "account_id")
private Account account;
#ManyToOne(fetch = FetchType.LAZY, optional = true, targetEntity = Company.class)
#JoinColumn(name = "company_id")
private Company company;
// Getters and setters
}
ThreadParticipant entity
#Entity
#Table(name = "thread_participant")
#IdClass(ThreadParticipantPK.class)
public class ThreadParticipant implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Participant.class, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "participant_id")
private Participant participant;
#Id
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Thread.class)
#JoinColumn(name = "thread_id")
private Thread thread;
#Column(name = "last_viewed", nullable = true)
private Date lastViewed;
// Getters and setters
}
ThreadParticipantPK
public class ThreadParticipantPK implements Serializable {
private Thread thread;
private Participant participant;
public ThreadParticipantPK() { }
public ThreadParticipantPK(Thread thread, Participant participant) {
this.thread = thread;
this.participant = participant;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ThreadParticipantPK)) return false;
ThreadParticipantPK that = (ThreadParticipantPK) o;
if (!participant.equals(that.participant)) return false;
if (!thread.equals(that.thread)) return false;
return true;
}
#Override
public int hashCode() {
int result = thread.hashCode();
result = 31 * result + participant.hashCode();
return result;
}
// Getters and setters
}
Now, I am trying to fetch threads with the following query (using Spring Data JPA) and Hibernate as my JPA provider.
#Repository
public interface ThreadRepository extends JpaRepository<Thread, Integer> {
#Query("select distinct t from Thread t inner join fetch t.threadParticipants tp inner join fetch tp.participant p left join fetch p.account a left join fetch p.company c")
public List<Thread> test();
}
The problem is that when the fetch type for the associations within ThreadParticipants are set to FetchType.LAZY, the Thread.threadParticipants collection is empty. Consequently, if I set the associations to FetchType.EAGER, Thread.threadParticipants contains two elements (as it should). In this case, however, Hibernate goes nuts and executes four SQL queries for fetching a single thread.
Hibernate: select thread0_.id as id1_18_0_, threadpart1_.participant_id as particip2_19_1_, threadpart1_.thread_id as thread_i3_19_1_, participan2_.id as id1_12_2_, account3_.id as id1_0_3_, company4_.id as id1_6_4_, thread0_.created as created2_18_0_, thread0_.last_activity as last_act3_18_0_, thread0_.subject as subject4_18_0_, threadpart1_.last_viewed as last_vie1_19_1_, threadpart1_.thread_id as thread_i3_18_0__, threadpart1_.participant_id as particip2_19_0__, threadpart1_.thread_id as thread_i3_19_0__, participan2_.account_id as account_2_12_2_, participan2_.company_id as company_3_12_2_, account3_.email as email2_0_3_, account3_.facebook_profile_id as facebook3_0_3_, account3_.first_name as first_na4_0_3_, account3_.last_name as last_nam5_0_3_, account3_.middle_name as middle_n6_0_3_, company4_.additional_address_text as addition2_6_4_, company4_.banner_name as banner_n3_6_4_, company4_.ci_number as ci_numbe4_6_4_, company4_.city_id as city_id22_6_4_, company4_.co_name as co_name5_6_4_, company4_.company_type_code as company_6_6_4_, company4_.created as created7_6_4_, company4_.description as descript8_6_4_, company4_.email as email9_6_4_, company4_.last_modified as last_mo10_6_4_, company4_.logo_name as logo_na11_6_4_, company4_.name as name12_6_4_, company4_.number_of_reviews as number_13_6_4_, company4_.phone_number as phone_n14_6_4_, company4_.postal_box as postal_15_6_4_, company4_.rating as rating16_6_4_, company4_.second_phone_number as second_17_6_4_, company4_.street_name as street_18_6_4_, company4_.street_number as street_19_6_4_, company4_.teaser as teaser20_6_4_, company4_.website as website21_6_4_ from thread thread0_ inner join thread_participant threadpart1_ on thread0_.id=threadpart1_.thread_id inner join participant participan2_ on threadpart1_.participant_id=participan2_.id left outer join account account3_ on participan2_.account_id=account3_.id left outer join company company4_ on participan2_.company_id=company4_.id
Hibernate: select participan0_.id as id1_12_0_, participan0_.account_id as account_2_12_0_, participan0_.company_id as company_3_12_0_ from participant participan0_ where participan0_.id=?
Hibernate: select thread0_.id as id1_18_0_, thread0_.created as created2_18_0_, thread0_.last_activity as last_act3_18_0_, thread0_.subject as subject4_18_0_ from thread thread0_ where thread0_.id=?
Hibernate: select participan0_.id as id1_12_0_, participan0_.account_id as account_2_12_0_, participan0_.company_id as company_3_12_0_ from participant participan0_ where participan0_.id=?
Apparently it's executing a query for each participant, and two queries for each thread. So, without FetchType.EAGER, my code is simply not working, but with it, my database will get killed. I tried adding a #OneToMany association between Participant and ThreadParticipant (similar to the one from Thread to ThreadParticipant), but with the same results. I also tried to add all of the aliases to my query's field list, but to no avail.
Why is this happening? Is my mapping or query wrong? Thank you in advance!
Try changing the the one-to-many collection to a Set:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "thread", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Set<ThreadParticipant> threadParticipants = new HashSet<>();
The additional queries should NEVER be generated if you only have LAZY associations. That may be because the many-to-one and one-to-one relationships are EAGER by default. Try setting those to LAZY instead.