Why is this field not being serialized? - java

My Issue entity was created from a DB table that has several fields (id, etc...). Each issue has as a field a list of Articles, which are stored in a separate DB table. Articles have a int issueID field, which is used to map them to the appropriate Issue (there is no corresponding column in the issues table): Ultimately, when an Issue object is constructed, I'm going to have it pull all of the articles whose issueID matches its ID, so that I can return a single serialized object that contains the issue data as well as a JSONArray representing its list of articles.
At this point, though, I'm just doing some testing - creating a few dummy Article objects and adding them to the articles collection. The problem is that, when I test GET requests on the Issue object, the JSONObject returned includes only the fields stored in the database (id, etc...) - no sign of the Article collection. Why is that?
I'm equally interested to know what other code you would need to see to answer this question: I've just begun teaching myself how to write web services and am still in the phase of wrapping my head around the broad concepts, so figuring out which of the moving parts has affects which behaviors - and which annotations are needed where - is ultimately what I'm trying to do.
That being the case, broader-based advice is welcomed.
#Entity
#Table(name = "issues")
#XmlRootElement
public class Issue implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Integer id;
....//other fields
#OneToMany(mappedBy = "issueID")
private Collection<Articles> articlesCollection;
public Issue() {
articlesCollection = new ArrayList<Articles>();
Articles a = new Articles();
a.setHeadline("butt cheese");
articlesCollection.add(a);
Articles b = new Articles();
articlesCollection.add(b);
Articles c = new Articles();
articlesCollection.add(c);
}

By default the relationship initialization is lazy so when the Issue object is loaded the articlesCollection is not fetched unless used.
In your case seems its the same situation.
Explore OpenEntityManagerInViewFilter if you do not intend to explicitly load articlesCollection. When your object serializes the articlesCollection will be loaded if you have configured OpenEntityManagerInViewFilter.

Does your Articles Class also has #XmlType or #XMLRootElement Tag?
Onany generic class like List<T> jaxb expects that T is annotated with #XMLType or #XMLRootElelemt

Related

How does JPA cache refreshes entities externally modified?

