JDO in Google App Engine: How should polymorphic relationships be implemented? - java

I'm working on GAE-based applications, which uses JDO to access datastore. I need to implement polymorphic relationship between persisted objects.
There's abstract parent class:
#PersistenceCapable
#Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class Parent {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
String id;
// ....
And several child classes:
#PersistenceCapable (identityType = IdentityType.APPLICATION)
public class Child extends Parent {
// ....
Also, there's one more class, which should have reference to one of child classes. According to "Polymorphic Relationships" section of "Entity Relationships in JDO" article, the best way to implement such relationship is to store key of an object, so this class looks in the following way:
#PersistenceCapable (identityType = IdentityType.APPLICATION)
public class OtherClass {
#Persistent
private String reference;
// ....
I retrieve string key of referenced object from instance of OtherClass. Then I would like to obtain referenced object itself: it's an instance of one of Parent subclasses. BUT:
If I do it with pm.getObjectById(oid) method:
Object object = pm.getObjectById(reference);
JDOObjectNotFoundException exception is thrown (javax.jdo.JDOObjectNotFoundException: No such object FailedObject:...).
If I do it with getObjectById(class, key) method:
Parent object = pm.getObjectById(Parent.class, reference);
FatalNucleusUserException exception is thrown (org.datanucleus.store.appengine.FatalNucleusUserException: Received a request to find an object of kind Parent but the provided identifier is the String representation of a Key for kind Child)
What is correct way to retrieve instance of one of subclasses referenced in another entity?
UPDATE: I found this thread in GAE google group, but frankly it did not help me a lot.

I found the same problem with JDO and App Engine, so I started a project that implements a workaround for this. https://code.google.com/p/datanucleus-appengine-patch/
My first test with the code I have now looks okay, feel free to try it out at give me some feedback.
Actually my workaround may solve your problem 2 ways.
I implemented a getObjectById(class, id) that also looks for kinds that are instances of the provided class.
I implemented a getObjectById(oid) that does some special handling of lookup if oid is of type com.google.appengine.api.datastore.Key, then it will figure out the correct class to return.
I added a new annotation #PolymorphicRelationship that will make is easy to handle to workaround that App Engine describes, with storing the keys. Sample shown below:
#Persist
public Collection<Key> myChildKeys;
#NotPersistent
#PolymorphicRelationship(keyField ="myChildKeys")
public Collection<TestChild> myChildren;

I'm using this rather cancerous and smelly anti-pattern to get around this limitation of JDO/App Engine.
#JsonIgnore
#Persistent(mappedBy="account")
private List<XProvider> xProviders;
#JsonIgnore
#Persistent(mappedBy="account")
private List<YProvider> yProviders;
// TODO: add extra providers here and in getProviders() below...
And then to get the collection:
public List<XProvider> getXProviders() {
if (xProviders == null) {
xProviders = new ArrayList<XProvider>();
}
return xProviders;
}
//etc with other getters and setters for each collection.
public List<Provider> getProviders() {
List<Provider> allProviders = new ArrayList<Provider>();
// TODO: add extra providers here...
allProviders.addAll(getXProviders());
allProviders.addAll(getYProviders());
return allProviders;
}
It's a bad solution, but any port in a storm...
(Also relates a little to this bug, using interfaces as the collection type http://code.google.com/p/datanucleus-appengine/issues/detail?id=207)

App Engine's JDO layer doesn't currently support polymorphism. In fact, I'm not sure if JDO supports it in general or not.

Related

Error when implementing interfaces with greenDAO

I'm trying to use greenDAO's implementsInterface method, here is most of my main generator class:
private static void addTables(final Schema schema) {
Entity photo_pronoun = addCard(schema);
Entity simple_pronoun = addSimpleCard(schema);
Entity original_pronoun = addOriginalCard(schema);
//implementsInterface method
original_pronoun.implementsInterface("addNewCard");
simple_pronoun.implementsInterface("addNewCard");
}
private static Entity addCard(final Schema schema) {
Entity card = schema.addEntity("addNewCard");
card.addIdProperty().primaryKey().autoincrement();
card.addStringProperty("cardName").notNull();
card.addStringProperty("cardSpeech");
card.addByteArrayProperty("cardIcon");
return card;
}
private static Entity addSimpleCard(final Schema schema) {
Entity card = schema.addEntity("addSimpleCard");
card.addIdProperty().primaryKey().autoincrement();
card.addStringProperty("cardName").notNull();
card.addStringProperty("cardSpeech");
card.addByteArrayProperty("cardIcon");
return card;
}
private static Entity addOriginalCard(final Schema schema) {
Entity card = schema.addEntity("addOriginalCard");
card.addIdProperty().primaryKey().autoincrement();
card.addStringProperty("cardName").notNull();
card.addStringProperty("cardSpeech");
card.addByteArrayProperty("cardIcon");
return card;
}
When I run this to create my files I get an error in original_pronoun and in simple_pronoun on my first line at addNewCard:
interface expected here
public class addOriginalCard implements addNewCard {
I get that error because its not an interface, but I'm confused on how to fix it. The implementsInterface method says it takes a string but I've tried this and the database name with no joy. Can anyone tell me what I need to do here?
This is not a greenDAO problem: addNewCard is a class, not an interface. If your model class needs to inherit from another class, you have to use setSuperclass() method. Example:
original_pronoun.setSuperclass("addNewCard");
Note that greenDAO doesn't support another entity as a super class yet, if this is your intention.
Check greenDAO docs for Inheritance and Interfaces.
See also this question: Implements vs. Extends. When to use? What's the Difference?

Add Java class to existing inheritance hierarchy in JPA with Hibernate

I have an abstract base class with existing subclasses that is mostly used for defining a number of common fields and associated methods. I have a separate concrete class that "organically evolved" (i.e., bad design due to unforeseen feature requests) to end up with all the same fields defined in that abstract subclass.
Is there any way of having that separate class extend the abstract class and carry over the data of existing stored instances of that separate class? I would like to use InheritanceType.SINGLE_TABLE, but if another strategy makes it easier or possible, I guess that's fine too.
Also, those are entities referenced in other entities (OneToMany). Is that a problem? Hibernate uses only one global sequence for assigning entity ids - so it should in theory be possible to not break those references even if the data is moved to another table, right?
Already tried a few things, but no luck so far (e.g., add the "extend" to the separate class, hard-code it to use the same table as the base class, manually add a field for the discriminator...).
I am also happy about any pointers to examples/docs on how to carry out class hierarchy changes and other data model changes with JPA/Hibernate without losing data!
So, here's a simplified example of the situation. Base is the abstract base class that already has sub-classes.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "Base")
public abstract class Base {
private long persistenceId;
private String privateField;
#Id
#GeneratedValue
public long getPersistenceId() {
return persistenceId;
}
public void setPersistenceId(long persistenceId) {
this.persistenceId = persistenceId;
}
[...]
}
#Entity
public class SubclassToBe {
private long persistenceId;
private String privateField;
private String someFieldNotInBaseClass;
#Override
#Id
#GeneratedValue
public long getPersistenceId() {
return persistenceId;
}
#Override
public void setPersistenceId(long persistenceId) {
this.persistenceId = persistenceId;
}
[...]
}
The goal would be to have SubclassToBe inherit from Base, removing the definitions of shared fields but keeping the information stored there. And at the same time, not break references to the persistence ids of SubclassToBe objects that are used in other objects as part of OneToMany relations.

Java #entity nested relationships

I have a parent entity that has child entities (A) who in turn have their own child entities (B).
#Entity
public class Parent {
#OneToMany
Set<ChildA> childrenA;
}
#Entity
public class ChildA {
#OneToMany
Set<ChildB> childrenB;
}
I'm trying to display the data via a JSF dataTable. I would like to show the following.
Parent1 | NumberOfRelatedChildrenB
Parent2 | NumberOfRelatedChildrenB
To generate the rows in the dataTable I'm using a MangagedBean which gets a List of the Parents via a ParentFacade.findAll(), but I can't figure out how I can get a List of all the associated ChildBs. I guess I could add a #OneToMany ChildB relationship to the Parent entity, but I was hoping there would be a way to get them via the ChildA relationship?
Thanks in advance & sorry for the poor explanation!
No, I suggest to avoid creating an additional relationship in this case. One way is to create a method in the managed bean that returns the number of related ChildB given an input Parent:
#ManagedBean
public class MyManagedBean {
private List<Parent> parentList;//+getter
private Map<Long, Long> relatedChildrenB = new HashMap<Long,Long>();//+getter
#EJB
private ParentFacade parentFacade;
#PostConstruct
public void init() {
parentList = parentFacade.findAll();
for (Parent parent : parentList) {
relatedChildrenB.put(parent.getId(), parentFacade.getNumberOfRelatedChildrenB(parent));
}
}
and in the facelets page:
<h:dataTable value="#{myManagedBean.parentList}" var="parent">
...
#{myManagedBean.relatedChildrenB[parent.id]}
</h:dataTable>
and implement the corresponding queries in the facade service class.
Note that passing an object using parenthesis () in the previous revision in an EL expression requires EL 2.2 and thus either a Servlet 3.0 compatible container or applying some workaround. This solution does not need method call with parameters.
Finally, note that in my final edit I have followed the wise suggestion of skuntsel to avoid db calls in getter methods.
Simple solution would be to put a method in Parent that returns children count and then use it in dataTable column e.g.
#Transient
public int getAllChildrenCount() {
// iterate through children list and count
}
On view:
#{parent.allChildrenCount}

Need a confirmation regarding persisting a referenced (embedded) object using jdo+datanucleus

Hi I am persisting a class with a collection(List) of interface.
I see this on link
http://www.datanucleus.org/products/accessplatform_2_1/jdo/orm/embedded.html#Collection
and it says "Embedded elements cannot have inheritance (this may be allowed in the future)"
So, how to persist such objects?
I came accross the same issue a few hours ago, hope it helps others starting with jdo/datanucleus.
As stated in the current docs, the only way to persist a collection of interfaces is through an unidirectional join table. It's not possible to directly embed the objects implementing the interface.
#PersistenceCapable
public class SomeClass {
#Join
#Extension(vendorName="datanucleus", key="implementation-classes", value="ImplementingClass")
private List<SomeInterface> myList;
// this list would be embedded
private List<SomeOtherClass> myOtherList;
// ...
}
#PersistenceCapable
public interface SomeInterface {
// ...
}
#PersistenceCapable
public class ImplementingClass implements SomeInterface {
// ...
}
#PersistenceCapable(embeddedOnly="true")
public class SomeOtherClass {
// ...
}

#Id #GeneratedValue but set own ID value

I have a table with a generated id, but in some cases I would like to set it on my own. Can I, somehow, force Hibernate to ignore the #GeneratedValue?
It may be an overkill but have you thought about writing your own CustomIDGenerator which probably subclasses say the AutoGenerator of hibernate and exposes a couple of methods where you can set the id of the next class object to be generated so for example
class MyGenerator extends .... {
public void setIdForObject(Class clazz, Long id) {
//once you use this API, the next time an object of
//type clazz is saved the id is used
}
public void setIdForObject(Class clazz, Long id, Matcher matcher) {
//once you use this API, the next time an object of
//type clazz is saved and the matcher matches yes the id will be
//assigned. Your matcher can match properties like name, age etc
//to say the matched object
}
}
This could get complicated but at the least is possible as per hibernate doco
create your own identifiergenerator/sequencegenerator
public class FilterIdentifierGenerator extends IdentityGenerator implements IdentifierGenerator{
#Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
// TODO Auto-generated method stub
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
modify your entity as:
#Id
#GeneratedValue(generator="myGenerator")
#GenericGenerator(name="myGenerator", strategy="package.FilterIdentifierGenerator")
#Column(unique=true, nullable=false)
private int id;
...
and while saving instead of using persist() use merge() or update()
Although this question was asked quite a while ago, I found the perfect answer for it in this post by #lOranger, and wanted to share it.
This proposal checks if the object's current id is set to something other than null, and if so, it uses it, otherwise, it generates it using the default (or configured) generation strategy.
It's simple, straight forward, and addresses the issue brought up by #Jens, of one not being able to retrieve the object's current id.
I just implemented it (by extending the UUIDGenerator), and it works like a charm :-D
For you use case, you can manually add this no user.
One way to do it is to put the insert operation on a file named "./import.sql" (in your classpath).
Hibernate will go execute these statements when the SessionFactory is started.

Categories

Resources