Skip to content

Quick Reference

DuyHai DOAN edited this page Aug 31, 2013 · 28 revisions

Let's consider the following entity for all the below examples:

@Entity
public class User
{
	@Id
	private Long userId;

	@Column
	private String firstname;

	@Column
	private String lastname;

	@Column
	private Counter tweetCount;

	public User(){}

	public User(Long userId, String firstname, String lastname){...}

	public User(Long userId, String firstname, String lastname, Counter tweetCount){...}
}

## Persisting a transient entity
	em.persist(new User(10L,"John","DOE"));

or

	em.merge(new User(10L,"John","DOE"));

Warning! Theorically nothing prevents you from persisting twice different users with the same primary key. In this case, all data from previous user will be wiped out and data from new user inserted.

Achilles persist() doest not work like an upsert.

The CQL implementation achieves it by a single INSERT statement, null values from the entity will be translated into column removal by the Cassandra engine. The Thrift implementation removes first the whole row before inserting new data, which is less performant.


## Updating an entity ### Updating a managed entity
	User user = em.find(User.class,10L);
	user.setFirstname("Jonathan");
	em.merge(user);

The drawback of the above update is the need to load the entity before updating it (the dreadful read-before-write pattern). However it has the benefit to guarantee you that the entity you are updating does really exist.

To avoid reading before update, use Direct Update as described below

Direct update

	User user = em.getReference(User.class,10L);
	user.setFirstname("Jonathan");
	em.merge(user);

Unlike find(), getReference() does not hit the database. Achilles simply instanciates a new User object, sets the primary key and returns a proxified version of the entity to you. Upon call to merge(), Achilles updates the firstname.

If you are sure that you entity does exist in Cassandra, prefer getReference() to find()


## Removing an entity ### Removing a managed entity ```java User user = em.find(User.class,10L); em.remove(user); ``` ### Direct removal ```java em.removeById(User.class,10L); ```
## Finding clustered entities

For all examples in this section, let's consider the following clustered entity representing a tweet line

public class TweetLine
{
	@EmbeddedId
	private TweetKey id;

	@Column
	private String content;

	public static class TweetKey
	{
		@Order(1)
		private Long userId;

		@Order(2)
		LineType type;

		@Order(3)
		UUID tweetId;
	}

	public static enum LineType
	{ USERLINE, TIMELINE, FAVORITELINE, MENTIONLINE}
}

Find by partition key and clustering keys

Get the last 10 tweets from timeline, starting from tweet with lastUUID

List<TweetLine> tweets = em.sliceQuery(TweetLine.class)
			.partitionKey(10L)
			.fromClusterings(LineType.TIMELINE,lastUUID)
			.toClusterings(LineType.TIMELINE)
			.orderind(OrderingMode.DESCENDING)
			.limit(10)
			.get();

Find by embedded ids

Same as above but using TweetKey instead of clustering components

List<TweetLine> tweets = em.sliceQuery(TweetLine.class)
			.fromEmbededId(new TweetKey(10L,LineType.TIMELINE,lastUUID))
			.toClusterings(new TweetKey(10L,LineType.TIMELINE))
			.orderind(OrderingMode.DESCENDING)
			.limit(10)
			.get();

Iterating through a large set of entities

Fetch all timeline tweets by batch of 100 tweets

Iterator<TweetLine> iterator = em.sliceQuery(TweetLine.class)
			.partitionKey(10L)
			.fromClusterings(LineType.TIMELINE,lastUUID)
			.toClusterings(LineType.TIMELINE)
			.orderind(OrderingMode.DESCENDING)
			.iterator(100);

while(iterator.hasNext())
{
	TweetLine timelineTweet = iterator.next();
	...
}		

Removing clustered entities

Removing all timeline tweets

	em.sliceQuery(TweetLine.class)
		.partitionKey(10L)
		.fromClusterings(LineType.TIMELINE)
		.toClusterings(LineType.TIMELINE)
		.remove();

