TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and Google Cloud Platform Instructor now residing in Colorado. Besides tech, I enjoy landscape photography and share my work at ColoradoPhoto.gallery.

  • Subscribe

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 224 other subscribers
  • Sleepless Nights…

    March 2023
    S M T W T F S
     1234
    567891011
    12131415161718
    19202122232425
    262728293031  
  • Blog Stats

    • 1,039,382 hits

Archive for the ‘Java Data Objects’ Category

Persisting enums with AppEngine Datastore

Posted by David Chandler on December 4, 2009

Much of Java’s appeal (not to mention JSF, ORM, and GWT with a little help) is the ability to code in terms of rich domain objects such as the enumerated type. My next several blog posts will demonstrate the use of enums throughout a GAE+GWT application, beginning with persistence.

According to the AppEngine docs,  Java enums are not one of the core value types, which might lead one to believe that they’re not supported. However, closer examination reveals that enums are indeed supported by DataNucleus JDO and JPA, which take care of mapping enums to Datastore core value types String (JDO + JPA) or Integer (JPA).

JDO

JDO mapping requires no special annotations and persists the enum as a String by name. One of the DataNucleus doc pages (search for “ordinal”) suggests it may be possible to persist by ordinal instead; however, I haven’t found this documented for JDO. Persistence by name is likely better, anyway, as it allows you the flexibility to add new enum values later in any ordinal position.

	public enum ScheduleMethod {RANDOM, FILL, CUSTOM};
	@Persistent
	private ScheduleMethod scheduleMethod = ScheduleMethod.RANDOM;

Note that Google does have a unit test for enum persistence in JDO.

JPA

JPA mapping requires the use of the @Enumerated annotation, which additionally lets you specify whether to map by name (String) or ordinal (Integer).

Roll-your-own

Of course, you can always roll your own mapping strategy if for some reason you need special behavior. With AppEngine Datastore, you can persist any object class by making it Serializable or Embedded, although the former has the disadvantage that you can’t use object properties in queries. You can also create a custom enum class and persist the enum by any core value type as in the example below.

	public static enum Frequency
	{
		DAILY(1),
		WEEKLY(7),
		MONTHLY(30),
		QUARTERLY(90),
		YEARLY(365);
		
		private int periodDays;

		Frequency(int periodDays)
		{
			this.periodDays = periodDays;
		}

		public int getPeriodDays()
		{
			return periodDays;
		}
		
		public static Frequency getValue(int i)
		{
			for (Frequency f : Frequency.values())
			{
				if (f.getPeriodDays() == i)
					return f;
			}
			throw new IllegalArgumentException("Invalid frequency period days: " + i);
		}
	}

	@Persistent
	private Integer periodDays = Frequency.WEEKLY.getPeriodDays();
	
	/**
	 * Convenience method to get frequency as an enum
	 * @return Frequency
	 */
	public Frequency getFrequency()
	{
		return Frequency.getValue(this.getPeriodDays());
	}
	
	public void setFrequency(Frequency f)
	{
		this.setPeriodDays(f.getPeriodDays());
	}
Advertisement

Posted in AppEngine, Java Data Objects | 1 Comment »

Correction: AppEngine Datastore and owned relationships

Posted by David Chandler on November 12, 2009

Last week, I mistakenly wrote that AppEngine Datastore would not retrieve Key fields (representing an unowned relationship) unless you annotated with defaultFetchGroup=”true”, but that it would automatically retrieve properties represented by an owned relationship. This was not correct, as AppEngine Datastore never allows joins. I’ve updated the original post accordingly.

 

Posted in AppEngine, Java Data Objects | Leave a Comment »

Key fields are not retrieved by default

Posted by David Chandler on November 6, 2009

I’ve noticed that AppEngine does not populate Key fields by default when querying. I’m sure it’s documented, but I can’t remember where. At any rate, you can annotate with defaultFetchGroup to resolve this. Same goes for properties of type Text and Blob.

...
import com.google.appengine.api.datastore.Key;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class PrayerItem implements Serializable
{
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;
	@Persistent(defaultFetchGroup="true")
	private Key list;
	...

}

In the case of owned relationships, where a property is represented by its actual domain class rather than a Key, it is also necessary to use defaultFetchGroup=”true”. Even though owned properties are in the same entity group and are therefore eligible for inclusion in a transaction, the AppEngine Datastore doesn’t allow joins, so owned relationships are not returned in the initial query. When you set defaultFetchGroup=”true” on an owned property, you will get this warning:

WARNING: Meta-data warning for com…your_owned_property_name: The datastore does not support joins and therefore cannot honor requests to place child objects in the default fetch group.  The field will be fetched lazily on first access.  You can modify this warning by setting the datanucleus.appengine.ignorableMetaDataBehavior property in your config.  A value of NONE will silence the warning.  A value of ERROR will turn the warning into an exception.

Lazy fetching does work as described in the message.

Posted in AppEngine, Java Data Objects | 9 Comments »

AppEngine JDO example #1: retrieve an object by ID

Posted by David Chandler on October 30, 2009

This is the first of a series of posts that will demonstrate a variety of JDO mappings and queries with AppEngine.

For starters, let’s retrieve an object from the Datastore given its ID. Here’s the User object in our domain model:

package com.roa.client.domain;

import java.io.Serializable;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class User implements Serializable
{
	private static final long serialVersionUID = -1126191336687818754L;

	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;
	@Persistent
	private String firstName;
	...
}

Now we want to retrieve a User object given the ID property obtained from getId(). Easy enough, you think, as the JDO PersistenceManager provides a method for this:

// Doesn't quite work
User attachedUser = (User) getPM().getObjectById(userId);

But there’s a catch. Confusingly, the ID needed by getObjectById is a JDO ID, not the object property named “id” that we’ve annotated as an IDENTITY. You can get the JDO ID one of two ways. Whenever the object is attached, such as just after you’ve called makePersistent() on it, you can call persistenceManager.getObjectId(obj) and keep it in a safe place for later use. Alternatively, you can use a JDO helper method at any time to get the JDO ID from the object ID:

		 // Must obtain the JDO ID first
		 Object idInstance = getPM().newObjectIdInstance(User.class, user.getId());
		 User attachedUser = (User) getPM().getObjectById(idInstance);

Fortunately, if you know the object’s class, there’s a 2-arg getObjectById that does take the object’s primary key property:

User attachedUser = getPM().getObjectById(User.class, u.getId());

Of course, you can always just run a normal query on the object’s ID property, but it’s a bit ugly because the simplest query syntax always returns a Collection and we assume here that there will be only one matching object.

		Query q = getPM().newQuery(User.class, "id == :userId");
		List<User> users = (List<User>) q.execute(u.getId());
		User attachedUser = users.get(0);

Better to let the query language know that we expect a unique object:

		Query q = getPM().newQuery(User.class, "id == :userId");
		q.setUnique(true);
		User attachedUser = (User) q.execute(u.getId());

Finallly, there’s the low-level Datastore API:

		Key key = KeyFactory.createKey(User.class.getSimpleName(), u.getId());
		Entity entity = DatastoreServiceFactory.getDatastoreService().get(key);

Posted in AppEngine, Java Data Objects | 5 Comments »

 
%d bloggers like this: