Skip to content

Quick Reference

DuyHai DOAN edited this page Dec 24, 2014 · 28 revisions

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

@Entity
public class User
{
    @PartitionKey
    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){...}
}

## Inserting a transient entity
	manager.insert(new User(10L,"John","DOE"));

## Updating an entity ### Updating a managed entity
	User user = manager.find(User.class,10L);
	user.setFirstname("Jonathan");
	manager.update(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 = manager.forUpdate(User.class,10L);
	user.setFirstname("Jonathan");
	manager.update(user);

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

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


## Deleting an entity ### Deleting a managed entity
	User user = manager.find(User.class,10L);
	manager.delete(user);

Direct deletion

	manager.deleteById(User.class,10L);

## Deleting a property for an entity ### From a managed entity
    User user = manager.find(User.class,10L);

    // Delete the bio, activate dirty checking here
    user.setBiography(null);

    // Flush modification to Cassandra
	manager.update(user);

### Direct deletion ```java
User user = manager.forUpdate(User.class,10L);

// Delete the bio, activate dirty checking here
user.setBiography(null);

// Flush modification to Cassandra
manager.update(user);
<br/>

## Finding clustered entities

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

```java
@Entity
public class TweetLine
{
	@CompoundPrimaryKey
	private TweetKey id;

	@Column
	private String content;

	public static class TweetKey
	{
		@PartitionKey
		private Long userId;

		@ClusteringColumn(1)
		LineType type;

		@ClusteringColumn(2)
		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 = manager.sliceQuery(TweetLine.class)
    .forSelect()
    .withPartitionComponents(10L)
    .fromClusterings(LineType.TIMELINE,lastUUID)
    .toClusterings(LineType.TIMELINE)
    .orderByDescending()
    .limit(10)
    .get();

Iterating through a large set of entities

Fetch all timeline tweets by batch of 100 tweets

Iterator<TweetLine> iterator = manager.sliceQuery(TweetLine.class)
    .forIteration()
    .withPartitionComponents(10L)
    .fromClusterings(LineType.TIMELINE,lastUUID)
    .toClusterings(LineType.TIMELINE)
    .orderByDescending()
    .iterator(100);

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

Removing clustered entities

Removing all timeline tweets

manager.sliceQuery(TweetLine.class)
  .forDeletion()
  .withPartitionComponents(10L)
  .deleteMatching(LineType.TIMELINE);

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 native protocol will support range deletion.


## Querying Cassandra ### Native query ```java List> rows = manager.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 = manager.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 = manager.rawTypedQuery(User.class,
					"SELECT firstname,lastname FROM user LIMIT 100");
	
	for(User user : user)
	{
		...
	}

Similar to Typed Query, except that the results 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

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

or

	  manager.update(managedUser,
		OptionsBuilder.withConsistency(ConsistencyLevel.QUORUM));

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

or

	User user = manager.forUpdate(User.class,10L,
	OptionsBuilder.withConsistency(ConsistencyLevel.QUORUM));

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 manager.insert(new User(10L,"John","DOE"), OptionsBuilder.withTtl(150)); // Expire in 150 secs ```

Setting TTL on individual field

	User managedUser = manager.forUpdate(User.class,10L);
	managedUser.setFirstname("temporary firstname");

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

Please notice the usage of forUpdate() to avoid reading beforehand 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 manager.insert(new User(10L,"John","DOE"), OptionsBuilder.withTimestamp(1357949499999L)); ```

Setting Timestamp on individual field

	User managedUser = manager.forUpdate(User.class,10L);
	managedUser.setFirstname("temporary firstname");

	/* Only firstname value will have timestamp set 
	 * to 1357949499999L
	 */	
	manager.update(managedUser,OptionsBuilder.withTimestamp(1357949499999L));

Check OptionsBuilder for more details on available settings.


## Working with Lightweight Transaction (LWT) ### For insertion

To insert an entity if it does not exist (using the IF NOT EXISTS clause in CQL), you can use OptionsBuilder.ifNotExists() as shown below

 manager.insert(new MyEntity(...), OptionsBuilder.ifNotExists());

For deletion

To delete an entity if it already exists (using the IF EXISTS clause in CQL), you can use OptionsBuilder.ifExists() as shown below

 manager.deleteById(MyEntity.class, primaryKey, OptionsBuilder.ifExists());

For conditional update or delete

For conditional LWT updates, you can inject as many LWTCondition as needed using the OptionsBuilder.ifEqualCondition() API.

@Entity(table = "user")
public class UserEntity {
	@PartitionKey
	private Long id;

	@Column
	private String login;

	@Column
	private String name;
}

... 

// For update
manager.update(user, OptionsBuilder
		.ifEqualCondition("login","jdoe"),
		.ifEqualCondition("id",10L));
		
// For delete
manager.deleteById(UserEntity.class, OptionsBuilder
		.ifEqualCondition("login","jdoe"));
		

LWT Result Listener

To have tighter control on LWT updates, inserts or deletes, Achilles lets you inject a listener for LWT operations result. Again the OptionsBuilder.lwtResultListener() API comes to the rescue.

LWTResultListener lwtListener = new LWTResultListener() {

	public void onSuccess() {
		// Do something on success
	}

	public void onError(LWTResult lwtResult) {
	
		//Get type of LWT operation that fails
		LWTResult.Operation operation = lwtResult.operation();
		
		// Print out current values
		TypedMap currentValues = lwtResult.currentValues(); 
		for(Entry<String,Object> entry: currentValues.entrySet()) {
			System.out.println(String.format("%s = %s",entry.getKey(), entry.getValue()));			
		}
	}
};

manager.update(user, OptionsBuilder
		.ifEqualCondition("login","jdoe")
		.lwtResultListener(lwtListener));

LWT Serial Consistency

To use LOCAL_SERIAL consistency in a multi data-center infrastructure instead of the SERIAL consistency for LWT operations, you can call lwtLocalSerial() on the OptionsBuilder

manager.update(user, OptionsBuilder
		.ifEqualCondition("login","jdoe")
		.lwtLocalSerial());
		  

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

or

	manager.update(managedUser, OptionsBuilder
	            .ifEqualCondition("login","jdoe")
                .withConsistency(QUORUM)
                .ttl(10)
                .timestamp(1357949499999L)); 

Check OptionsBuilder for more details on available settings.


## Using counter type ### Inserting a new counter value
	// Creating new user John DOE with 13 tweets
	manager.insert(new User(10L,"John","DOE",CounterBuilder.incr(13L)));

Updating counter value from managed entity

	User managedUser = manager.find(User.class,10L);

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

  manager.update(managedUser);

Direct update of counter value

	User user = manager.forUpdate(User.class,10L);

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

  manager.update(managedUser);

Again, direct update using forUpdate() is much more efficient than the find() version


## Asynchronous operations

To perform all operations asynchronously, you should use the AsyncManager instead of the PersistenceManager:

    AsyncManager asyncManager = persistenceManagerFactory.createAsyncManager();
    
    AchillesFuture<User> futureUser = asyncManager.insert(new User(...));
    ...
    AchillesFuture<User> futureFoundUser = asyncManager.find(User.class, userId);
    ...

For more details, check Asynchronous Operations


## Removing proxies from entities ```java User managedUser = manager.find(User.class,10L);
// Transient instance of User entity
User transient = manager.removeProxy(managedUser);

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

<br/>
## Generating DDL scripts

Sometime it is nice to let **Achilles** generate for you the `CREATE TABLE` script. To do that:

* Set up unit test using **[Achilles JUnit test resource]** 
* Activate the DDL logging by setting **DEBUG** level on the logger `ACHILLES_DDL_SCRIPT` 

```xml
    <logger name="ACHILLES_DDL_SCRIPT">
        <level value="DEBUG" />
    </logger>

## Generating DML statements

To debug Achilles behavior, you can enable DML statements logging by setting DEBUG level on the logger ACHILLES_DML_STATEMENT

    <logger name="ACHILLES_DML_STATEMENT">
        <level value="DEBUG" />
    </logger>

Home

Clone this wiki locally