Right now, due some technical limitation on the protocol side (internally it's feasible) it is not possible to perform range deletion. This limitation may be lifted in a near future when the protocol (native or Thrift) will support range deletion.


## Querying Cassandra ### Native query ```java List> rows = em.nativeQuery("SELECT firstname,lastname FROM user LIMIT 100");
for(Map<String,Object> row : rows)
{
	String firstname = row.get("firstname");
	String lastname = row.get("lastname");
	...
}

 Please note that the returned Map structure is indeed a `LinkedHashMap` which preserves the insertion order, which is the order of the columns declared in the query string (here _firstname_ then _lastname_ )

### Typed query
```java
	List<User> users = em.typedQuery(User.class,"SELECT userId,firstname,lastname FROM user LIMIT 100");
	
	for(User user : user)
	{
		...
	}

Please note that the Typed Query returned managed instanced of the entities. Thus the primary key/compound primary keys/select * should be present in the query string.

Raw typed query

	List<User> users = em.typedQuery(User.class,"SELECT firstname,lastname FROM user LIMIT 100");
	
	for(User user : user)
	{
		...
	}

Similar to Typed Query, except that the resuls are transient entities. Consequently the restriction on mandatory primary key columns in the query string is lifted.


## Working with consistency ### Defining consistency statically
@Entity
@Consistency(read=ConsistecyLevel.ONE,write=ConsistencyLevel.QUORUM)
public class User
{
	...
}

Setting consistency level at runtime

Write consistency

	em.persist(new User(10L,"John","DOE"), OptionsBuilder.withConsistency(ConsistencyLevel.QUORUM));

or

	em.merge(new User(10L,"John","DOE"),OptionsBuilder.withConsistency(ConsistencyLevel.QUORUM));

**Read consistency** ```java User user = em.find(User.class,10L,OptionsBuilder.withConsistency(ConsistencyLevel.ALL)); ```

or

	User user = em.getReference(User.class,10L,OptionsBuilder.withConsistency(ConsistencyLevel.ALL));

The default consistency level is ONE when not set

Check [OptionsBuilder] for more details on available settings.


## Working with TTL ### Setting TTL on an entity ```java em.persist(new User(10L,"John","DOE"), OptionsBuilder.withTtl(150)); // Expire in 150 secs ```

or

	em.merge(new User(10L,"John","DOE"),OptionsBuilder.withTtl(150)); // Expire in 150 secs

Setting TTL on individual field

	User user = em.getReference(User.class,10L);
	user.setFirstname("temporary firstname");

	/* Firstname value will expire in 150 secs, 
	 * leaving the user with only userId and
	 * lastname
	 */	
	em.merge(user,OptionsBuilder.withTtl(150));

Please notice the usage of getReference() to save a read from Cassandra

Check [OptionsBuilder] for more details on available settings.


## Working with Timestamp ### Setting Timestamp on an entity ```java // Set timestamp value on the all fields of User entity em.persist(new User(10L,"John","DOE"), OptionsBuilder.withTimestamp(1357949499999L)); ```

or

	// Set timestamp value on the all fields of User entity
	em.merge(new User(10L,"John","DOE"),OptionsBuilder.withTimestamp(1357949499999L));

Setting Timestamp on individual field

	User user = em.getReference(User.class,10L);
	user.setFirstname("temporary firstname");

	/* Only firstname value will have timestamp set 
	 * to 1357949499999L
	 */	
	em.merge(user,OptionsBuilder.withTimestamp(1357949499999L));

Check [OptionsBuilder] for more details on available settings.


## Setting multiple options Of course it is possible to specify the TTL value, timestamp and/or consistency level at the same time:
	em.persist(new User(10L,"John","DOE"), OptionsBuilder
                .withConsistency(ALL)
                .ttl(10)
                .timestamp(1357949499999L)); 

or

	em.merge(new User(10L,"John","DOE"), OptionsBuilder
                .withConsistency(ALL)
                .ttl(10)
                .timestamp(1357949499999L)); 

Check [OptionsBuilder] for more details on available settings.


## Using counter type ### Persisting new counter value
	// Creating new user John DOE with 13 tweets
	em.persist(new User(10L,"John","DOE",CounterBuilder.incr(13L)));

Updating counter value from managedd entity

	User user = em.find(User.class,10L);

	// Increment tweet count by 2
	user.getTweetCount().incr(2L);

Direct update of counter value

	User user = em.getReference(User.class,10L);

	// Increment tweet count by 2
	user.getTweetCount().incr(2L);

Again, direct update using getReference() is much more performant than the find() version

Updating counter value with Consistency Level

	User user = em.getReference(User.class,10L);

	// Increment tweet count by 2 with consistency ALL
	user.getTweetCount().incr(2L,ConsistencyLevel.ALL);

## Unwrapping entities ```java User user = em.find(User.class,10L);
// Transient instance of User entity
User transient = em.unwrap(user);

 The `unwrap()` method also accept `List` or `Set` of entities as argument

[OptionsBuilder]: https://github.com/doanduyhai/Achilles/wiki/Achilles-Custom-Types#optionsbuilder

Home

Clone this wiki locally