Skip to content

Entity persister

stevenzwu edited this page Feb 21, 2013 · 28 revisions

Astyanax provides entity persister APIs that is similar to Java Persistence API (JPA). Please check out "com.netflix.astyanax.entitystore" package.

Quick notes

  • we uses annotations from javax.persistence package
  • entity class must be annotated with @Entity
  • field with @Id annotation will be written as rowKey in cassandra data model. If the same field can also have @Column annotation, then it will also be written as a column redundantly.
  • column name cannot contain dot (.) char, which is reserved separator for nested structure
  • column name will be normalized to lower cases when writing to cassandra. In another word, column name is case insensitive.

Basic put/get/delete operations

Here is sample code snippet from DefaultEntityManagerTest.java

final String id = "foo";
final SampleEntity origEntity = createRandomEntity(id);
final EntityManager<SampleEntity, String> entityManager = 
	new DefaultEntityManager.Builder<SampleEntity, String>()
	.withEntityType(SampleEntity.class)
	.withKeyspace(keyspace)
	.withColumnFamily(CF_SAMPLE_ENTITY)
	.build();

entityManager.put(origEntity);
SampleEntity getEntity = entityManager.get(id);
entityManager.delete(id);

TTL

Here are the ways to specify TTL (lower priority first)

  • @TTL annotation at entity class level
@Entity
@TTL(3600)  // 1 hour
public class FooEntity {
    // your class definition
}
  • DefaultEntityManager Builder EntityManager<FooEntity, String> entityPersister = new DefaultEntityManager.Builder<FooEntity, String>() .withEntityType(FooEntity.class) .withKeyspace(keyspace) .withColumnFamily(columnFamily) .withTTL(3600) // 1 hour .build();
@Entity
@TTL(3600)  // 1 hour
public class FooEntity {
    // your class definition
}
  • @TTL annotation at entity method level. this the dynamic and most flexible way of deriving TTL value. e.g. you may have entities that are time dependent (e.g. flight schedules, subscriptions to services, etc.) where the TTL basically has to be bigger than the "end date" of that particular entity (which will vary per row/entity).
@Entity
public class FooEntity {
    // it must be zero args and return Integer type
    @TTL
    public Integer getTTL() {
        // your TTL calculation logic
    }
}

Nested entity

  • We support nested structure. column name will be concatenated by "." char. That's why dot char is invalid in @Column name annotation.
  • Nested type must also be annotated with @Entity just like the root type. But the difference is that nested type doesn't need @Id field/annotation.
  • Nesting can be any arbitrary level deep.

Here is the code snippet from SampleEntity.java. When SampleEntity is written to cassandra. There will be two columns of "bar.i" and "bar.s".

@Entity
public class SampleEntity {
	@Entity
	public static class Bar {		
		@Column(name="i")
		public int i;
		@Column(name="s")
		public String s;
		...
	}
	...
	@Column(name="BAR")
	private Bar bar;
	...
}

Explicit @Serializer annotation

By default (without @Serializer annotation), astyanax will automatically infer the Serializer type based on entity field's java type. Here are the following inference types supported by entity persister.

  • basic java types (both primitive and wrapper): byte, short, int, long, boolean, float, double
  • String, byte[], Date, UUID

If you have a field that requires custom Serializer, you can specify it with the @Serializer annotation. Here is the code snippet from SampleEntity.java

public class SampleEntity {
	
	public static class Foo {
		
		public int i;
		public String s;
		
		public Foo(int i, String s) {
			this.i = i;
			this.s = s;
		}
		
		@Override
		public String toString() {		
			try {
				JSONObject jsonObj = new JSONObject();
				jsonObj.put("i", i);
				jsonObj.put("s", s);
				return jsonObj.toString();
			} catch (JSONException e) {
				throw new RuntimeException("failed to construct JSONObject for toString", e);
			}
		}
		
		public static Foo fromString(String str) {
			try {
				JSONObject jsonObj = new JSONObject(str);
				return new Foo(jsonObj.getInt("i"), jsonObj.getString("s"));
			} catch (JSONException e) {
				throw new RuntimeException("failed to construct JSONObject for toString", e);
			}			
		}

		...
	}
	
	public static class FooSerializer extends AbstractSerializer<Foo> {
		
		private static final String UTF_8 = "UTF-8";
		private static final Charset charset = Charset.forName(UTF_8);
		private static final FooSerializer instance = new FooSerializer();

		public static FooSerializer get() {
			return instance;
		}

		@Override
		public ByteBuffer toByteBuffer(Foo obj) {
			if (obj == null) {
				return null;
			}
			return ByteBuffer.wrap(obj.toString().getBytes(charset));
		}

		@Override
		public Foo fromByteBuffer(ByteBuffer byteBuffer) {
			if (byteBuffer == null) {
				return null;
			}
			return Foo.fromString(charset.decode(byteBuffer).toString());
		}

		...
	}

	@Column(name="FOO")
	@Serializer(FooSerializer.class)
	private Foo foo;
	...
}
Clone this wiki locally