Hibernate entity with more than two OneToMany fields - java

I have the following hibernate entity:
#Entity
public class Customer {
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Address> addresses = new ArrayList<>();
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Contact> contacts = new ArrayList<>();
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Name> names = new ArrayList<>();
// Many more, including a primary key
}
Starting the application, I got the following exception:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
If I remove one arbitary OneToMany association, or if I add a #Fetch(value = FetchMode.JOIN) to an arbitary OneToMany association, everything works fine.
Is this a hibernate bug, a hibernate limitation, or is there anything wrong with my entity? TIA!

It is not a bug. It is because of Hibernate uses a one select with a join to fetch all the data. Hibernate can join three and more tables, but the result of joining will have duplicates of, for example, Address columns. Hibernate needs to remove duplicates — it is a reason why Set works.
Possible workarounds:
Use Set<Address> instead of List<Address>. You should use Set for all collections.
Use lazy fetching fetch = FetchType.LAZY
Use #Fetch(value = FetchMode.SUBSELECT)
Some additional reading:
A beginner’s guide to Hibernate Set and List behavior
Hibernate does not return distinct results for a query with outer join fetching enabled for a collection (even if I use the distinct keyword)?

Try to use:
#OneToMany
#LazyCollection(value=LazyCollectionOption.TRUE)
private Collection<Name> names = new ArrayList<>();

Related

Replace entity's list instead of adding to it when updating

FooObject has an entity with a few lists of bar objects managed by entities. Whenever I edit FooObject and call update on it, all of those lists are added to, not replaced.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "barEntity", targetEntity = BarTypeAEntity.class)
private List<BarTypeAEntity> barTypeAs;
I can first remove all the lists before calling update but in auditing these tables it appears as a delete then an new creation, rather than just one update.
How can I just simply overwrite rather than append these lists
Use orphanRemoval = true. This tells Hibernate that if you remove BarTypeAEntity object from the collection of barTypeAsand then save FooObject, the removed BarTypeAEntityshould be deleted from the underlying DB.
#OneToMany( cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "barEntity", targetEntity = BarTypeAEntity.class,
orphanRemoval = true )
private List<BarTypeAEntity> barTypeAs;
And do something like this for update
foo.getBarTypeAs().clear();
foo.getBarTypeAs().add(new BarTypeAEntity());

How to fetch grand children table with Hibernate annotation?

I have 3 tables working as LetterOfCredit has many ProformatInvoice which has many PurchaseOrder.
I would like to get in my entity LetterOfCredit the list of all the PurchaseOrder linked via ProformatInvoice.
In SQL it looks like :
SELECT *
FROM purchase_order
JOIN proformat_invoice pi ON pi.id = pi_id
JOIN letter_of_credit lc ON lc.id = pi.lc_id
WHERE lc_id = 3;
But in LetterOfCredit.java, I tried to use proformat_invoice as a jointable but I get an empty list....
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(
name="proformat_invoice",
joinColumns = #JoinColumn(name="lc_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name="id", referencedColumnName = "pi_id")
)
private List<PurchaseOrder> purchaseOrders;
Could you tell me what I did wrong?
We can fetch the grand children efficiently by using hibernate annotation ---- #OneToMany( mappedBy = "category", fetch = FetchType.LAZY ).
So in this case this will not give an exception if its unable to fetch the sub children till session is open.
We can manually pull the sub children by using the getters in our code without getting any exception , as we have already told the compiler that it will be lazy loading because there are child elements and there sub child elements.

JPA : How to save list with order in a many-to-many relationship

When I persist a list which has an order, the result in the database has a different order. How can I persist a many-to-many relationship with list order in JPA?
#Entity
#Table(name = "house")
public final class House {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Basic(optional = false)
#Column(name = "name")
private String name
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "house_room",
joinColumns =
#JoinColumn(name = "id_house", referencedColumnName = "id"),
inverseJoinColumns =
#JoinColumn(name = "id_room", referencedColumnName = "id"))
private List<Room> rooms;
}
House object:
Room room1 = new Room();
room1.setName("Kitchen");
Room room2 = new Room();
room2.setName("Bathroom");
Room room3 = new Room();
room3.setName("Bedroom");
List<Run> runs = new ArrayList<Run>();
rooms.add(0,room2);
rooms.add(1,room1);
rooms.add(2,room3);
House cottage = new House();
cottage.setRooms(rooms);
persist(cottage); // order in database should be "room2", "room1" then "room3" but it is saved as "room1", "room2" then "room3"
There are 2 ways to order list in JPA using #OrderBy and #OrderColumn annotations. #OrderColumn is perhaps what you are looking for.
#OrderBy
Specify an ordering rule based on the comparison of a particular attribute of the entity or element
Example:
#OrderBy("name ASC")
private List<Room> rooms;
By adding an #OrderBy annotation on the mapping, we can indicate that we want the rooms to be ordered in ascending alphabetical order by its name attribute.
Note: We needn’t have included the ASC in the #OrderBy annotations because it would be ascending by default.
Simply changing the order of the items in a List in memory will not cause that order to be stored in the database at commit time.
The annotation applies only when collection is retrieved from the database. That means, the order will not be stored in the database but when data is retrieved from the DB and stored to the List collection in memory, they will be ordered.
#OrderColumn
The OrderColumn annotation specifies a column that is used to maintain the persistent order of a list. Example:
#OrderColumn(name="ROOM_ORDER")
private List<Room> rooms;
This means you have an additional column named "ROOM_ORDER" that will indicate the order of your items in your table.
Relational databases do not store order of referenced data. In order to store the order of elements in a list, JPA needs another column in DB table. You may map the column with #OrderColumn in the same ways, as you mapped #JoinColumn (you just need single #OrderColumn, even when you used multiple #JoinColumn annotations)