I faced an issue earlier with JPA.
I have two apps : the main one, using Java/JPA (EclipseLink), and a second one, using PHP. The two apps have access to the same database.
Now, I'm accessing an "Expedition" object through Java, then calling the PHP app through a web-service (which is supposed to modify an attribute of this object in the shared database table "Expedition"), then accessing this attribute through the Java app.
Problem is, the object seems not to be modified in the Java app, even if it is modified in the database. I'm thinking about a cache problem.
The original code (simplified) :
System.out.println(expedition.getInfosexpedition()); // null
// Calling the web-service (modification of the "expedition" object in the database)
this.ec.eXtractor(expedition);
System.out.println(expedition.getInfosexpedition()); // Still null, should not be
Definitions of the "Expedition" and "Infosexpedition" classes :
Expedition :
#Entity
#Table(name = "expedition")
#XmlRootElement
public class Expedition implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idExpedition")
private Integer idExpedition;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "idExpedition")
#XmlTransient
private Infosexpedition infosexpedition;
Infosexpedition :
#Entity
#Table(name = "infosexpedition")
#XmlRootElement
public class Infosexpedition implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idInfoExpedition")
private Integer idInfoExpedition;
#JoinColumn(name = "idExpedition", referencedColumnName = "idExpedition")
#OneToOne(optional = false)
#XmlTransient
private Expedition idExpedition;
I've been able to make the original code work by doing this :
System.out.println(expedition.getInfosexpedition()); // null
// Calling the web-service (modification of the "expedition" object in the database)
this.ec.eXtractor(expedition);
try
{
// Getting explicitly the "infosExpedition" item through a simple named request
Infosexpedition infos = this.ec.getFacade().getEm().createNamedQuery("Infosexpedition.findByIdExpedition", Infosexpedition.class)
.setParameter("idExpedition", expedition)
.setHint("eclipselink.refresh", "true")
.setHint("eclipselink.cache-usage", "DoNotCheckCache")
.setHint("eclipselink.read-only", "true") // This line did the trick
.getSingleResult();
expedition.setInfosexpedition(infos);
}
catch (NoResultException nre) {}
System.out.println(expedition.getInfosexpedition()); // Not null anymore, OK
I'm trying to understand what happens here, and why did I had to specify a "read-only" hint to make this work... Before that, I tried almost everything, from evictAll() calls to detach()/merge() calls, and nothing worked.
Can someone help me to understand how the different levels of cache worked here ? And why is my newly created line "read-only" ?
Thanks a lot.
The settings you are using are attempting to bypass the cache. ("eclipselink.read-only", "true") causes it to bypass the first level cache, while the ("eclipselink.cache-usage", "DoNotCheckCache") makes the query go to the database instead of pulling data from the second level cache. Finally ("eclipselink.refresh", "true") refreshes the data in the shared cache rather then return the prebuilt object. Your facade must be using the same EntityManager for both requests even though you have made changes to the objects between the requests. As mentioned in the comments, an EntityManager is meant to be used as a transaction would, so that you are isolated from changes made during your transactions. If this doesn't work for you, you should clear or release the entityManager after the first call, so that the calls after the web-service modifications can be picked up.
If applications outside this one are going to be making data changes frequently, you might want to look at disabling the shared cache as described here:
https://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F
And also implement optimistic locking to prevent either application from overwriting the other with stale data as described here:
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Locking/Optimistic_Locking
What you call cache is the 1st level cache, id est the in memory projection of the database state at a time t.
This "cache" has the same lifecycle that the entity manager itself and generally won't be refreshed until you explicitely clear it (using myEntityManager.clear()) (you shouldn't) or force it to refreh a specific entity instance (using myEntityManager.refresh(myEntityInstance), this is the way you should go)
See Struggling to understand EntityManager proper use and Jpa entity lifecycle for a more detailed explanation

Improving JPA entity classes for existing DB schema

I am attempting to implement a Hibernate/JPA2 solution over an existing schema, which cannot be changed. Here is a minimal example of the existing schema:
CREATE TABLE REASON (
REASON_CODE CHAR(1),
REASON_DESCRIPTION CHAR(50))
CREATE TABLE HEADER (
REASON_CODE CHAR(1),
OTHERFIELD1 CHAR(40),
OTHERFIELD2 CHAR(40) )
Normally this would be the "correct" way from a DB perspective: Link REASON to HEADER by the REASON_CODE. However it's presenting me with an awkward problem in Java and I'm not sure of the best way to solve it. I've modeled these entities as follows:
#Entity
#Table(name="REASON")
public class Reason implements java.io.Serializable {
#Id
#Column(name="REASON_CODE", unique=true, nullable=false, length=1)
private Character reasonCode;
#Column(name="REASON_DESCRIPTION", nullable=false, length=25)
private String reasonDescription;
}
#Entity
#Table(name="HEADER")
public class Header implements java.io.Serializable {
#ManyToOne
#JoinColumn(name = "REASON_CODE", nullable = false)
private Reason reason;
#Column(name="OTHERFIELD1")
private String otherField1;
#Column(name="OTHERFIELD2")
private String otherField2;
}
Once again, as far as I can tell, this is "correct" from a Java perspective - linking Header to Reason with a reference.
The problem is that when I need to use one of these Reason values in my code I wind up with awkward syntax like:
Reason r = reasonService.findOne('X'); // X is the REASON_CODE in the database record
// Do some processing with variable r
Or this:
header.setReason(reasonService.findOne('X'));
Ideally I could implement Reason as an enum like:
public enum Reason {
X_MARKSTHESPOT("X"),
C_MEANSSOMETHINGELSE("C"),
F_MEANSATHIRDTHING("F") ;
private String code;
private Reason(String code) {
this.code = code;
}
}
And then simply have this in my code:
header.setReason(Reason.X_MARKSTHESPOT);
But from what I understand that is not possible with JPA, which offers only EnumType.STRING (basically the name) or EnumType.ORDINAL (even worse, the index in the enum list). A possible way around this would be JPA 2.1's Converter, but I have never used it. I have also read here (in one of the answers) that a Hibernate User Type might be useful. One of our programmers has solved this in another app by writing two complete classes - an enum class for internal use and a "shadow" class which iterates through the enum and syncs the records in the database on every startup. But this seems like a kludgey way to do it. What is the best way to handle this, bearing in mind that the database schema cannot be changed?

