My domain model in my Java EE 6 application contains bi-directional relationships like the following:
#Entity
public class Users implements PrimaryKeyHolder<String>, Serializable {
#Id
private String username;
#ManyToMany(mappedBy= "users")
private List<Category> categories;
public List<Category> getCategories() {
if (categories == null) {
categories = new ArrayList<Category>();
}
return Collections.unmodifiableList(categories);
}
public void addCategory(Category category) {
if (categories == null) {
categories = new ArrayList<Category>();
}
categories.add(category);
if (!category.getUsers().contains(this)) {
category.addUser(this);
}
}
public void removeCategory(Category category) {
if (categories == null) {
categories = new ArrayList<Category>();
}
categories.remove(category);
if (category.getUsers().contains(this)) {
category.removeUser(this);
}
}
public void setCategories(Collection<Category> categories) {
if (this.categories == null) {
this.categories = new ArrayList<Category>();
}
for (Iterator<Category> it = this.categories.iterator(); it.hasNext();) {
Category category = it.next();
it.remove();
if (category.getUsers().contains(this)) {
category.removeUser(this);
}
}
for (Category category : categories) {
addCategory(category);
}
}
}
#Entity
public class Category implements PrimaryKeyHolder<Long>, Serializable {
#Id
private Long id;
#ManyToMany
private List<User> users;
public List<User> getUsers() {
if (users == null) {
users = new ArrayList<User>();
}
return Collections.unmodifiableList(users);
}
protected void addUser(User user) {
if (users == null) {
users = new ArrayList<User>();
}
users.add(user);
}
protected void removeUser(User user) {
if (users == null) {
users = new ArrayList<User>();
}
users.remove(user);
}
}
UPDATE: I added relationship management code. Relationships are only set on the user side, therefore, the add/remove methods are protected in the Categoriy class. I set the categories on the user via setCategories.
Eclipselink correctly generates a join table CATEGORY_USERS. However, it does not persist any information in it (it only caches the information). E.g. when I execute a find operation on the entity manager (e.g. a user), it returns the complete object graph (including the category relationship). But when I look at the tables, information are not updated (even though the transactions are committed). I also inserted a flush operation in my code, without success. Basic information (like String, Integer, etc. columns) gets correctly persisted and updated. After turning the log level to FINE, I can see that no SQL statements are executed for the relationships and the join table, respectively. But I do see SQL statements for uni-directional relationships.
My datamodel is covered by extensive unit tests, which all pass successfully. I basically do the same operation as in the container, commit the transaction, reload the entities from the db and check if the relationships are correctly set, which they are (I'm using the in-memory derby database for testing).
My app server is Glassfish v3.1-b17.
Any help is appreciated.
Thanks,
Theo
Ensure you are setting both sides of the relationship. The specification requires that the application sets both sides of the relationship as there is no relationship maintenance in JPA.
After endless hours of trying I finally got to a solution: I simply changed the owning side of the relationship, i.e. I put the mappedBy attribute to the category entity like this:
#ManyToMany(mappedBy= "categories")
private List<User> users;
The explanation for this can be found here
Four points:
1.- When you have an error, it's more simple find solution isolating them in an example (Or unit test) that reproduces the error. In your case, you could do an example with more simple getter and setter (for example, removing unmodifiableList use and other innecesary methods for testing actually issue).
2.- I advise you to use pojos for model, without any logic. So, remove logic from pojos.
3.- We are using eclipselink and we do not have problems persisting relations. So, it is more possible that error will be in your code.
4.- Test annoting relation with "cascade = javax.persistence.CascadeType.ALL"
Apology for my poor English :(
Related
I am currently working on a medium sized Java project with Hibernate and I have come across what seems to be a rare but quite persistent error. The situation is as follows: I have a Student entity who has a bidirectional many-to-many relation to an Education entity (implemented as a join table on the database) and an Admin entity who is a subclass of Student. My code allows for a Student to be "upgraded" to an Admin by removing the Student from the database, creating a new Admin based on the Student and persisting this Admin. However, whenever this happens, Hibernate throws the following error on EntityManager.flush():
org.hibernate.HibernateException: Found two representations of same collection: domain.Student.enrolledEducations
Below you can find the relevant code:
Education class
#Entity
public class Education {
...
#ManyToMany
#JoinColumn(name = "education_id")
private Set<Course> courses = new HashSet<>();
Student class
#Entity
public class Student {
....
#ManyToMany
#JoinColumn(name = "student_id")
private Set<Education> enrolledEducations = new HashSet<>();
Admin class
#Entity
public class Admin extends Student {
...
public Admin(Student student) {
this.setId(student.getId());
this.setFirstName(student.getFirstName());
this.setLastName(student.getLastName());
this.setEmail(student.getEmail());
this.setSalt(student.getSalt());
this.setSuperAdmin(false);
this.setEnrolledEducations(student.getEnrolledEducations());
this.setSessions(student.getSessions());
this.setManagedEducations(new HashSet<Education>());
}
Database methods
public Admin upgrade(Person person) {
Admin admin;
if (person instanceof Student){
removeStudent((Student) person);
admin = new Admin((Student) person);
}
else{
removePerson(person);
admin = new Admin(person);
}
addAdmin(admin); //exception happens here
return admin;
}
public void addAdmin(Admin admin) {
manager.getTransaction().begin();
if(manager.contains(admin)){
manager.merge(admin);
}
else{
manager.persist(admin);
}
manager.flush(); //exception happens here
manager.getTransaction().commit();
}
Test method
#Test
public void getEducationsForAdmin_and_upgrade_to_admin_work_correctly(){
educationSetup();
Admin admin1 = facade.upgrade(student1); //exception happens here
Admin admin2 = facade.upgrade(student2);
admin1.addNewEducation(education1);
admin1.addNewEducation(education2);
admin2.addNewEducation(education1);
facade.updateAdmin(admin1);
facade.updateAdmin(admin2);
Set<Education> educations1 = new HashSet<>(facade.getEducationsForStudent(admin1));
Set<Education> educations2 = new HashSet<>(facade.getEducationsForStudent(admin2));
assertTrue("admin 1 does not have exactly 1 education", educations1.size()==1);
assertTrue("admin 2 does not have exactly 2 educations", educations2.size()==2);
assertTrue("admin 1 does not have the IT education",educations1.contains(education1));
assertTrue("admin 2 does not have the IT education",educations2.contains(education1));
assertTrue("admin 2 does not have the M education",educations2.contains(education2));
}
It seems that you have a problem that both Admin and Student have the same identifier.
Since the Admin is created by calling the new function, it is not in the persistent state, the code
manager.contains(admin)
will always return false, so it will always go to the manager.persist statement.
Since Admin is a different object with the same identifier, you will get the exception
Found two representations of same collection
All you need to do is to add
manager.delete(person)
in your
removePerson
function. It should solve this problem.
I faced with a very strange behavior in my web app with spring 3 and hibernate-core 3.5.1-Final.
For simplicity i provide my code..
if(ripid!=null){ //Parameter
Appuntamento apDaRip = appuntamentoService.findById(ripid);
if(apDaRip.getIdpadre()!=null){
apDaRip.setNota("RIPROGRAMMATO n."+ripid.toString()+"\n"+apDaRip.getNota());
apDaRip.setIdpadre(apDaRip.getIdpadre());
}else{
apDaRip.setNota("RIPROGRAMMATO n."+ripid.toString()+"\n"+apDaRip.getNota());
apDaRip.setIdpadre(ripid);
}
try{
apDaRip.setOrarioinizio(null);
apDaRip.setDurata(null);
//apDaRip.setIdappuntamento(null);
}catch(Exception e){e.printStackTrace();}
map.put("appuntamento", apDaRip);
}
di = datiintranetService.findById(DatiintranetService.PASS_X_INTERVENTI);
map.put("passinterventi", di.getBoolean());
The idea behind is to use some data of an object "Appuntamento" for produce a new one.
So i'm going to change some value and before send the object to my view (jsp) i fetch other data by calling findbyid. This cause an update to the Appuntamento object... Off course i don't want this behavior. Someone can have an explanation of this?
Edit-1
Here's the Dao
#Transactional
public class DatiintranetService {
private DatiintranetDAO datiintranetDAO;
public void setDatiintranetDAO(DatiintranetDAO datiintranetDAO) {
this.datiintranetDAO = datiintranetDAO;
}
public DatiintranetDAO getDatiintranetDAO() {
return datiintranetDAO;
}
public Datiintranet findById(Integer id) {
return datiintranetDAO.findById(id);
}
}
and For Appuntamento class I provide to you a snapshot
#Entity
#Table(name = "appuntamento", schema = "public")
public class Appuntamento implements java.io.Serializable {
#Id
#SequenceGenerator(name="appuntamentoID", sequenceName="appuntamento_idappuntamento_seq",allocationSize =1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="appuntamentoID")
#Column(name = "idappuntamento", unique = true, nullable = false)
public Integer getIdappuntamento() {
return this.idappuntamento;
}
}
Edit-2
IF i move thoese two row above the if statement no update occur.
di = datiintranetService.findById(DatiintranetService.PASS_X_INTERVENTI);
map.put("passinterventi", di.getBoolean());
If you query for an entity and change the entity, the default behavior is to persist those changes via an update to the database. This is usually what you want to happen, but obviously not in all cases.
If you want to avoid the update, you need to detach the entity by calling session.evict(apDaRip) where session is a reference to the hibernate session (see Session.evict()). You probably want to evict the entity right after you get it (immediately following the call to findById).
In my new project I am trying to use Hibernate model class, here one user domain class having OneToMany relation userProfile like
class User {
//Some fields and getter setter
//Problematic thing
#javax.persistence.OneToMany(mappedBy = "User")
private Set<UserProfile> userProfiles;
//getter is like
public Set<userProfile> getProfile() {
// the logic
}
public void setProfile() {
// the logic
}
}
So when I try to access this field in grails criteria like
def criteria = User.createCriteria()
List<User> userList = criteria.list() {
userProfiles {
eq("id",1 as long)
}
}
I getting the error like No signature of method: UserService.userProfiles(). I think it might be because of different getter and setter name, 'cause for remaining OneToMany fields the criteria is working fine.
Is there any possible and standard way to address this issue.
This is a more common thing to do:
class User {
static hasMany = [userProfiles: UserProfile]
}
Methods like getUserProfiles(), setUserProfiles(), addToUserProfiles(UserProfile p) etc. are automatically generated. See http://grails.org/doc/latest/ref/Domain%20Classes/hasMany.html.
Then you could do something like this:
def userList = User.withCriteria {
userProfiles {
idEq 1
}
}
I hope that helps.
I am working on an application using Hibernate and I want to delete some records in the database. The relevant Entities are:
#Entity
public class Product {
private String serialNumber;
private Set<Part> parts = new HashSet<Part>();
#Id
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
#OneToMany
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
...
}
#Entity
public class Part implements Serializable {
#Id
#GeneratedValue
private Long part_id;
private String userCode = "";
//getters and setters
....
}
I have let Eclipse implement equals and hashCode in Entity Part based on part_id and userCode. There is also an Entity Factory from which 'begin' all the associations to the other Entities. Therefore, in order to save all the changes it only necessary to execute the comand:
session.update(factory);
All the changes are saved successfully except from the delete from parts. I do:
products.getParts.remove(part);
The issues comig out are:
1) In some cases is part from the Set not removed although the comparison to a part in the Set with equals true returns (the part is in Set according to equals but it is not removed)
2) Even if the remove in the Set succeeds, the record in the database is not deleted.
Based on the above ascertainments what is the best way to remove the records in this case using not loads of queries?
You need to explicitly remove the child:
session.delete(part);
From Hibernate Docs:
The following code:
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
c.setParent(null);
session.flush();
will not remove c from the database. In this case, it will only remove
the link to p and cause a NOT NULL constraint violation. You need to
explicitly delete() the Child.
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();
When using hibernate to map relationships you must be aware of two main concerns:
Which is the owner of the relationship? The owner is the side of the relation whose changes will be persisted in database. In your case the owner is the Part object.
Is a true parent/child relationship or simply a composition relationship? In your case I think the answer is composition
If you want to manage the relation using the set, you have two options:
use #ElementCollection instead of #OnetoMany
change ownership. Something like this:
#OneToMany
#JoinColumn(name="part_id")
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
However, the second option is not recommended here. See section 2.2.5.3.1.2.
I hava a basic Hibernate/JPA question. I want to find a best practice solution for saving entities. I have a List of Entities and many of them might be altered so I want to save them all at once.
I believe everything is pretty much standard. (Just example code for readability reasons)
Entity: Car
#Entity
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private id;
private String model;
// .... Setter
// .... Getter
}
Service Class: CarService
#Named
#Transactional
public class CarServiceImpl implements CarService {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<Car> findAll() {
TypedQuery<Car> q = entityManager.createQuery(
"FROM Car", Car.class);
return q.getResultList();
}
#Override
public void saveEntity (Car car) {
/* What exactly am I doing here? */
}
}
Controller: CarEditController
#Named
public class CarEditController implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private CarService carService;
private List<Car> cars;
public List<Car> getCars () {
return carService.findAll();
}
public void setCars (List<Car> cars) {
this.cars = cars;
}
public void btn_newClick() {
Car newCar = new Car();
car setModel("BMW");
cars.add(newCar);
}
public void btn_saveClick() {
for (Car car : cars) {
carService.saveEntity(car);
}
}
}
I found quite a few ways of saving the entity. The obvious are entityManager.merge(car) for existing entities and entityManager.persist(car) for new ones. In theory thats easy but how do I know which entity is new?
The documentation suggests entityManager.flush(), which in theory updates all existing and inserts all new entities. It works but only if I fetch the entity with find().
The Question:
I want to fetch all entities in one step, add new ones and then save them all in one methode (btn_saveClick()). How is this task best accomplished?
Just check if the #Id is been set.
#Override
public void saveEntity (Car car) {
if (car.getId() == null) {
entityManager.persist(car);
} else {
entityManager.merge(car);
}
}
More common approach, however, is to offer separate create() and update() service methods.
I'm not familiar with JPA but in hibernate there is session.saveOrUpdate()
for(Car car : existingAndNewCars)
{
session.saveOrUpdate(car);
}
Update:
As i understand JPA, its merge is like session.merge which is totally different as it doesn't track changes to object supplied to it while persist/save/update/saveOrUpdate would track subsequent changes to car, leading to subtle differences
Update:
since you use the same entitymanager it should suffice to
#Override
public void saveEntity (Car car) {
if (car.getId() == null) {
entityManager.persist(car);
}
without the subtle difference of persist and merge
The flush operation will operate on all entities in the current Hibernate session - new entities are inserted and existing entities are updated if they have changed.
You need to ensure that all entities are attached to the session. You do this by using merge as you correctly say. After you have loaded all of the entities the session is closed when the transaction ends. Your objects are then in a detached state i.e. have been persisted but are no longer attached to a session.
I would amend your logic so that your carService#save takes a List. It can then call merge on each one (attaching them to the session). Then when your transaction ends Hibernate will flush all changes to the database at once.