-
Notifications
You must be signed in to change notification settings - Fork 92
Quick Reference
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
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);
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}
}
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();
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 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.
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
{
...
}
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 ```
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)); ```
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());
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 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"));
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));
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)));
User managedUser = manager.find(User.class,10L);
// Increment tweet count by 2
managedUser.getTweetCount().incr(2L);
manager.update(managedUser);
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>
-
Bootstraping Achilles at runtime
- Runtime Configuration Parameters
-
Manager
-
Consistency Level
-
Cassandra Options at runtime
-
Lightweight Transaction (LWT)
-
JSON Serialization
-
Interceptors
-
Bean Validation (JSR-303)