Persisting #ElementCollection of #Embeddable (Google App Engine, datanucleus)

I am trying to persist a JPA entity with a collection of custom #Embeddable objects using the JPA2 #ElementCollection annotation. Simple example (both classes are enhanced by datanucleus):
#Entity
public class TestEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ElementCollection
private Set<TestEmbeddable> testEmbeddables;
public Set<TestEmbeddable> testEmbeddables() {
return this.testEmbeddables;
}
}
#Embeddable
public class TestEmbeddable implements Serializable {
public String s;
}
The persisted Datastore entity, however, will only contain a collection of null values instead of the actual objects:
TestEntity.testEmbeddables = [null, null, ...]
Persisting a collection of basic types such as Strings or embedding a single TestEmbeddable object using #Embedded works perfectly fine. Would someone be able to clarify whether element collections of embeddables are supported by datanucleus-appengine?
While the datanucleus section on JPA element collections only gives an example for a String collection, the corresponding JDO section uses a custom embedded-only type. The feature list further states that embedded collections in general are compatible with GAE, but does not say whether custom types are supported. I also found one other person claiming that this should work.
-- Edit --
Following DataNucleus' answer, I ran some more tests:
#ElementCollection
private List<String> stringsElementCollection;
--> Works. The individual Strings are persisted as TestEntity.stringsElementCollection = [str1, str2, ...]
#Embedded
private List<String> stringsEmbedded;
--> Same as #ElementCollection. I wonder if the JPA specification covers the use of #Embedded on a collection, though?
#ElementCollection
private List<TestEmbeddable> embeddablesElementCollection;
--> Doesn't work. Instead of the actual TestEmbeddable objects, the Datastore persists only a collection of null values: TestEntity.embeddablesElementCollection = [null, null, ...]
#Embedded
private List<TestEmbeddable> embeddablesEmbedded;
--> This seems to work. The TestEmbeddable.s field is stored as TestEntity.s.0, .s.1, etc. plus a TestEntity.embeddablesEmbedded.size property.
(App Engine SDK 1.7.7.1, datanucleus 3.1.3, datanucleus-appengine 2.1.2)
No idea if Google support embedded collections of such objects yet with JPA. That was only developed for JDO and works there. You could try putting a #Embedded on the embedded field to reinforce the idea that its embedded.
The issue has been recorded by David Geiger here:
https://code.google.com/p/datanucleus-appengine/issues/detail?id=318

Bulk Insert via Spring/Hibernate where ids are needed

