Fetching data from jdo datastore - java

I have created two classes on client-side and they are stored using GWT JDO.
The parent class looks like:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Park implements Serializable{
#PrimaryKey
#Persistent
private String parkId;
//...
#Persistent(mappedBy = "park", defaultFetchGroup = "true")
private List<Facility> facilityList;
// other stuff
and the child looks like:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Facility implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String encodedKey;
#Persistent
private Park park;
// other stuff
And on server side, I have a method to fetch everything:
public Park[] getParks(){
PersistentManager pm = getPersistentManager();
ArrayList<Park> parkList = new ArrayList<Park>();
try {
Query q = pm.newQuery(Park.class);
List<Park> parks = (List<Park>) q.execute();
for(Park p:parks)
parkList.add(p);
} finally {
pm.close();
}
return parkList.toArray(new Park[parkList.size()]);
}
When I call this method, it throws an exception:
com.google.gwt.user.client.rpc.SerializationException: Type 'org.datanucleus.store.types.sco.backed.ArrayList' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.
I can't figure out whats wrong. Any suggestion is welcome.

I had this problem a while back and it was quite frustrating. And I'm also quite sure your problem isn't because you imported the wrong package. You probably already solved this, but if anyone else need to solve the problem.
Try the below example code:
public Park[] getParks(){
PersistentManager pm = getPersistentManager();
List<Park> parkList = null;
try {
Query q = pm.newQuery(Park.class);
parkList = (List<Park>) q.execute();
parkList = pm.detachCopyAll(parkList);
} finally {
pm.close();
}
return parkList.toArray(new Park[parkList.size()]);
}
In the example above I've changed ArrayList to just List (not sure if necessary) and use detachCopyAll to copy the list so it is not connected to the datastore any more, this is the magic that removed the error for me.
There's an annotation that can be used, i quote from the documentation:
You can modify an object after the PersistenceManager has been closed
by declaring the class as "detachable." To do this, add the detachable
attribute to the #PersistenceCapable annotation:
#PersistenceCapable(detachable="true")
Not sure though if this means that you can do, what you originally did, but it's worth a try because I don't think my solution is "pretty".

Change the ArrayList declaration to use Java's ArrayList:
java.util.ArrayList<Park> parkList = new java.util.ArrayList<Park>();
GWT cannot serialize objects that do not have a default constructor and the ArrayList you have imported (org.datanucleus.store.types.sco.backed.ArrayList) doesn't provide a default constructor, making serialization fail.
It is possible that you imported this class by mistake in which case you can just change the import declaration. But if you are using it somewhere else, then you will have to use the full qualifier as shown.

Related

Lombok's `#Builder` annotation stubbornly stays package - private

I have the following #Builder - annotated class:
#Data
#Builder(access = AccessLevel.PUBLIC)
#Entity
public class LiteProduct
{
// Minimal information required by our application.
#Id
private Long id; // Unique
private String name;
private Long costInCents;
private String type;
// Model the product types as a Hash Set in case we end up with several
// and need fast retrieval.
public final static Set<String> PRODUCT_TYPES = new HashSet<>
(Arrays.asList("flower", "topical", "vaporizer", "edible", "pet"));
// Have to allow creation of products without args for Entities.
public LiteProduct()
{
}
public LiteProduct(#NonNull final Long id, #NonNull final String name,
#NonNull final String type, #NonNull final Long costInCents)
{
if(!PRODUCT_TYPES.contains(type))
{
throw new InvalidProductTypeException(type);
}
this.name = name;
this.id = id;
this.costInCents = costInCents;
}
Whenever I want to use the builder class that Lombok is purported to give me, despite the fact that the IDE seems to detect it just fine:
I get a compile-time error about its visibility:
I have looked at some workarounds such as this or this, and they all seem to imply that my problem ought to already be solved automatically and that Lombok by default produces public Builder classes. This does not seem to be implied from my output, and does not happen even after I put the parameter access=AccessLevel.PUBLIC in my #Builder annotation in LiteProduct. Any ideas? Is there something wrong with the fact that this class is also an #Entity? Something else I'm not detecting?
// Edit: I determined that when I move the class in the same package as the one I am calling the builder pattern from, it works just fine. This is not an #Entity issue, but a package visibility issue which based on what I'm reading should not be there.
The problem was that I was using the following line of code to create an instance of LiteProduct:
return new LiteProduct.builder().build();
instead of:
return LiteProduct.builder().build();
which is what the #Builder annotation allows you to do. Clearly builder() is like a factory method for Builders that already calls new for you.

how to cascade GroupSequenceProvider to member List?

Hi say I have sample bean called car and I want to redefine its validation sequence:
#GroupSequenceProvider(value = CarSequenceProvider.class)
public class Car{
#NotNull(groups = {groupOne.class})
private Boolean isGood;
#Valid
private List<Driver> drivers;
// getter/setter
}
this is my driver class
public class Driver{
#NotEmpty(groups = {groupTwo.class})
private List<String> skills;
//getter/setter
}
and here goes my sequence provider:
public class CarSequenceProvider implements DefaultGroupSequenceProvider<Car>{
#Override
public List<Class<?>> getValidationGroups(Car car) {
List<Class<?>> sequence = new ArrayList<Class<?>>();
sequence.add(Car.class);
sequence.add(groupOne.class);
if(car != null && car.IsGood()){
sequence.add(groupTwo.class);
}
}
Basically I only want All drivers skills not to be empty if the car is good. What is happening right now is that #notEmpty never gets called because my sequence redefinition is not cascaded to Driver List. Is it possible to make it cascade then?
Thank you so much
Here #ConvertGroup comes into play which allows you during cascaded
validation to use a different group than the originally requested one.
Is that annotation you tried
Source: example-group-conversion-dedault-to-driver-checks

Glassfish server cache not getting updated when using #XmlAdapter?

I've stumbled upon a strange problem using a #XmlAdapter. Let me try to sketch the situation:
At the server side I have a class Cows:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class Cows implements Serializable {
...a bunch of cow properties like ID, name, ...
#XmlElementWrapper(name = "sampless")
#XmlElement(name = "samples")
#OneToMany(mappedBy = "cowId")
private List<Samples> samplesList;
...a bunch of getters & setters...
}
I have a class Samples:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class Samples implements Serializable {
...a bunch of samples properties like ID, cellcount, ...
#JoinColumn(name = "cow_id", referencedColumnName = "id")
#ManyToOne
#XmlJavaTypeAdapter(CowsAdapter.class)
private Cows cowId;
}
CowsAdapter class:
public class CowsAdapter extends XmlAdapter<Cows, Cows> {
#Override
public Cows unmarshal(Cows v) throws Exception {
return v;
}
#Override
public Cows marshal(Cows v) throws Exception {
return null;
}
}
I include a list of samples when a Cow object is requested by the client as shown above.
Now, at client side, when I try to add a new Sample for a Cow, it marshals correctcly into a xml string (including the Cow object) which is send to the webservice. A new entry is correctly added to the database.
However, when I request the same Cow again (with the samplesList included), the new Sample is not added althou it is in the database? When I restart the glassfish instance, it shows.. Why is this and should I use another approach to avoid this?
Edit: same behaviour happens when deleting samples. It gets deleted from the database but is still included in the response of a Cow request. This has something to do with the cache of glassfish?
Edit2: I found some kind of solution for this. I changed the findById method as follows:
#GET
#Path("cow/{id}")
#Produces({"application/xml"})
public Cows findById(#PathParam("id") Integer id) {
//Cows cow = super.find(id);
final Query qry = getEntityManager().createNamedQuery("Cows.findById", Cows.class);
qry.setParameter("id", id);
qry.setHint("javax.persistence.cache.storeMode", "REFRESH");
Cows cow = (Cows) qry.getSingleResult();
return cow;
}
However, could someone let me know if this is the best approach?
Thanks in advance.
Disabled the Glassfish cache for now by adding a property to persistence.xml
You need to maintain both sides of a relationship. If you create a new Sample you need to add it to its Cow's samples.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side

My LinkedList is not saved as it should be (regarding its elements order)

First of all, pardon me for my poor english level. I will try to be as understandable as I can.
I am trying to re-order a playlist of music files. A Playlist is basically a LinkedList<MusicFiles> with a name.
I change the position of an element, it seems to be as it should, cool. But when I save it in the database the order doesn't change! I am doing something wrong, that's a fact, but after hours spent debugging, my mind could really use a debugger for itself...
Here is my jsf code (inside a p:datatable):
<p:commandButton title="Move Down"
ajax="false"
image="down"
action="#{playlistMBean.increasePosition(musicFile.URL)}"
onclick="show_my_playlists?face-redirect=true"/>
The backing bean code:
#Named(value = "playlistMBean")
#SessionScoped
public class PlaylistMBean implements Serializable {
#EJB
private PlaylistManager playlistManager;
private Playlist currentPlaylist;
//...
public void increasePosition(String musicURL) {
currentPlaylist.increasePosition(musicURL);
playlistManager.save(currentPlaylist);
}
//...
}
"currentPlaylist" is obviously a Playlist, so here's the code of the method in the entity bean "Playlist":
#Entity
#NamedQueries(/*...*/)
public class Playlist implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToMany(cascade= {CascadeType.REFRESH, CascadeType.MERGE},fetch= FetchType.EAGER)
#OrderBy(/* ??????????? */
private LinkedList<MusicFile> musicList;
private String name;
//...
public void increasePosition(String url) {
if (url != null) {
MusicFile mf = getMusicFileByURL(url);
int position = getPosition(url);
if (position < musicList.size() - 1) {
musicList.remove(position);
musicList.add(position + 1, mf);
}
}
}
And finally the code of the playlist manager which should save the reordered playlist in the database:
#Stateless
#LocalBean
public class PlaylistManager {
#PersistenceContext(unitName = "Doozer-ejbPU")
private EntityManager em;
public void save(Playlist playlist) {
Playlist p = em.find(Playlist.class, playlist.getId());
if (p != null) {
em.merge(playlist);
}
else {
em.persist(playlist);
}
}
//...
}
The given playlist in this last step is the good one (reordered). So my guess is that my problem is with the entity manager (I'm such a genius, I know...).
Does anyone know why?
May it comes from the cascade type? Due to my lack of knowledge about it I'm not sure which one should I put. I have tried CascadeType.ALL but then it raises exception when adding music files. CascadeType.DETACH was my first choice since I dont't want to delete the musics when deleting a playlist... But here again I really don't know for sure if I know what I'm talking about :(
[Edit]: Thanks to Piotr Nowicki, my question has changed quite a lot: how can I define the #OrderBy annotation in order to sort the LinkedList according to its inner order? Is it even possible? The easy/ugly method would be to add a property position to the MusicFile entity but I'd rather not.
If you want to preserve the order in which elements in List are stored in the database (and further retrieved from it) you should use #OrderBy or #OrderColumn annotations.
In your case, if you just want the List to be returned in order without any advanced conditions, the #OrderColumn should be sufficient:
#OneToMany(...)
#OrderColumn
private LinkedList<MusicFile> musicList;

JPA: persisting object, parent is ok but child not updated

I have my domain object, Client, I've got a form on my JSP that is pre-populated with its data, I can take in amended values, and persist the object.
Client has an abstract entity called MarketResearch, which is then extended by one of three more concrete sub-classes.
I have a form to pre-populate some MarketResearch data, but when I make changes and try to persist the Client, it doesn't get saved, can someone give me some pointers on where I've gone wrong?
My 3 domain classes are as follows (removed accessors etc)
public class Client extends NamedEntity
{
#OneToOne
#JoinColumn(name = "MARKET_RESEARCH_ID")
private MarketResearch marketResearch;
...
}
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class MarketResearch extends AbstractEntity
{
...
}
#Entity(name="MARKETRESEARCHLG")
public class MarketResearchLocalGovernment extends MarketResearch
{
#Column(name = "CURRENT_HR_SYSTEM")
private String currentHRSystem;
...
}
This is how I'm persisting
public void persistClient(Client client)
{
if (client.getId() != null)
{
getJpaTemplate().merge(client);
getJpaTemplate().flush();
} else
{
getJpaTemplate().persist(client);
}
}
To summarize, if I change something on the parent object, it persists, but if I change something on the child object it doesn't. Have I missed something blatantly obvious?
I've put a breakpoint right before the persist/merge calls, I can see the updated value on the object, but it doesn't seem to save. I've checked at database level as well, no luck
Thanks
You need to set a proper cascade option on #OneToOne in order to get your operations cascaded:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "MARKET_RESEARCH_ID")
private MarketResearch marketResearch;

Categories

Resources