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

ru.vyarus.guice.persist.orient.OrientModule Maven / Gradle / Ivy

There is a newer version: 4.1.0
Show newest version
package ru.vyarus.guice.persist.orient;

import com.google.inject.Binder;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import com.google.inject.persist.PersistModule;
import com.google.inject.persist.PersistService;
import com.google.inject.persist.UnitOfWork;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import org.aopalliance.intercept.MethodInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.vyarus.guice.persist.orient.db.DatabaseManager;
import ru.vyarus.guice.persist.orient.db.pool.DocumentPool;
import ru.vyarus.guice.persist.orient.db.pool.PoolManager;
import ru.vyarus.guice.persist.orient.db.retry.Retry;
import ru.vyarus.guice.persist.orient.db.retry.RetryMethodInterceptor;
import ru.vyarus.guice.persist.orient.db.transaction.TransactionManager;
import ru.vyarus.guice.persist.orient.db.transaction.TxConfig;
import ru.vyarus.guice.persist.orient.db.transaction.internal.TransactionInterceptor;

import javax.inject.Singleton;
import java.lang.reflect.Method;

/**
 * Module provides integration for orient db with guice through guice-persist.
 * 

Orient storage format is unified within database types (object, document, graph), so it's possible to use * the same database as object, document or graph.

*

For example, object connection could be used for schema initialization and property updates * and graph connection to work with relations

*

Module initialize set of connection pools. By default its object, document and graph * (but depends on available jars in classpath: * if graph or object jars are not in classpath these pools will not be loaded). Set of pools may be modified * by overriding {@code #configurePools()} method.

*

To initialize (create or update) database schema register * {@code ru.vyarus.guice.persist.orient.db.scheme.SchemeInitializer} * implementation. By default no-op implementation registered.

* Two implementations provided to automatically initialize scheme from domain objects: *
    *
  • {@link ru.vyarus.guice.persist.orient.support.PackageSchemeModule}. * Useful if all domain entities located in one package (package by layer)
  • *
  • {@link ru.vyarus.guice.persist.orient.support.AutoScanSchemeModule}. * Useful if domain model located in different packages or to provide more control on which entities are mapped * (package by feature).
  • *
* NOTE: it's better to not perform db updates in schema initializer, because schema updates * must be performed in no-tx mode. *

Both default initializers use extended object mapper * {@link ru.vyarus.guice.persist.orient.db.scheme.initializer.ObjectSchemeInitializer}, build around * default orient object mapper. It allows defining custom annotations (plugins).

*

To initialize or migrate database data you can define * {@link ru.vyarus.guice.persist.orient.db.data.DataInitializer}. By default, * no-op implementation registered. Data initializer called without transaction, because different initialization * cases are possible.

*

All pools share the same transaction (object and graph connections use document connection internally). * All transactions are orchestrated with {@code ru.vyarus.guice.persist.orient.db.transaction.TransactionManager}. * Pool maintains lazy transaction, so when transaction * manager starts new transaction, pool will not initialize connection, until connection will be requested.

*

It's possible to override default transaction manager implementation: simply register new manager * implementation of {@code ru.vyarus.guice.persist.orient.db.transaction.TransactionManager}

*

Transaction could be initialized with @Transactional annotation or using transaction templates ( * {@code ru.vyarus.guice.persist.orient.db.transaction.template.TxTemplate} or * {@code ru.vyarus.guice.persist.orient.db.transaction.template.SpecificTxTemplate}. To define transaction type * for specific transaction (or switch off transaction within unit of work) use @TxType annotation. * Also this could be done with transaction templates.

* To work with database objects use {@link ru.vyarus.guice.persist.orient.db.PersistentContext}: *
    *
  • PersistentContext<OObjectDatabaseTx> for object db connection
  • *
  • PersistentContext<ODatabaseDocumentTx> for document db connection
  • *
  • PersistentContext<OrientBaseGraph> for graph db connection (transactional or not)
  • *
  • PersistentContext<OrientGraph> for transactional graph db connection * (will fail if notx transaction type)
  • *
  • PersistentContext<OrientGraphNoTx> for non transactional graph db connection (will provide only * for notx transaction type, otherwise fail)
  • *
* It is also possible to obtain connection by using provider directly {@code Provider}. * Provider will fail to provide connection if unit of work is not defined (using annotation or transactional template) *

{@link ru.vyarus.guice.persist.orient.db.PersistentContext} combines provider, * templates and access to transaction manager (single point to access almost all api).

*

Persistent service must be manually started or stopped: obtain PersistService and call .start() and .stop() when * appropriate. This will start/stop all registered pools. Without initialization any try * to obtain connection will fail.

*

Local database auto creation is enabled by default. Disable it if required.

* * @see ru.vyarus.guice.persist.orient.db.transaction.TransactionManager for details about transactions */ public class OrientModule extends PersistModule { private final Logger logger = LoggerFactory.getLogger(OrientModule.class); private final String uri; private final String user; private final String password; private TxConfig txConfig; private boolean autoCreateDb = true; private Multibinder poolsMultibinder; private MethodInterceptor interceptor; /** * Configures module with database credentials. * * @param uri database uri * @param user database user * @param password database password */ public OrientModule(final String uri, final String user, final String password) { this.uri = uri; this.user = user; this.password = password; } /** * Use if you need to change transactions type globally or define some generic exceptions to rollback * handling (see @Transactional annotation) * By default, {@code OTransaction.TXTYPE.OPTIMISTIC} transactions enabled and no exceptions defined * for rollback (every exception will lead to rollback). * * @param txConfig default tx config to use for transactions without explicit config definition. * @return module itself for chained calls */ public OrientModule defaultTransactionConfig(final TxConfig txConfig) { this.txConfig = txConfig; return this; } /** * Use to disable auto creation. Auto creation works only for local connection types (plocal, memory). * By default, local database auto creation is enabled. * * @param autoCreateDb true to enable auto creation, false to disable * @return module itself for chained calls */ public OrientModule autoCreateLocalDatabase(final boolean autoCreateDb) { this.autoCreateDb = autoCreateDb; return this; } @Override protected void configurePersistence() { poolsMultibinder = Multibinder.newSetBinder(binder(), PoolManager.class); bindConstant().annotatedWith(Names.named("orient.uri")).to(uri); bindConstant().annotatedWith(Names.named("orient.user")).to(user); bindConstant().annotatedWith(Names.named("orient.password")).to(password); bindConstant().annotatedWith(Names.named("orient.db.autocreate")).to(autoCreateDb); bind(TxConfig.class).annotatedWith(Names.named("orient.txconfig")) .toInstance(txConfig == null ? new TxConfig() : txConfig); // extension points bind(TransactionManager.class); // SchemeInitializer.class // DataInitializer.class bind(PersistService.class).to(DatabaseManager.class); bind(UnitOfWork.class).to(TransactionManager.class); configurePools(); configureInterceptor(); bindRetryInterceptor(); } /** * Default pools configuration. * Setup object, document and graph pools. * Override to register new pool implementations or reduce default pools. * NOTE: graph pool requires 3 providers: one for base graph type (OrientBaseGraph) and two more for * transactional (OrientGraph) and not transactional (OrientGraphNoTx) connections. */ protected void configurePools() { bindPool(ODatabaseDocumentTx.class, DocumentPool.class); // pools availability should depend on available jars in classpath // this way object and graph dependencies are optional loadOptionalPool("ru.vyarus.guice.persist.orient.support.pool.ObjectPoolBinder"); loadOptionalPool("ru.vyarus.guice.persist.orient.support.pool.GraphPoolBinder"); } /** * Configures transactional annotation interceptor. * Override to register different interceptor implementation. */ protected void configureInterceptor() { interceptor = new TransactionInterceptor(); requestInjection(interceptor); } /** * Register pool within pools set and register provider for specified type. * Use to register custom pools in {@code #configurePools()}. * * @param type connection object type * @param pool pool type * @param connection object type * @param

pool type */ protected final > void bindPool(final Class type, final Class

pool) { bind(pool).in(Singleton.class); poolsMultibinder.addBinding().to(pool); bind(type).toProvider(pool); } /** * Allows to load pool only if required jars are in classpath. * For example, no need for graph dependencies if only object db is used. * * @param poolBinder pool binder class * @see ru.vyarus.guice.persist.orient.support.pool.ObjectPoolBinder as example */ protected void loadOptionalPool(final String poolBinder) { try { final Method bindPool = OrientModule.class.getDeclaredMethod("bindPool", Class.class, Class.class); bindPool.setAccessible(true); try { Class.forName(poolBinder) .getConstructor(OrientModule.class, Method.class, Binder.class) .newInstance(this, bindPool, binder()); } finally { bindPool.setAccessible(false); } } catch (Exception ignored) { if (logger.isTraceEnabled()) { logger.trace("Failed to process pool loader " + poolBinder, ignored); } } } @Override protected void bindInterceptor(final Matcher> classMatcher, final Matcher methodMatcher, final MethodInterceptor... interceptors) { // hack to correctly bind @Transactional annotation for java8: // aop tries to intercept synthetic methods which cause a lot of warnings // (and generally not correct) super.bindInterceptor(classMatcher, new AbstractMatcher() { @Override public boolean matches(final Method method) { return !method.isSynthetic() && !method.isBridge() && methodMatcher.matches(method); } }, interceptors); } @Override protected MethodInterceptor getTransactionInterceptor() { return interceptor; } protected void bindRetryInterceptor() { // retry interceptor must be bound before transactional interceptor final RetryMethodInterceptor retryInterceptor = new RetryMethodInterceptor(); requestInjection(retryInterceptor); bindInterceptor(Matchers.any(), Matchers.annotatedWith(Retry.class), retryInterceptor); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy