All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.archinnov.achilles.persistence.PersistenceManager Maven / Gradle / Ivy

There is a newer version: 6.1.0
Show newest version
/*
 * Copyright (C) 2012-2014 DuyHai DOAN
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package info.archinnov.achilles.persistence;


import static com.datastax.driver.core.BatchStatement.Type.LOGGED;
import static com.datastax.driver.core.BatchStatement.Type.UNLOGGED;
import static info.archinnov.achilles.options.OptionsBuilder.noOptions;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.datastax.driver.core.Statement;
import info.archinnov.achilles.internal.validation.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.datastax.driver.core.Session;
import info.archinnov.achilles.internal.context.ConfigurationContext;
import info.archinnov.achilles.internal.context.DaoContext;
import info.archinnov.achilles.internal.context.PersistenceContextFactory;
import info.archinnov.achilles.internal.metadata.holder.EntityMeta;
import info.archinnov.achilles.query.cql.NativeQuery;
import info.archinnov.achilles.query.slice.SliceQueryBuilder;
import info.archinnov.achilles.query.typed.TypedQuery;
import info.archinnov.achilles.type.IndexCondition;
import info.archinnov.achilles.options.Options;

/**
 * 

* Stateless object to manage entity persistence. * This class is totally thread-safe and can be shared by many threads. * You should normally have only one instance of PersistenceMananger across the application * *
* A PersistenceMananger is very cheap to create from an {@link info.archinnov.achilles.persistence.PersistenceManagerFactory} *

* *

*

I Persist transient entity

*

 *      // Insert
 *      MyEntity managedEntity = manager.insert(myEntity);
 *  
* *

II Update for modifications

*

 *      User managedUser = manager.find(User.class,1L);
 *      user.setFirstname("DuyHai");
 *
 *      manager.update(user);
 *  
* *

III Deleting entities

*

 *      // Simple deletion
 *      User managedUser = manager.find(User.class,1L);
 *      manager.delete(managedUser);
 *
 *      // Direct deletion without read-before-write
 *      manager.deleteById(User.class,1L);
 *  
* *

IV Loading entities

*

 *      // Read data from Cassandra
 *      User managedUser = manager.find(User.class,1L);
 *  
* *

V Creating proxy for update

*

 *      // No data read from Cassandra
 *      User managedUser = manager.forUpdate(User.class,1L);
 *      managedUser.setAge(30);
 *
 *      // Direct update, no read from Cassandra has been done
 *      manager.update(managedUser);
 *  
* *

VI Reloading state for managed entities

*

 *      // Read data from Cassandra
 *      User managedUser = manager.find(User.class,1L);
 *      ...
 *      // Perform some logic
 *
 *      // Reload data from Cassandra into the managed entity
 *      manager.refresh(managedUser);
 *  
* *

VII Removing proxy from managed entities

*

 *      // Create managed entity
 *      User managedUser = manager.find(User.class,1L);
 *      ...
 *      // Perform some logic
 *
 *      // Removing proxy before passing it to client via serialization
 *      User transientUser = manager.removeProxy(managedUser);
 *  
* *

VIII Accessing native Session object

*

 *      Session session = manager.getNativeSession();
 *      ...
 *
 *      // Issue simple CQL queries
 *      session.execute("UPDATE users SET age=:age WHERE id=:id",30,10);
 *  
* *

IX JSON serialization/deserialization

*

 *      // Serialize an object to JSON using the registered or default object mapper
 *      String json = manager.serializeToJSON(myModel);
 *      ...
 *
 *      // Deserialize a JSON string into an object  the registered or default object mapper
 *      MyModel myModel = manager.deserializeFromJSON(json);
 *  
* *

X Initializing all counter fields

*

 *      // Create managed entity
 *      User user = manager.find(User.class,1L);
 *      ...
 *      // Perform some logic
 *      ...
 *
 *      // Load all lazy counter fields
 *      manager.initialize(user);
 *  
*

* * * @see Persistence Manager operations */ public class PersistenceManager extends CommonPersistenceManager { private static final Logger log = LoggerFactory.getLogger(PersistenceManager.class); protected PersistenceManager(Map, EntityMeta> entityMetaMap, PersistenceContextFactory contextFactory, DaoContext daoContext, ConfigurationContext configContext) { super(entityMetaMap, contextFactory, daoContext, configContext); } /** * Find an entity. * *

     *      // Read data from Cassandra
     *      User managedUser = manager.find(User.class,1L);
     *  
* * @param entityClass * Entity type * @param primaryKey * Primary key (Cassandra row key) of the entity to load * * @return T managed entity */ public T find(Class entityClass, Object primaryKey) { log.debug("Find entity class '{}' with primary key '{}'", entityClass, primaryKey); return super.asyncFind(entityClass, primaryKey, noOptions()).getImmediately(); } /** * Find an entity with the given Consistency Level for read * *

     *      // Read data from Cassandra
     *      User managedUser = manager.find(User.class,1L,QUORUM);
     *  
* * @param entityClass * Entity type * @param primaryKey * Primary key (Cassandra row key) of the entity to load * @param options * Options for read * * @return T managed entity */ public T find(final Class entityClass, final Object primaryKey, Options options) { if (log.isDebugEnabled()) { log.debug("Find entity class '{}' with primary key {} and options {}", entityClass, primaryKey, options); } return super.asyncFind(entityClass, primaryKey, options).getImmediately(); } /** * Create a proxy for the entity update. An new empty entity will be created, * populated with the provided primary key and then proxified. This method * never returns null. Use this method to perform direct update without * read-before-write * *

     *      // No data read from Cassandra
     *      User proxifiedUser = manager.forUpdate(User.class,1L);
     *      proxifiedUser.setAge(33);
     *      manager.update(proxifiedUser);
     *  
* * @param entityClass * Entity type * @param primaryKey * Primary key (Cassandra row key) of the entity to initialize * * @return T proxy */ public T forUpdate(Class entityClass, Object primaryKey) { log.debug("Get reference for entity class '{}' with primary key {}", entityClass, primaryKey); return super.getProxyForUpdateInternal(entityClass, primaryKey); } /** * Initialize all lazy counter fields of a set of 'managed' entities * *

     *      // Create a managed entity
     *      User user = manager.find(User.class,1L);
     *      ...
     *      // Perform some logic
     *
     *      // Initialize all counter fields not yet loaded into the managed entity
     *      manager.initialize(user);
     *  
* * Raise an IllegalStateException if an entity is not 'managed' * */ public T initialize(final T entity) { if (log.isDebugEnabled()) { log.debug("Force lazy fields initialization for entity {}", proxifier.removeProxy(entity)); } return super.initialize(entity); } /** * Initialize all lazy counter fields of a list of 'managed' entities * *

     *      // Create managed entities
     *      User user1 = manager.find(User.class,1L);
     *      User user2 = manager.find(User.class,2L);
     *      ...
     *      // Perform some logic
     *      ...
     *
     *      // Initialize all counter fields not yet loaded into the managed entity
     *      manager.initialize(Sets.newHashSet(user1, user2));
     *  
* * Raise an IllegalStateException if an entity is not 'managed' * */ public Set initialize(final Set entities) { log.debug("Force lazy fields initialization for entity set {}", entities); return super.initialize(entities); } /** * Initialize all lazy counter fields of a list of 'managed' entities * *

     *      // Create managed entities
     *      User user1 = manager.find(User.class,1L);
     *      User user2 = manager.find(User.class,2L);
     *      ...
     *      // Perform some logic
     *      ...
     *
     *      // Initialize all counter fields not yet loaded into the managed entity
     *      manager.initialize(Arrays.asList(user1, user2));
     *  
* * Raise an IllegalStateException if an entity is not 'managed' * */ public List initialize(final List entities) { log.debug("Force lazy fields initialization for entity set {}", entities); return super.initialize(entities); } /** * Shorthand for manager.removeProxy(manager.initialize(T entity)) * */ public T initAndRemoveProxy(T entity) { return super.removeProxy(super.initialize(entity)); } /** * Shorthand for manager.removeProxy(manager.initialize(Set entities)) * */ public Set initAndRemoveProxy(Set entities) { return super.removeProxy(super.initialize(entities)); } /** * Shorthand for manager.removeProxy(manager.initialize(List entities)) * */ public List initAndRemoveProxy(List entities) { return super.removeProxy(super.initialize(entities)); } /** * Remove the proxy of a 'managed' entity and return the underlying "raw" * entity *
*
* If the argument is not a proxy objet, return itself
* Else, return the target object behind the proxy * *

     *      // Create managed entity
     *      User managedUser = manager.find(User.class,1L);
     *      ...
     *      // Perform some logic
     *
     *      // Removing proxy before passing it to client via serialization
     *      User transientUser = manager.removeProxy(managedUser);
     *  
* * @param proxy * @return real object */ public T removeProxy(T proxy) { log.debug("Removing proxy for entity {}", proxy); return super.removeProxy(proxy); } /** * Remove the proxy of a list of 'managed' entities and return the * underlying "raw" entities * * See {@link #removeProxy} * * @param proxies * list of proxified entity * @return real object list */ public List removeProxy(List proxies) { log.debug("Removing proxy for a list of entities {}", proxies); return super.removeProxy(proxies); } /** * Remove the proxy of a set of 'managed' entities return the underlying * "raw" entities * * See {@link #removeProxy} * * @param proxies * set of proxified entities * @return real object set */ public Set removeProxy(Set proxies) { log.debug("Removing proxy for a set of entities {}", proxies); return super.removeProxy(proxies); } /** * Create a builder to start slice query DSL. The provided entity class must be: * *
    *
  • a entity type managed by Achilles
  • *
  • a clustered entity, slicing is irrelevant for non-clustered entity
  • *
* * @see Slice query API * * @param entityClass type of the clustered entity * @param : type of the clustered entity * @return SliceQueryBuilder */ public SliceQueryBuilder sliceQuery(Class entityClass) { log.debug("Execute slice query for entity class {}", entityClass); final EntityMeta meta = super.validateSliceQueryInternal(entityClass); return new SliceQueryBuilder<>(sliceQueryExecutor, entityClass, meta); } /** * Return a CQL native query * *
*
* *

Native query without bound values

*

     *      RegularStatement nativeQuery = select("name",age_in_years").from("UserEntity").where(in("id",Arrays.asList(10,11))).limit(20);
     *      List<TypedMap> actual = manager.nativeQuery(nativeQuery).get();
     *  
* *
*
* *

Native query with bound values

*

     *      RegularStatement nativeQuery = select("name",age_in_years").from("UserEntity").where(in("id",bindMarker())).limit(bindMarker());
     *      List<TypedMap> actual = manager.nativeQuery(nativeQuery,Arrays.asList(10,11),20).get();
     *  
* * @see Native query API * * @param statement * native CQL regularStatement, including limit, ttl and consistency * options * * @param boundValues * values to be bind to the parameterized query, if any * * @return NativeQuery */ public NativeQuery nativeQuery(Statement statement, Object... boundValues) { log.debug("Execute native query {}", statement); Validator.validateNotNull(statement, "The statement for native query should not be null"); return new NativeQuery(daoContext, configContext, statement, noOptions(), boundValues); } /** * Return a CQL native query builder * *
*
* *

Native query without bound values

*

     *      RegularStatement nativeQuery = select("name",age_in_years").from("UserEntity").where(in("id",Arrays.asList(10,11))).limit(20);
     *      List<TypedMap> actual = manager.nativeQuery(nativeQuery).get();
     *  
* *
*
* *

Native query with bound values

*

     *      RegularStatement nativeQuery = select("name",age_in_years").from("UserEntity").where(in("id",bindMarker())).limit(bindMarker());
     *      List<TypedMap> actual = manager.nativeQuery(nativeQuery,Arrays.asList(10,11),20).get();
     *  
* * @see Native query API * * @param statement * native CQL statement, including limit, ttl and consistency * options * * @param options * options for the query. Only LWT Result listener passed as option is taken * into account. For timestamp, TTL and LWT conditions you must specify them * directly in the query string * * @param boundValues * values to be bind to the parameterized query, if any * * @return NativeQuery */ public NativeQuery nativeQuery(Statement statement, Options options, Object... boundValues) { log.debug("Execute native query {}", statement); Validator.validateNotNull(statement, "The statement for native query should not be null"); return new NativeQuery(daoContext, configContext, statement, options, boundValues); } /** * Return a CQL typed query builder * * All found entities will be in managed state * *
*
* *

Typed query without bound values

*

     *      RegularStatement nativeQuery = select().from("MyEntity").where().limit(3);
     *      List<MyEntity> actual = manager.typedQuery(MyEntity.class, nativeQuery).get();
     *  
* *
*
* *

Typed query with bound values

*

     *      RegularStatement statement = select().from("MyEntity").limit(bindMarker());
     *      List<MyEntity> actual = manager.typedQuery(MyEntity.class, statement,3).get();
     *  
* * @see Typed query API * * @param entityClass * type of entity to be returned * * @param statement * native CQL regularStatement, including limit, ttl and consistency * options * * @param boundValues * values to be bind to the parameterized query, if any * * @return TypedQuery */ public TypedQuery typedQuery(Class entityClass, Statement statement, Object... boundValues) { log.debug("Execute typed query {}", statement); final EntityMeta meta = super.typedQueryInternal(entityClass, statement, boundValues); return new TypedQuery<>(entityClass, daoContext, configContext, statement, meta, contextFactory, boundValues); } /** * Return a CQL typed query builder * * All found entities will be in 'managed' state * * @param entityClass * type of entity to be returned * * @param indexCondition * index condition * * @return TypedQuery */ public TypedQuery indexedQuery(Class entityClass, IndexCondition indexCondition) { log.debug("Execute indexed query for entity class {}", entityClass); final Statement statement = super.indexedQueryInternal(entityClass, indexCondition); final EntityMeta meta = super.typedQueryInternal(entityClass, statement, indexCondition.getColumnValue()); return new TypedQuery<>(entityClass, daoContext, configContext, statement, meta, contextFactory, new Object[]{indexCondition.getColumnValue()}); } /** * Serialize the entity in JSON using a registered Object Mapper or default Achilles Object Mapper * @param entity * @return serialized entity in JSON * @throws IOException */ public String serializeToJSON(Object entity) throws IOException { return super.serializeToJSON(entity); } /** * Deserialize the given JSON into entity using a registered Object Mapper or default Achilles Object Mapper * @param type * @param serialized * @param * @return deserialized entity from JSON * @throws IOException */ public T deserializeFromJSON(Class type, String serialized) throws IOException { return super.deserializeFromJSON(type, serialized); } /** * Return Session object from Java Driver * @return Session */ public Session getNativeSession() { return super.getNativeSession(); } /** * Create a new state-full LOGGED Batch
*
*

* WARNING : This Batch is state-full and not * thread-safe. In case of exception, you MUST not re-use it but create * another one * * @return a new state-full Batch */ public Batch createLoggedBatch() { log.debug("Create new Logged Batch instance"); return new Batch(entityMetaMap, contextFactory, daoContext, configContext, LOGGED, false); } /** * Create a new state-full UNLOGGED Batch
*
*

* WARNING : This Batch is state-full and not * thread-safe. In case of exception, you MUST not re-use it but create * another one * * @return a new state-full Batch */ public Batch createUnloggedBatch() { log.debug("Create new Unlogged Batch instance"); return new Batch(entityMetaMap, contextFactory, daoContext, configContext, UNLOGGED, false); } /** * Create a new state-full ordered and LOGGED Batch
*
*

* This Batch respect insertion order by generating increasing timestamp with micro second resolution. * If you use ordered Batch in multiple clients, do not forget to synchronize the clock between those clients * to avoid statements interleaving *

* WARNING : This Batch is state-full and not * thread-safe. In case of exception, you MUST not re-use it but create * another one * * @return a new state-full ordered Batch */ public Batch createOrderedLoggedBatch() { log.debug("Create new ordered Logged Batch"); return new Batch(entityMetaMap, contextFactory, daoContext, configContext, LOGGED, true); } /** * Create a new state-full ordered and UNLOGGED Batch
*
*

* This Batch respect insertion order by generating increasing timestamp with micro second resolution. * If you use ordered Batch in multiple clients, do not forget to synchronize the clock between those clients * to avoid statements interleaving *

* WARNING : This Batch is state-full and not * thread-safe. In case of exception, you MUST not re-use it but create * another one * * @return a new state-full ordered Batch */ public Batch createOrderedUnloggedBatch() { log.debug("Create new ordered Unlogged Batch"); return new Batch(entityMetaMap, contextFactory, daoContext, configContext, UNLOGGED, true); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy