Reputation: 23415
Let's say I have a House entity which maps to many Person entities. I then load an existing House which has 20 occupants.
beginTransaction();
House house = houseDao.find(1L);
commitTransaction();
Later in the code, I can then add a new Person to the House:
...
List<Person> people = house.getPeople();
people.add(new Person("Dilbert"));
....
When I make the call:
session.saveOrUpdate(house);
Hibernate performs 21 queries: 1 to SELECT the House and 20 to SELECT each existing Person in the House.
I'm sure it's a small issue on my part, however, what should I do so that I can add a new Person to the house without having such a heavy hit on the database in this situation?
This is all done within the same session.
Upvotes: 0
Views: 920
Reputation: 1061
I think this is a potential answer to your problem, but perhaps not in the exact way you are thinking.
I haven't found a way to make Hibernate behave the way you want (it doesn't have that level of granularity). I'm sure you could code it yourself if you wanted. But if you are really worried about the performance hit on the DB, turn the problem around and focus on how to minimize the load on the DB.
The best way to optimize calls from Hibernate to the DB is to make sure all of the hibernate object tables are indexed. Hibernate calls the table using the primary key of the parent object. Those 20 queries will take up very little DB processing time if they hit an index and are located quickly. DB's are built to handle lots of transactions like this - the expensive part is locating the data on disk and loading it for your use.
Finally, I would also add an object cache. This could potentially eliminate the round-trip to the DB, saving your program a lot of I/O wait time.
Upvotes: 0
Reputation: 454
Because you really haven't given alot on the definition of your objects, I am going to make some assumptions.
Make sure the house is defined like:
@Entity
public class House implements Serializable {
@Id
private int id;
@OneToMany(mappedBy="house")
private Set<Person> people;
... rest of your class
}
@Entity
public class Person implements Serializable {
@Id
private int id;
@ManyToOne(targetEntity=House.class)
private House house;
@Column(name="person_name")
private String name
... rest of your class
public Person(House house, String name) {
this.house = house;
this.name = name;
}
}
now your code:
beginTransaction();
House house = houseDao.find(1L);
commitTransaction()
... your magic
Person person = new Person(house,"Dilbert");
session.saveOrUpdate(person);
In the example above, when you are working with Parent/Child relations (doesn't matter it it is one to many or many to many), you can make the relationship through the child. I generally stay away from just doing blanket updates through the parent. In your example you see that is alot of over head. When you start working where there is thousands of records, it becomes impossible.
The other thing to look into, depending on your model is making subtle changes to the annotations to your lists. An Example:
@Entity
public class House implements Serializable {
@Id
private int id;
@OneToMany(mappedBy="house")
@Fetch(FetchMode.JOIN)
private Set<Person> people;
... rest of your class
}
Given that @Fetch is not part of the JPA spec, but a hibernate annotation, this, depending on your model can give a big performance boost because it will grap the house object and all the people in a single query. This is very effective if your are limiting the number of houses and people that belongs to house. If you are getting very large resultsets, this might not be a good situation. The next example might be more suitable:
@Entity
public class House implements Serializable {
@Id
private int id;
@OneToMany(mappedBy="house")
@Fetch(FetchMode.SUBSELECT)
private Set<Person> people;
... rest of your class
}
This will use two querys to grab all the objects. One query for the House object (or multiply house objects depending on the query) and One query for all the people for the house object(s).
Upvotes: 3