Hibernate criteria filter by related entity

I have an entity which has a collection of related entities.
public class Student{
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "COURSE_STUDENT_ID" , insertable=false,updatable=false)
private Set <Course> courses;
I want to filter students by course names and student class id. For now I have worked it out how to filter by class id but I have no idea how to filter by courseId given that Student entity has a set of courses and the tables are related. I have read some articles but no code matches the one I have already.
CriteriaBuilder criteriaBuilder = persistenceStore.createCriteriaBuilder();
CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
Root<Student> root = criteria.from(Student.class);
List<Predicate> params = new ArrayList<Predicate>();
params.add(criteriaBuilder.equal(root.get("classId"),classId));
Predicate[] predicates = new Predicate[params.size()];
params.toArray(predicates);
criteria.select(root);
criteria.where(criteriaBuilder.and(predicates));
Query query = persistenceStore.createQuery(criteria);
List<Student> resultList = query.getResultList();
First of all, there is an error in your Entity: the JoinColumn annotation applies to the entity on the inverse side of the relationship, Course in your case.
So, if Course entity has a property student, Student has a property like:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "student")
private Set<Course> courses;
and in Course entity you have (here it also states that in the db the table course has a field called "student":
#JoinColumn(name = "student", referencedColumnName = "id")
#ManyToOne(optional = false)
private Student student;
Read this link for an entry-level explaination on how to map entity relationships.
Regarding the Criteria Query, since you want to retrieve a List of StudentS, you can define your CriteriaQuery in a more type safe way:
CriteriaQuery<Student> criteria = criteriaBuilder.createQuery(Student.class);
Regarding the question, you have to join the tables in this way:
SetJoin<Student, Course> courses = root.join("courses");
or, using MetaModel:
SetJoin<Student, Course> courses = root.join(Student_.courses);
(had the OneToMany property been defined as a List or a Collection, you'd have had to use the corresponding ListJoin and CollectionJoin classes).
on the courses you can apply the desired Predicate conditions (supposing that Course entity has a string property called courseName):
Predicate p = criteriaBuilder.equal(courses.get("courseName"), "name-to-look-for");
or, using Metamodel:
Predicate p = criteriaBuilder.equal(courses.get(Course_.courseName), "name-to-look-for");
Finally, in order to concatenate correctly a list of predicates, you can use (at least) two techniques:
Predicate p1 = ...;
Predicate p2 = ...;
criteria.where(criteriaBuilder.and(p1, p2));
or
List<Predicate> conditions = new ArrayList<Predicate> ();
conditions.add(p1);
conditions.add(p2);
criteria.where(conditions.toArray(new Predicate[] {}));
See also this excellent article.
type mismatch: cannot convert from cascadetype to cascadetype[]
Answer is:
import javax.persistence.CascadeType;
import this line

Hibernate OneToMany and ManyToOne confusion! Null List!

I have two tables...
For example - Company and Employee (let's keep this real simple)
Company( id, name );
Employee( id, company_id );
Employee.company_id is a foreign key.
My entity model looks like this...
Employee
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "company_id")
Company company;
Company
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
List<Employee> employeeList = new ArrayList<Employee>();
So, yeah I want a list of employees for a company.
When I do the following...
Employee e = new Employee();
e.setCompany(c); //c is an Company that is already in the database.
DAO.insertEmployee(e); //this works fine!
If I then get my Company object it's list is empty!
Ive tried endless different ways from the Hibernate documentation!
Obviously not tried the correct one yet!
I just want the list to be populated for me or find out a sensible alternative.
Help would be greatly appreciated, thanks!
You need to make the relationship bidirectional. Yours is two unidirectional ones. Add mappedBy = "company" to employeeList. This tells hibernate the employee list is simply the reverse of the ref from Employee to Company
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "company")
List<Employee> employeeList = new ArrayList<Employee>();
I dont think you should do
List<Employee> employeeList = new ArrayList<Employee>();
This should be done when you have defined the side which has the list as the owning side which in your case is the Employee and not the Company. I think each time you load the Company instance, you are being supplied with a new instance of employeeList, not sure though. But surely, there is no need to instantiate the new ArrayList each time.

Categories

Resources