I'm running into the issue of the child not having the reference to its parent. Supposedly I've got the setup of a bidirectional relationship where I can get children from the parent and get the parent from the child; however my childrens' parents are always coming back null.
I've got a parent of,
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Company {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent(mappedBy = "company")
private List<Employee> employees = new ArrayList<Employee>();
}
With a child of,
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private Company company;
#Persistent
private String username;
}
I'm persisting the object by,
Company newCompany = new Company();
Employee newEmployee = new Employee();
newEmployee.setUsername("ryan");
newCompany.getEmployees().add(newEmployee);
pm.makePersistent(newCompany);
I'm accessing the object like so,
Query query = pm.newQuery(Employee.class,"username == s");
query.declareParameters("String s");
List<Employee> employees = (List<Employee>)query.execute(username);
pm.close();
if (employees.size() > 0)
{
Employee employee = employeeList.get(0);
...
}
I'm then seeing "company" as null when debugging while the rest of the employee's fields are populated. Any thoughts on what I'm missing?
Fetching of the parent depends on where you check it, according to the object state. If your PM has been closed by that point then it will not have fetched the parent field. Touching the parent field before closing the PM (and having retainValues set to true, or detaching at close of the PM) will result in the parent field being set.
Related
I am trying to implement a tree referencing itself (same class) with CRUD operations using Java and Hibernate. My class is :
#Entity
#Table(name="Person")
public class Person implements Comparable<Person>{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#ManyToOne(cascade = {CascadeType.ALL})
private Person father;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
private List<Person> children = new ArrayList<Person>();
}
Insertion works good, at each insertion I set person's father to person and add person to father's children. While deleting, if I delete the person, it complains that person id is referenced by father, if I delete father, it complains that father id is referenced by person. So, what is the correct procedure of deleting or updating? There are similar questions, but I can not find the exact explanation for this bidirectional referencing problem.
So, I have found a solution to the problem thanks to #Al1's mapped byannotation. Still, after that I could not retrieve objects due to LazyInitializationException , but was able to delete the Leafs in a tree.
I have fixed that issue by changing private List<Person> children= new ArrayList<Person>(); to private Collection<Person> children = new LinkedHashSet<Person>();
The class now looks like:
public class Person implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#ManyToOne(cascade = {CascadeType.ALL})
private Person father;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, mappedBy= "father")
private Collection<Person> children = new LinkedHashSet<Person>();
}
In order to delete the tree node, I had to to load the children by Hibernate.initialize(this.getChildren()); and then recursively delete every node. My function for deletion:
public static String deletePerson(Person p){
Transaction trns = null;
Session session = HibernateUtil.buildSessionFactory().openSession();
try {
trns = session.beginTransaction();
Hibernate.initialize(p.getChildren());
if (p.hasChildren()){
Collection<Person> children = p.getChildren();
for (Person person : children) {
deletePerson(person);
}
String hql = "delete from Person where name = :name";
session.createQuery(hql).setString("name", p.getName()).executeUpdate();
session.getTransaction().commit();
return "success";
}
else {
String hql = "delete from Person where name = :name";
session.createQuery(hql).setString("name", p.getName()).executeUpdate();
session.getTransaction().commit();
return "success";
}
} catch (RuntimeException e) {
if (trns != null) {
trns.rollback();
}
e.printStackTrace();
} finally {
session.flush();
session.close();
}
return "failure";
}
Hope this helps somebody who works with Hibernate and trees:)
I have an EJB many-to-many (bi-directional) relation between classes (entity-classes) Person and Hobby. There are corresponding tables in the database, called PERSON and HOBBY, as well as a table PERSON_HOBBY for the many-to-many relationship.
As I will detail below, the problem is that whenever I try to persist a person with hobbies, I run into a Foreign Key constraint violation. This is because the entityManager tries to save new rows into PERSON_HOBBY that contain references to a person-entity with ID=0, which doesn’t exist in the PERSON table. I’ll come back to that later, but first I’ll show the relevant parts of the entity classes.
First, here is entity class Person:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
#ManyToMany(cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
/* Note: I used to have CascadeType.PERSIST in the above line as well, but
it caused "Detached object passed to persist" exceptions whenever I tried to
persist a person with hobbies. So I suppose I was right in deleting
CascadeType.PERSIST...? */
#JoinTable(name = "PERSON_HOBBY",
joinColumns = #JoinColumn(name="personId", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="hobbyId", referencedColumnName="id"))
private List<Hobby> hobbies = new ArrayList<Hobby>();
public List<Hobby> getHobbies () {
return hobbies;
}
public void setHobbies (List<Hobby> hobbies) {
this.hobbies = hobbies;
for(Hobby h:hobbies) { // this is to maintain bi-directionality
h.addPerson(this);
}
}
// other getters and setters omitted here.
Then entity class Hobby:
#Entity
public class Hobby {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String description;
#ManyToMany(mappedBy = "hobbies", fetch = FetchType.EAGER)
private List<Person> persons;
public Hobby() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// getter and setter for Description omitted here.
public List<Person> getPersons () {
return persons;
}
public void setPersons (List<Person> persons) {
this.persons = persons;
}
public void addPerson (Person p) {
this.persons.add(p);
}
}
I also have a stateless session bean, that’s shown here as far as relevant to the issue:
#Stateless
#Default
public class PersonRepositoryImpl implements PersonRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public Person create(Person p) {
entityManager.persist(p);
entityManager.flush();
return p;
}
#Override
public Person createPersonWithHobbies(Person p, List<Hobby>hobbyLijst) {
p = create(p); // I've also tried simply: create(p);
System.out.println("The newly assigned ID for the persisted
person is: " + p.getId());
// That last line **always** prints the person-ID as being 0 !!!!
p.setHobbies(hobbyLijst);
entityManager.merge(p); // This should save/persist the person's hobby's!
entityManager.flush();
return p;
}
}
Now from my servlet, I've been trying in two different ways. First, I tried calling method create(p) on the above session bean. That is, after creating a new Person instance p, setting all its non-relational fields, AND calling setHobbies on it (with a non-zero list of Hobby objects taken from the database), I called:
personRepo.create(p);
But this resulted in the Foreign Key (FK) exception:
INSERT on table 'PERSON_HOBBY' caused a violation of foreign key
constraint 'FK_EQAEPVYK583YDWLXC63YB3CXK' for key (0). The statement
has been rolled back.”
The FK-constraint mentioned here is the one in PERSON_HOBBY referring to PERSON.
The second way I tried was to make the following call from the servlet:
personRepo.createPersonWithHobbies(p, hobbyLijst);
where, just like before, p is the new person object; and hobbyLijst is that person's list of hobbies. And this resulted in the exact same FK-exception as the earlier call to personRepo.create(p).
Importantly, the println statement within method createPersonWithHobbies, calling getId() on the newly persisted person-object, ALWAYS gives that object's ID as being 0. Which I suppose does explain the FK-exception, since there's no person entity/row in the PERSON table with an ID of 0, nor is there supposed to be one. But of course the getId() call should not output 0. Instead, it should output the newly generated ID of the newly persisted person entity. (And yes, it IS persisted correctly in the PERSON tabel, with a correctly generated ID>0. So the correct ID is there in the PERSON-table - it just seems to be invisible to the entityManager and/or the container.)
Thanks.
Using the peristance manager, how can I retrieve a child object knowing a child property and the parent key?
The Parent is defined like this:
public class User {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key id;
#Persistent(mappedBy = "user")
#Element(dependent = "true")
private List<Section> sections;
...
And the child is defined like this:
public class Section {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent
private User user;
#Persistent
private String title;
...
Knowing the 'User' id and the 'Section' title, how can I retrieve the section?. I was trying to build a query to retrieve the section using something like this: 'where title == xxx AND user.id ¿? == xxx' but I'm not sure how to specify the user id. Is there any way to do it using queries or methods from the persistance manager?
Thanks.
I finally made it with this method:
public static Section getSectionByTitle(String title, Key user_key){
PersistenceManager pm = PMF.get().getPersistenceManager();
Query query = pm.newQuery("select from "+Section.class.getName()+" WHERE title == s && user == keyParam");
query.declareParameters("String s, String k");
query.setUnique(true);
Section section = (Section) query.execute(title, user_key.getId());
return section;
}
You can call this method on the query object:
q.setAncestor(ancestorKey);
Read this page for more information (Ancestor Queries).
I remember seeing something like 'where ANCESTOR = ' syntax but I cannot find any reference for it now.
I am trying to load the full object graph for User, which contains a
collection of decks, which then contains a collection of cards, as
such:
User:
#PersistenceCapable(detachable = "true")
#Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
#FetchGroup(name = "decks", members = { #Persistent(name =
"_Decks") })
public abstract class User {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
protected Key _ID;
#Persistent
protected String _UniqueIdentifier;
#Persistent(mappedBy = "_Owner")
#Element(dependent = "true")
protected Set<Deck> _Decks;
protected User()
{
}
}
Each Deck has a collection of Cards, as such:
#PersistenceCapable(detachable = "true")
#FetchGroup(name = "cards", members = { #Persistent(name =
"_Cards") })
public class Deck {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key _ID;
#Persistent
String _Name;
#Persistent(mappedBy = "_Parent")
#Element(dependent = "true")
private Set<Card> _Cards = new HashSet<Card>();
#Persistent
private Set<String> _Tags = new HashSet<String>();
#Persistent
private User _Owner;
}
And finally, each card:
#PersistenceCapable
public class Card {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key _ID;
#Persistent
private Text _Question;
#Persistent
private Text _Answer;
#Persistent
private Deck _Parent;
}
I am trying to retrieve and then detach the entire object graph. I
can see in the debugger that it loads fine, but then when I get to
detaching, I can't make anything beyond the User object load. (No
Decks, no Cards). At first I tried without a transaction to simply
"touch" all the fields on the attached object before detaching, but
that didn't help. Then I tried adding everything to the default fetch
group, but that just generated warnings about GAE not supporting
joins. I tried setting the fetch plan's max fetch depth to -1, but
that didn't do it. Finally, I tried using FetchGroups as you can see
above, and then retrieving with the following code:
PersistenceManager pm = _pmf.getPersistenceManager();
pm.setDetachAllOnCommit(true);
pm.getFetchPlan().setGroup("decks");
pm.getFetchPlan().setGroup("cards");
Transaction tx = pm.currentTransaction();
Query query = null;
try {
tx.begin();
query = pm.newQuery(GoogleAccountsUser.class); //Subclass of User
query.setFilter("_UniqueIdentifier == TheUser");
query.declareParameters("String TheUser");
List<User> results = (List<User>)query.execute(ID); //ID = Supplied
parameter
//TODO: Test for more than one result and throw
if(results.size() == 0)
{
tx.commit();
return null;
}
else
{
User usr = (User)results.get(0);
//usr = pm.detachCopy(usr);
tx.commit();
return usr;
}
} finally {
query.closeAll();
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
This also doesn't work, and I'm running out of ideas...
I'm sure reading of the log (Debug level) would tell you way more, since it certainly tells you when it is detaching things. Perhaps GAE/J is not respecting lazy loading at detach ? DataNucleus itself works fine, with all other datastores.
Why call FetchPlan.setGroup() when that overwrites all existing groups ? addGroup() makes more sense to me.
I've omitted some code(package declarations, imports, other fields)
for shortness.
I have here simple One-to-Many relation.
It worked fine till this moment.
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
class Restaurant implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key id
#Persistent(mappedBy = "restaurant")
List<RestaurantAddress> addresses = new ArrayList<RestaurantAddress>()
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
class RestaurantAddress implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key id
#Persistent
Restaurant restaurant
}
Now i need to get(select) all the Restaurants from DB:
def getRestaurantsToExport(final String dst, final int count) {
String field = restaurantExportFields[dst]
return transactionExecute() { PersistenceManager pm ->
Query q = pm.newQuery(Restaurant.class)
q.filter = "$field == null"
q.setRange(0, count)
return q.execute()
}
}
But there are on problem - query gives me 12 restaurants(as in DB) but
every Restaurant has 0 Address but in Datastore every Restaurant has
minimum 2 addresses.
Have anyone the same problem or knows the solution ?
are you sure the Addresses are not lazy loaded? Just a guess... is there some way to force an "eager" loading of the objects
If someone will have the same problem:
Replace
#Persistent(mappedBy = "restaurant")
List<RestaurantAddress> addresses = new
ArrayList<RestaurantAddress>
with
#Persistent(mappedBy = "restaurant",defaultFetchGroup = "true")
List<RestaurantAddress> addresses = new
ArrayList<RestaurantAddress>
Another method is that you have to "touch" addresses property for
every Restaurant in the retrieved list before closing
PersistentManager. After PersistenManager being closed you cannot
retrieve anything from datastore and Restaurant keeps null.
Solution found with help of google-appengine-java users.