I have to do bulk inserts, and need the ids of what's being added. This is a basic example that shows what I am doing (which is obviously horrible for performance). I am looking for a much better way to do this.
public void omgThisIsSlow(final Set<ObjectOne> objOneSet,
final Set<ObjectTwo> objTwoSet) {
for (final ObjectOne objOne : objOneSet) {
persist(objOne);
for (final ObjThree objThree : objOne.getObjThreeSet()) {
objThree.setObjOne(objOne);
persist(objThree);
}
for (final ObjectTwo objTwo : objTwoSet) {
final ObjectTwo objTwoCopy = new ObjTwo();
objTwoCopy.setFoo(objTwo.getFoo());
objTwoCopy.setBar(objTwo.getBar());
persist(objTwoCopy);
final ObjectFour objFour = new ObjectFour();
objFour.setObjOne(objOne);
objFour.setObjTwo(objTwoCopy);
persist(objFour);
}
}
}
In the case above persist is a method which internally calls
sessionFactory.getCurrentSession().saveOrUpdate();
Is there any optimized way of getting back the ids and bulk inserting based upon that?
Thanks!
Update: Got it working with the following additions and help from JustinKSU
import javax.persistence.*;
#Entity
public class ObjectFour{
#ManyToOne(cascade = CascadeType.ALL)
private ObjectOne objOne;
#ManyToOne(cascade = CascadeType.ALL)
private ObjectTwo objTwo;
}
// And similar for other classes and their objects that need to be persisted
If you define the relationships using annotations and define appropriate cascading, you should be able set the object relationships in the objects in java and persist it all in one call. Hibernate will handle setting the foreign keys for you.
Documentation -
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-mapping-association
An example annotation on a parent object would be
#OneToMany(mappedBy = "foo", fetch = FetchType.LAZY, cascade=CascadeType.ALL)
On the child object you would do the following
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COLUMN_NAME", nullable = false)
I'm not sure but Hibernate makes bulk inserts/updates. The problem I understand is you need to persist the parent object in order to assign the reference to the child object.
I would try to persist all the "one" objects. And then, iterate over all their "three" objects and persist them in a second bulk insertion.
If your tree has three levels you can achieve all the insertions in 3 batchs. Pretty decent I think.
Assuming that you are just looking at getting a large amount of data persisted in one go and your problem is that you don't know what the IDs are going to be as the various related objects are persisted, one possible solution for this is to run all your inserts (as bulk inserts) into ancillary tables (one per real table) with temporary IDs (and some session ID) and have a stored procedure perform the inserts into the real tables whilst resolving the IDs.

What is appropriate way of creating objects with One-to-Many relationship using Objectify and RequestFactory?

What is appropriate way of creating objects with One-to-Many relationship using Objectify and RequestFactory? I've read documentation for these libraries, and also reviewed number of sample projects such as listwidget and gwtgae2011. All of them use #Embedded annotation which is not what I want because it stores everything within one entity. Another option according to documentation would be to use #Parent property in child classes. In my example (getters/setters removed for simplicity) I have entities Person and Organization which defined as
#Entity
public class Person extends DatastoreObject
{
private String name;
private String phoneNumber;
private String email;
#Parent private Key<Organization> organizationKey;
}
and
#Entity
public class Organization extends DatastoreObject
{
private String name;
private List<Person> contactPeople;
private String address;
}
Now if I understood documentation correctly in order to persist Organization with one Person I have to persist Organization first, then set organizationKey to ObjectifyService.factory().getKey(organization) for Person object and then persist it. I already don't like that I have to iterate through every child object manually but using RequestFactory makes everything is more convoluted due to presence of proxy classes. How would I define Organization and OrganizationProxy classes - with Key<> or without it ? Will I have to define something like this in Organization ?
public void setContactPeople(List<Person> contactPeople)
{
for (int i = 0; i < contactPeople.size(); ++i)
{
DAOBase dao = new DAOBase();
Key<Organization> key = dao.ofy().put(this);
contactPeople.get(i).setOrganizationKey(key);
}
this.contactPeople = contactPeople;
}
And how would I load Organization with its children from Datastore ? Will I have to manually fetch every Person and fill out Organization.contactPeople in #PostLoad method ?
It seems like I'll have to write A LOT of maintenance code just to do what JPA/JDO does behind the scene. I simply don't get it :(
Am I missing something or it's the only way to implement it ?
Thanks a lot for answers in advance!!!
You need to make it as #Parent only when you going to use it in transaction against all Person in this Organization. I'm sure it's not what you want.
It's enough to save just private Key<Organization> organizationKey, and filter by this field when you need to find Person for specified Organization
As about loading all referenced objects - yes, it is, you have to load it manually. It's pita, but it's not a lot of code.
Also, there is a different way to store this relationship, if your organization are small enough, and consists of few hundreds of people. At this case you can have List<Key<Person>> contactPeopleKey;, and load all this people by existing Key, manually, it much be much faster than loading by new Query

Categories