
io.ebeaninternal.server.core.DefaultServer Maven / Gradle / Ivy
Show all versions of ebean Show documentation
package io.ebeaninternal.server.core;
import io.ebean.AutoTune;
import io.ebean.BackgroundExecutor;
import io.ebean.BeanState;
import io.ebean.CallableSql;
import io.ebean.DocumentStore;
import io.ebean.ExpressionFactory;
import io.ebean.Filter;
import io.ebean.FutureIds;
import io.ebean.FutureList;
import io.ebean.FutureRowCount;
import io.ebean.PagedList;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.RawSql;
import io.ebean.SqlQuery;
import io.ebean.SqlRow;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.TransactionCallback;
import io.ebean.TxCallable;
import io.ebean.TxRunnable;
import io.ebean.TxScope;
import io.ebean.Update;
import io.ebean.UpdateQuery;
import io.ebean.ValuePair;
import io.ebean.Version;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.CallStack;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.PersistenceContext;
import io.ebean.bean.PersistenceContext.WithOption;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.EncryptKeyManager;
import io.ebean.config.ServerConfig;
import io.ebean.config.TenantMode;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.dbmigration.DdlGenerator;
import io.ebean.event.BeanPersistController;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
import io.ebean.plugin.BeanType;
import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import io.ebeaninternal.api.LoadBeanRequest;
import io.ebeaninternal.api.LoadManyRequest;
import io.ebeaninternal.api.ScopeTrans;
import io.ebeaninternal.api.ScopedTransaction;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuery.Type;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.TransactionEventTable;
import io.ebeaninternal.server.autotune.AutoTuneService;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.el.ElFilter;
import io.ebeaninternal.server.grammer.EqlParser;
import io.ebeaninternal.server.lib.ShutdownManager;
import io.ebeaninternal.server.query.CQuery;
import io.ebeaninternal.server.query.CQueryEngine;
import io.ebeaninternal.server.query.CallableQueryIds;
import io.ebeaninternal.server.query.CallableQueryList;
import io.ebeaninternal.server.query.CallableQueryCount;
import io.ebeaninternal.server.query.LimitOffsetPagedList;
import io.ebeaninternal.server.query.QueryFutureIds;
import io.ebeaninternal.server.query.QueryFutureList;
import io.ebeaninternal.server.query.QueryFutureRowCount;
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
import io.ebeaninternal.server.querydefn.DefaultOrmUpdate;
import io.ebeaninternal.server.querydefn.DefaultRelationalQuery;
import io.ebeaninternal.server.querydefn.DefaultUpdateQuery;
import io.ebeaninternal.server.text.csv.TCsvReader;
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
import io.ebeaninternal.server.transaction.TransactionManager;
import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.util.ParamTypeHelper;
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import io.ebeanservice.docstore.api.DocStoreIntegration;
import io.ebean.TxIsolation;
import io.ebean.TxType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* The default server side implementation of EbeanServer.
*/
public final class DefaultServer implements SpiServer, SpiEbeanServer {
private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class);
private static final int IGNORE_LEADING_ELEMENTS = 5;
private static final String IO_EBEAN = "io.ebean";
private final ServerConfig serverConfig;
private final String serverName;
private final DatabasePlatform databasePlatform;
private final TransactionManager transactionManager;
private final TransactionScopeManager transactionScopeManager;
private final DataTimeZone dataTimeZone;
private final CallStackFactory callStackFactory = new DefaultCallStackFactory();
private final int maxCallStack;
/**
* Ebean defaults this to true but for EJB compatible behaviour set this to
* false;
*/
private final boolean rollbackOnChecked;
/**
* Handles the save, delete, updateSql CallableSql.
*/
private final Persister persister;
private final OrmQueryEngine queryEngine;
private final RelationalQueryEngine relationalQueryEngine;
private final ServerCacheManager serverCacheManager;
private final BeanDescriptorManager beanDescriptorManager;
private final AutoTuneService autoTuneService;
private final ReadAuditPrepare readAuditPrepare;
private final ReadAuditLogger readAuditLogger;
private final CQueryEngine cqueryEngine;
private final List serverPlugins;
private final DdlGenerator ddlGenerator;
private final ExpressionFactory expressionFactory;
private final SpiBackgroundExecutor backgroundExecutor;
private final DefaultBeanLoader beanLoader;
private final EncryptKeyManager encryptKeyManager;
private final JsonContext jsonContext;
private final DocumentStore documentStore;
private final MetaInfoManager metaInfoManager;
private final CurrentTenantProvider currentTenantProvider;
/**
* The default PersistenceContextScope used if it is not explicitly set on a query.
*/
private final PersistenceContextScope defaultPersistenceContextScope;
/**
* Flag set when the server has shutdown.
*/
private boolean shutdown;
/**
* The default batch size for lazy loading beans or collections.
*/
private final int lazyLoadBatchSize;
/**
* The query batch size
*/
private final int queryBatchSize;
private final boolean updateAllPropertiesInBatch;
private final boolean collectQueryOrigins;
private final boolean collectQueryStatsByNode;
/**
* Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points).
*/
protected final ConcurrentHashMap objectGraphStats;
/**
* Create the DefaultServer.
*/
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
this.serverConfig = config.getServerConfig();
this.objectGraphStats = new ConcurrentHashMap<>();
this.metaInfoManager = new DefaultMetaInfoManager(this);
this.serverCacheManager = cache;
this.databasePlatform = config.getDatabasePlatform();
this.backgroundExecutor = config.getBackgroundExecutor();
this.serverName = serverConfig.getName();
this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize();
this.queryBatchSize = serverConfig.getQueryBatchSize();
this.cqueryEngine = config.getCQueryEngine();
this.expressionFactory = config.getExpressionFactory();
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
this.defaultPersistenceContextScope = serverConfig.getPersistenceContextScope();
this.currentTenantProvider = serverConfig.getCurrentTenantProvider();
this.beanDescriptorManager = config.getBeanDescriptorManager();
beanDescriptorManager.setEbeanServer(this);
this.updateAllPropertiesInBatch = serverConfig.isUpdateAllPropertiesInBatch();
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
this.maxCallStack = serverConfig.getMaxCallStack();
this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked();
this.persister = config.createPersister(this);
this.queryEngine = config.createOrmQueryEngine();
this.relationalQueryEngine = config.createRelationalQueryEngine();
this.autoTuneService = config.createAutoTuneService(this);
this.readAuditPrepare = config.getReadAuditPrepare();
this.readAuditLogger = config.getReadAuditLogger();
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
this.dataTimeZone = config.getDataTimeZone();
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
this.transactionScopeManager = config.createTransactionScopeManager(transactionManager);
this.documentStore = docStoreComponents.documentStore();
this.serverPlugins = config.getPlugins();
this.ddlGenerator = new DdlGenerator(this, serverConfig);
configureServerPlugins();
// Register with the JVM Shutdown hook
ShutdownManager.registerEbeanServer(this);
}
private void configureServerPlugins() {
autoTuneService.startup();
for (Plugin plugin : serverPlugins) {
plugin.configure(this);
}
}
/**
* Execute all the plugins with an online flag indicating the DB is up or not.
*/
public void executePlugins(boolean online) {
if (!serverConfig.isDocStoreOnly()) {
ddlGenerator.execute(online);
}
for (Plugin plugin : serverPlugins) {
plugin.online(online);
}
}
@Override
public boolean isCollectQueryOrigins() {
return collectQueryOrigins;
}
@Override
public boolean isUpdateAllPropertiesInBatch() {
return updateAllPropertiesInBatch;
}
public int getLazyLoadBatchSize() {
return lazyLoadBatchSize;
}
public int getQueryBatchSize() {
return queryBatchSize;
}
@Override
public Object currentTenantId() {
return currentTenantProvider.currentId();
}
public ServerConfig getServerConfig() {
return serverConfig;
}
public DatabasePlatform getDatabasePlatform() {
return databasePlatform;
}
@Override
public DataTimeZone getDataTimeZone() {
return dataTimeZone;
}
@Override
public MetaInfoManager getMetaInfoManager() {
return metaInfoManager;
}
@Override
public SpiServer getPluginApi() {
return this;
}
public BackgroundExecutor getBackgroundExecutor() {
return backgroundExecutor;
}
public ExpressionFactory getExpressionFactory() {
return expressionFactory;
}
@Override
public AutoTune getAutoTune() {
return autoTuneService;
}
@Override
public DataSource getDataSource() {
return transactionManager.getDataSource();
}
@Override
public ReadAuditPrepare getReadAuditPrepare() {
return readAuditPrepare;
}
@Override
public ReadAuditLogger getReadAuditLogger() {
return readAuditLogger;
}
/**
* Run any initialisation required before registering with the ClusterManager.
*/
public void initialise() {
if (encryptKeyManager != null) {
encryptKeyManager.initialise();
}
}
/**
* Start any services after registering with the ClusterManager.
*/
public void start() {
if (!TenantMode.DB.equals(serverConfig.getTenantMode())) {
serverConfig.runDbMigration(serverConfig.getDataSource());
}
}
/**
* Shutting down via JVM Shutdown hook.
*/
public void shutdownManaged() {
synchronized (this) {
shutdownInternal(true, false);
}
}
/**
* Shutting down manually.
*/
public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) {
synchronized (this) {
// Unregister from JVM Shutdown hook
ShutdownManager.unregisterEbeanServer(this);
shutdownInternal(shutdownDataSource, deregisterDriver);
}
}
/**
* Shutdown the services like threads and DataSource.
*/
private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) {
logger.debug("Shutting down EbeanServer {}", serverName);
if (shutdown) {
// already shutdown
return;
}
shutdownPlugins();
autoTuneService.shutdown();
// shutdown background threads
backgroundExecutor.shutdown();
// shutdown DataSource (if its an Ebean one)
transactionManager.shutdown(shutdownDataSource, deregisterDriver);
shutdown = true;
if (shutdownDataSource) {
// deregister the DataSource in case ServerConfig is re-used
serverConfig.setDataSource(null);
}
}
private void shutdownPlugins() {
for (Plugin plugin : serverPlugins) {
try {
plugin.shutdown();
} catch (Exception e) {
logger.error("Error when shutting down plugin", e);
}
}
}
/**
* Return the server name.
*/
public String getName() {
return serverName;
}
public BeanState getBeanState(Object bean) {
if (bean instanceof EntityBean) {
return new DefaultBeanState((EntityBean) bean);
}
// Not an entity bean
return null;
}
/**
* Compile a query. Only valid for ORM queries.
*/
public CQuery compileQuery(Query query, Transaction t) {
SpiOrmQueryRequest qr = createQueryRequest(Type.SUBQUERY, query, t);
OrmQueryRequest orm = (OrmQueryRequest) qr;
return cqueryEngine.buildQuery(orm);
}
public ServerCacheManager getServerCacheManager() {
return serverCacheManager;
}
public void refreshMany(Object parentBean, String propertyName) {
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName);
}
public void loadMany(LoadManyRequest loadRequest) {
beanLoader.loadMany(loadRequest);
}
public void loadMany(BeanCollection> bc, boolean onlyIds) {
beanLoader.loadMany(bc, onlyIds);
}
public void refresh(Object bean) {
beanLoader.refresh(checkEntityBean(bean));
}
public void loadBean(LoadBeanRequest loadRequest) {
beanLoader.loadBean(loadRequest);
}
public void loadBean(EntityBeanIntercept ebi) {
beanLoader.loadBean(ebi);
}
public Map diff(Object a, Object b) {
if (a == null) {
return null;
}
BeanDescriptor> desc = getBeanDescriptor(a.getClass());
return DiffHelp.diff(a, b, desc);
}
/**
* Process committed beans from another framework or server in another
* cluster.
*
* This notifies this instance of the framework that beans have been committed
* externally to it. Either by another framework or clustered server. It needs
* to maintain its cache and text indexes appropriately.
*
*/
public void externalModification(TransactionEventTable tableEvent) {
SpiTransaction t = transactionScopeManager.get();
if (t != null) {
t.getEvent().add(tableEvent);
} else {
transactionManager.externalModification(tableEvent);
}
}
/**
* Developer informing eBean that tables where modified outside of eBean.
* Invalidate the cache etc as required.
*/
public void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
TransactionEventTable evt = new TransactionEventTable();
evt.add(tableName, inserts, updates, deletes);
externalModification(evt);
}
/**
* Clear the query execution statistics.
*/
public void clearQueryStatistics() {
for (BeanDescriptor> desc : getBeanDescriptors()) {
desc.clearQueryStatistics();
}
}
/**
* Create a new EntityBean bean.
*
* This will generally return a subclass of the parameter 'type' which
* additionally implements the EntityBean interface. That is, the returned
* bean is typically an instance of a dynamically generated class.
*
*/
@SuppressWarnings("unchecked")
public T createEntityBean(Class type) {
BeanDescriptor desc = getBeanDescriptor(type);
return (T) desc.createEntityBean(true);
}
/**
* Return a Reference bean.
*
* If a current transaction is active then this will check the Context of that
* transaction to see if the bean is already loaded. If it is already loaded
* then it will returned that object.
*
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public T getReference(Class type, Object id) {
if (id == null) {
throw new NullPointerException("The id is null");
}
BeanDescriptor desc = getBeanDescriptor(type);
id = desc.convertId(id);
PersistenceContext pc = null;
SpiTransaction t = transactionScopeManager.get();
if (t != null) {
pc = t.getPersistenceContext();
Object existing = desc.contextGet(pc, id);
if (existing != null) {
return (T) existing;
}
}
InheritInfo inheritInfo = desc.getInheritInfo();
if (inheritInfo == null) {
return (T) desc.contextRef(pc, null, false, id);
}
BeanProperty idProp = desc.getIdProperty();
if (idProp == null) {
throw new PersistenceException("No ID properties for this type? " + desc);
}
// we actually need to do a query because we don't know the type without the discriminator
// value, just select the id property and discriminator column (auto added)
return find(type).select(idProp.getName()).setId(id).findUnique();
}
@Override
public void register(TransactionCallback transactionCallback) {
Transaction transaction = currentTransaction();
if (transaction == null) {
throw new PersistenceException("Not currently active transaction when trying to register transactionCallback");
}
transaction.register(transactionCallback);
}
/**
* Creates a new Transaction that is NOT stored in TransactionThreadLocal. Use
* this when you want a thread to have a second independent transaction.
*/
public Transaction createTransaction() {
return transactionManager.createTransaction(true, -1);
}
/**
* Create a transaction additionally specify the Isolation level.
*
* Note that this transaction is not stored in a thread local.
*
*/
public Transaction createTransaction(TxIsolation isolation) {
return transactionManager.createTransaction(true, isolation.getLevel());
}
public T execute(TxCallable c) {
return execute(null, c);
}
public T execute(TxScope scope, TxCallable c) {
ScopeTrans scopeTrans = createScopeTrans(scope);
try {
return c.call();
} catch (Error e) {
throw scopeTrans.caughtError(e);
} catch (RuntimeException e) {
throw scopeTrans.caughtThrowable(e);
} finally {
scopeTrans.onFinally();
}
}
public void execute(TxRunnable r) {
execute(null, r);
}
public void execute(TxScope scope, TxRunnable r) {
ScopeTrans scopeTrans = createScopeTrans(scope);
try {
r.run();
} catch (Error e) {
throw scopeTrans.caughtError(e);
} catch (RuntimeException e) {
throw scopeTrans.caughtThrowable(e);
} finally {
scopeTrans.onFinally();
}
}
/**
* Determine whether to create a new transaction or not.
*
* This will also potentially throw exceptions for MANDATORY and NEVER types.
*
*/
private boolean createNewTransaction(SpiTransaction t, TxScope scope) {
TxType type = scope.getType();
switch (type) {
case REQUIRED:
return t == null;
case REQUIRES_NEW:
return true;
case MANDATORY:
if (t == null) {
throw new PersistenceException("Transaction missing when MANDATORY");
}
return false;
case NEVER:
if (t != null) {
throw new PersistenceException("Transaction exists for Transactional NEVER");
}
return false;
case SUPPORTS:
return false;
case NOT_SUPPORTED:
throw new RuntimeException("NOT_SUPPORTED should already be handled?");
default:
throw new RuntimeException("Should never get here?");
}
}
public ScopeTrans createScopeTrans(TxScope txScope) {
if (txScope == null) {
// create a TxScope with default settings
txScope = new TxScope();
} else {
// check for implied batch mode via setting batchSize
txScope.checkBatchMode();
}
SpiTransaction suspended = null;
// get current transaction from ThreadLocal or equivalent
SpiTransaction t = transactionScopeManager.get();
boolean newTransaction;
if (txScope.getType().equals(TxType.NOT_SUPPORTED)) {
// Suspend existing transaction and
// run without a transaction in scope
newTransaction = false;
suspended = t;
t = null;
} else {
// create a new Transaction based on TxType and t
newTransaction = createNewTransaction(t, txScope);
if (newTransaction) {
// suspend existing transaction (if there is one)
suspended = t;
// create a new transaction
int isoLevel = -1;
TxIsolation isolation = txScope.getIsolation();
if (isolation != null) {
isoLevel = isolation.getLevel();
}
t = transactionManager.createTransaction(true, isoLevel);
}
}
// replace the current transaction ... ScopeTrans.onFinally()
// has the job of restoring the suspended transaction
transactionScopeManager.replace(t);
return new ScopeTrans(rollbackOnChecked, newTransaction, t, txScope, suspended, transactionScopeManager);
}
/**
* Returns the current transaction (or null) from the scope.
*/
public SpiTransaction getCurrentServerTransaction() {
return transactionScopeManager.get();
}
/**
* Start a transaction with 'REQUIRED' semantics.
*
* If a transaction already exists that transaction will be used.
*
*
* Note that the transaction is stored in a ThreadLocal variable.
*
*/
public Transaction beginTransaction() {
return beginTransaction(TxScope.required());
}
public Transaction beginTransaction(TxScope scope) {
ScopeTrans scopeTrans = createScopeTrans(scope);
return new ScopedTransaction(scopeTrans);
}
/**
* Start a transaction with a specific Isolation Level.
*
* Note that the transaction is stored in a ThreadLocal variable.
*
*/
public Transaction beginTransaction(TxIsolation isolation) {
// start an explicit transaction
SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel());
transactionScopeManager.set(t);
return t;
}
/**
* Return the current transaction or null if there is not one currently in
* scope.
*/
public Transaction currentTransaction() {
return transactionScopeManager.get();
}
/**
* Commit the current transaction.
*/
public void commitTransaction() {
transactionScopeManager.commit();
}
/**
* Rollback the current transaction.
*/
public void rollbackTransaction() {
transactionScopeManager.rollback();
}
/**
* If the current transaction has already been committed do nothing otherwise
* rollback the transaction.
*
* Useful to put in a finally block to ensure the transaction is ended, rather
* than a rollbackTransaction() in each catch block.
*
*
* Code example:
*
*
* <code>
* Ebean.startTransaction();
* try {
* // do some fetching and or persisting
*
* // commit at the end
* Ebean.commitTransaction();
*
* } finally {
* // if commit didn't occur then rollback the transaction
* Ebean.endTransaction();
* }
* </code>
*
*
*
*/
public void endTransaction() {
transactionScopeManager.end();
}
/**
* return the next unique identity value.
*
* Uses the BeanDescriptor deployment information to determine the sequence to
* use.
*
*/
public Object nextId(Class> beanType) {
BeanDescriptor> desc = getBeanDescriptor(beanType);
return desc.nextId(null);
}
@SuppressWarnings("unchecked")
public void sort(List list, String sortByClause) {
if (list == null) {
throw new NullPointerException("list is null");
}
if (sortByClause == null) {
throw new NullPointerException("sortByClause is null");
}
if (list.isEmpty()) {
// don't need to sort an empty list
return;
}
// use first bean in the list as the correct type
Class beanType = (Class) list.get(0).getClass();
BeanDescriptor beanDescriptor = getBeanDescriptor(beanType);
if (beanDescriptor == null) {
throw new PersistenceException("BeanDescriptor not found, is [" + beanType + "] an entity bean?");
}
beanDescriptor.sort(list, sortByClause);
}
@Override
public Set validateQuery(Query query) {
BeanDescriptor beanDescriptor = getBeanDescriptor(query.getBeanType());
if (beanDescriptor == null) {
throw new PersistenceException("BeanDescriptor not found, is [" + query.getBeanType() + "] an entity bean?");
}
return ((SpiQuery) query).validate(beanDescriptor);
}
public Filter filter(Class beanType) {
BeanDescriptor desc = getBeanDescriptor(beanType);
if (desc == null) {
String m = beanType.getName() + " is NOT an Entity Bean registered with this server?";
throw new PersistenceException(m);
}
return new ElFilter<>(desc);
}
public CsvReader createCsvReader(Class beanType) {
BeanDescriptor descriptor = getBeanDescriptor(beanType);
if (descriptor == null) {
throw new NullPointerException("BeanDescriptor for " + beanType.getName() + " not found");
}
return new TCsvReader<>(this, descriptor);
}
public UpdateQuery update(Class beanType) {
return new DefaultUpdateQuery<>(createQuery(beanType));
}
public Query find(Class beanType) {
return createQuery(beanType);
}
@Override
public Query findNative(Class beanType, String nativeSql) {
BeanDescriptor desc = getBeanDescriptor(beanType);
if (desc == null) {
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
}
DefaultOrmQuery query = new DefaultOrmQuery<>(desc, this, expressionFactory);
query.setNativeSql(nativeSql);
return query;
}
@Override
public Query createNamedQuery(Class beanType, String namedQuery) {
BeanDescriptor desc = getBeanDescriptor(beanType);
if (desc == null) {
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
}
String named = desc.getNamedQuery(namedQuery);
if (named != null) {
return createQuery(beanType, named);
}
RawSql rawSql = desc.getNamedRawSql(namedQuery);
if (rawSql != null) {
DefaultOrmQuery query = createQuery(beanType);
query.setRawSql(rawSql);
return query;
}
throw new PersistenceException("No named query called " + namedQuery + " for bean:" + beanType.getName());
}
@Override
public Query createQuery(Class beanType, String eql) {
DefaultOrmQuery query = createQuery(beanType);
EqlParser.parse(eql, query);
return query;
}
public DefaultOrmQuery createQuery(Class beanType) {
BeanDescriptor desc = getBeanDescriptor(beanType);
if (desc == null) {
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
}
return new DefaultOrmQuery<>(desc, this, expressionFactory);
}
public Update createUpdate(Class beanType, String ormUpdate) {
BeanDescriptor> desc = getBeanDescriptor(beanType);
if (desc == null) {
String m = beanType.getName() + " is NOT an Entity Bean registered with this server?";
throw new PersistenceException(m);
}
return new DefaultOrmUpdate<>(beanType, this, desc.getBaseTable(), ormUpdate);
}
public SqlQuery createSqlQuery(String sql) {
return new DefaultRelationalQuery(this, sql);
}
public SqlUpdate createSqlUpdate(String sql) {
return new DefaultSqlUpdate(this, sql);
}
public CallableSql createCallableSql(String sql) {
return new DefaultCallableSql(this, sql);
}
public T find(Class beanType, Object uid) {
return find(beanType, uid, null);
}
/**
* Find a bean using its unique id.
*/
public T find(Class beanType, Object id, Transaction t) {
if (id == null) {
throw new NullPointerException("The id is null");
}
Query query = createQuery(beanType).setId(id);
return findId(query, t);
}
SpiOrmQueryRequest createQueryRequest(Type type, Query query, Transaction t) {
SpiQuery spiQuery = (SpiQuery) query;
spiQuery.setType(type);
spiQuery.checkNamedParameters();
return createQueryRequest(spiQuery, t);
}
private SpiOrmQueryRequest createQueryRequest(SpiQuery query, Transaction t) {
query.setDefaultRawSqlIfRequired();
if (query.isAutoTunable() && !autoTuneService.tuneQuery(query)) {
// use deployment FetchType.LAZY/EAGER annotations
// to define the 'default' select clause
query.setDefaultSelectClause();
}
query.selectAllForLazyLoadProperty();
// if determine cost and no origin for AutoTune
if (query.getParentNode() == null) {
query.setOrigin(createCallStack());
}
OrmQueryRequest request = new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
request.prepareQuery();
return request;
}
/**
* Try to get the object out of the persistence context.
*/
@SuppressWarnings("unchecked")
private T findIdCheckPersistenceContextAndCache(Transaction transaction, SpiQuery query, Object id) {
SpiTransaction t = (SpiTransaction) transaction;
if (t == null) {
t = getCurrentServerTransaction();
}
BeanDescriptor desc = query.getBeanDescriptor();
id = desc.convertId(id);
PersistenceContext pc = null;
if (t != null && useTransactionPersistenceContext(query)) {
// first look in the transaction scoped persistence context
pc = t.getPersistenceContext();
if (pc != null) {
WithOption o = desc.contextGetWithOption(pc, id);
if (o != null) {
if (o.isDeleted()) {
// Bean was previously deleted in the same transaction / persistence context
return null;
}
return (T) o.getBean();
}
}
}
if (!query.isUseBeanCache() || (t != null && t.isSkipCache())) {
return null;
}
// Hit the L2 bean cache
return desc.cacheBeanGet(id, query.isReadOnly(), pc);
}
/**
* Return true if transactions PersistenceContext should be used.
*/
private boolean useTransactionPersistenceContext(SpiQuery query) {
return PersistenceContextScope.TRANSACTION.equals(getPersistenceContextScope(query));
}
/**
* Return the PersistenceContextScope to use defined at query or server level.
*/
public PersistenceContextScope getPersistenceContextScope(SpiQuery> query) {
PersistenceContextScope scope = query.getPersistenceContextScope();
return (scope != null) ? scope : defaultPersistenceContextScope;
}
@SuppressWarnings("unchecked")
private T findId(Query query, Transaction t) {
SpiQuery spiQuery = (SpiQuery) query;
spiQuery.setType(Type.BEAN);
if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) {
// See if we can skip doing the fetch completely by getting the bean from the
// persistence context or the bean cache
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, spiQuery.getId());
if (bean != null) {
return bean;
}
}
SpiOrmQueryRequest request = createQueryRequest(spiQuery, t);
if (request.isUseDocStore()) {
return docStore().find(request);
}
try {
request.initTransIfRequired();
return (T) request.findId();
} finally {
request.endTransIfRequired();
}
}
public T findUnique(Query query, Transaction transaction) {
SpiQuery spiQuery = (SpiQuery) query;
spiQuery.checkIdEqualTo();
Object id = spiQuery.getId();
if (id != null) {
// actually a find by Id query
return findId(query, transaction);
}
SpiTransaction t = (SpiTransaction) transaction;
if (t == null) {
t = getCurrentServerTransaction();
}
if (t == null || !t.isSkipCache()) {
id = spiQuery.getBeanDescriptor().cacheNaturalKeyIdLookup(spiQuery);
if (id != null) {
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, id);
if (bean != null) {
return bean;
}
}
}
// a query that is expected to return either 0 or 1 beans
List list = findList(query, t);
return extractUnique(list);
}
private T extractUnique(List list) {
if (list.isEmpty()) {
return null;
} else if (list.size() > 1) {
throw new NonUniqueResultException("Unique expecting 0 or 1 results but got [" + list.size() + "]");
} else {
return list.get(0);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public Set findSet(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.SET, query, t);
Object result = request.getFromQueryCache();
if (result != null) {
return (Set) result;
}
try {
request.initTransIfRequired();
return request.findSet();
} finally {
request.endTransIfRequired();
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public Map findMap(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.MAP, query, t);
Object result = request.getFromQueryCache();
if (result != null) {
return (Map) result;
}
try {
request.initTransIfRequired();
return request.findMap();
} finally {
request.endTransIfRequired();
}
}
@Override
@SuppressWarnings("unchecked")
public List findSingleAttributeList(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.ATTRIBUTE, query, t);
Object result = request.getFromQueryCache();
if (result != null) {
return (List) result;
}
try {
request.initTransIfRequired();
return (List) request.findSingleAttributeList();
} finally {
request.endTransIfRequired();
}
}
public int findCount(Query query, Transaction t) {
SpiQuery copy = ((SpiQuery) query).copy();
return findCountWithCopy(copy, t);
}
public int findCountWithCopy(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.COUNT, query, t);
try {
request.initTransIfRequired();
return request.findCount();
} finally {
request.endTransIfRequired();
}
}
public List findIds(Query query, Transaction t) {
return findIdsWithCopy(((SpiQuery) query).copy(), t);
}
public List findIdsWithCopy(Query query, Transaction t) {
SpiOrmQueryRequest> request = createQueryRequest(Type.ID_LIST, query, t);
try {
request.initTransIfRequired();
return request.findIds();
} finally {
request.endTransIfRequired();
}
}
public int delete(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.DELETE, query, t);
try {
request.initTransIfRequired();
request.markNotQueryOnly();
return request.delete();
} finally {
request.endTransIfRequired();
}
}
public int update(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.UPDATE, query, t);
try {
request.initTransIfRequired();
request.markNotQueryOnly();
return request.update();
} finally {
request.endTransIfRequired();
}
}
public FutureRowCount findFutureCount(Query q, Transaction t) {
SpiQuery copy = ((SpiQuery) q).copy();
copy.setFutureFetch(true);
Transaction newTxn = createTransaction();
CallableQueryCount call = new CallableQueryCount<>(this, copy, newTxn);
QueryFutureRowCount queryFuture = new QueryFutureRowCount<>(call);
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
public FutureRowCount findFutureRowCount(Query q, Transaction t) {
return findFutureCount(q, t);
}
public FutureIds findFutureIds(Query query, Transaction t) {
SpiQuery copy = ((SpiQuery) query).copy();
copy.setFutureFetch(true);
Transaction newTxn = createTransaction();
CallableQueryIds call = new CallableQueryIds<>(this, copy, newTxn);
QueryFutureIds queryFuture = new QueryFutureIds<>(call);
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
public FutureList findFutureList(Query query, Transaction t) {
SpiQuery spiQuery = (SpiQuery) query;
spiQuery.setFutureFetch(true);
// FutureList query always run in it's own persistence content
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
if (!spiQuery.isDisableReadAudit()) {
BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType());
desc.readAuditFutureList(spiQuery);
}
// Create a new transaction solely to execute the findList() at some future time
Transaction newTxn = createTransaction();
CallableQueryList call = new CallableQueryList<>(this, spiQuery, newTxn);
QueryFutureList queryFuture = new QueryFutureList<>(call);
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@Override
public PagedList findPagedList(Query query, Transaction transaction) {
SpiQuery spiQuery = (SpiQuery) query;
int maxRows = spiQuery.getMaxRows();
if (maxRows == 0) {
throw new PersistenceException("maxRows must be specified for findPagedList() query");
}
if (spiQuery.isUseDocStore()) {
return docStore().findPagedList(createQueryRequest(Type.LIST, query, transaction));
}
return new LimitOffsetPagedList<>(this, spiQuery);
}
public QueryIterator findIterate(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t);
try {
request.initTransIfRequired();
return request.findIterate();
} catch (RuntimeException ex) {
request.endTransIfRequired();
throw ex;
}
}
public void findEach(Query query, Consumer consumer, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t);
if (request.isUseDocStore()) {
docStore().findEach(request, consumer);
return;
}
request.initTransIfRequired();
request.findEach(consumer);
// no try finally - findEach guarantee's cleanup of the transaction if required
}
public void findEachWhile(Query query, Predicate consumer, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t);
if (request.isUseDocStore()) {
docStore().findEachWhile(request, consumer);
return;
}
request.initTransIfRequired();
request.findEachWhile(consumer);
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
}
@Override
public List> findVersions(Query query, Transaction transaction) {
SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, transaction);
try {
request.initTransIfRequired();
return request.findVersions();
} finally {
request.endTransIfRequired();
}
}
@SuppressWarnings("unchecked")
public List findList(Query query, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t);
Object result = request.getFromQueryCache();
if (result != null) {
return (List) result;
}
if (request.isUseDocStore()) {
return docStore().findList(request);
}
try {
request.initTransIfRequired();
return request.findList();
} finally {
request.endTransIfRequired();
}
}
public SqlRow findUnique(SqlQuery query, Transaction t) {
// no findId() method for SqlQuery...
// a query that is expected to return either 0 or 1 rows
List list = findList(query, t);
return extractUnique(list);
}
@Override
public void findEach(SqlQuery query, Consumer consumer, Transaction transaction) {
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, transaction);
try {
request.initTransIfRequired();
request.findEach(consumer);
} finally {
request.endTransIfRequired();
}
}
@Override
public void findEachWhile(SqlQuery query, Predicate consumer, Transaction transaction) {
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, transaction);
try {
request.initTransIfRequired();
request.findEachWhile(consumer);
} finally {
request.endTransIfRequired();
}
}
public List findList(SqlQuery query, Transaction t) {
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
try {
request.initTransIfRequired();
return request.findList();
} finally {
request.endTransIfRequired();
}
}
/**
* Persist the bean by either performing an insert or update.
*/
public void save(Object bean) {
save(bean, null);
}
/**
* Save the bean with an explicit transaction.
*/
public void save(Object bean, Transaction t) {
persister.save(checkEntityBean(bean), t);
}
@Override
public void markAsDirty(Object bean) {
if (!(bean instanceof EntityBean)) {
throw new IllegalArgumentException("This bean is not an EntityBean?");
}
// mark the bean as dirty (so that an update will not get skipped)
((EntityBean) bean)._ebean_getIntercept().setDirty(true);
}
/**
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
public void update(Object bean) {
update(bean, null);
}
/**
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
public void update(Object bean, Transaction t) {
persister.update(checkEntityBean(bean), t);
}
/**
* Update the bean specifying the deleteMissingChildren option.
*/
public void update(Object bean, Transaction t, boolean deleteMissingChildren) {
persister.update(checkEntityBean(bean), t, deleteMissingChildren);
}
@Override
public void updateAll(Collection> beans) throws OptimisticLockException {
updateAll(beans, null);
}
/**
* Update all beans in the collection with an explicit transaction.
*/
public void updateAll(Collection> beans, Transaction t) {
if (beans == null || beans.isEmpty()) {
// Nothing to update?
return;
}
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
for (Object bean : beans) {
update(checkEntityBean(bean), trans);
}
wrap.commitIfCreated();
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
/**
* Insert the bean.
*/
public void insert(Object bean) {
insert(bean, null);
}
/**
* Insert the bean with a transaction.
*/
public void insert(Object bean, Transaction t) {
persister.insert(checkEntityBean(bean), t);
}
/**
* Insert all beans in the collection.
*/
public void insertAll(Collection> beans) {
insertAll(beans, null);
}
/**
* Insert all beans in the collection with a transaction.
*/
public void insertAll(Collection> beans, Transaction t) {
if (beans == null || beans.isEmpty()) {
// Nothing to insert?
return;
}
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
for (Object bean : beans) {
persister.insert(checkEntityBean(bean), trans);
}
wrap.commitIfCreated();
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
public List publish(Query query, Transaction transaction) {
TransWrapper wrap = initTransIfRequired(transaction);
try {
SpiTransaction trans = wrap.transaction;
List liveBeans = persister.publish(query, trans);
wrap.commitIfCreated();
return liveBeans;
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
@Override
public T publish(Class beanType, Object id) {
return publish(beanType, id, null);
}
@Override
public List publish(Query query) {
return publish(query, null);
}
@Override
public T publish(Class beanType, Object id, Transaction transaction) {
Query query = find(beanType).setId(id);
List liveBeans = publish(query, transaction);
return (liveBeans.size() == 1) ? liveBeans.get(0) : null;
}
@Override
public List draftRestore(Query query, Transaction transaction) {
TransWrapper wrap = initTransIfRequired(transaction);
try {
SpiTransaction trans = wrap.transaction;
List beans = persister.draftRestore(query, trans);
wrap.commitIfCreated();
return beans;
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
@Override
public T draftRestore(Class beanType, Object id, Transaction transaction) {
Query query = find(beanType).setId(id);
List beans = draftRestore(query, transaction);
return (beans.size() == 1) ? beans.get(0) : null;
}
@Override
public T draftRestore(Class beanType, Object id) {
return draftRestore(beanType, id, null);
}
@Override
public List draftRestore(Query query) {
return draftRestore(query, null);
}
private EntityBean checkEntityBean(Object bean) {
if (bean == null) {
throw new IllegalArgumentException(Message.msg("bean.isnull"));
}
if (!(bean instanceof EntityBean)) {
throw new IllegalArgumentException("Was expecting an EntityBean but got a " + bean.getClass());
}
return (EntityBean) bean;
}
@Override
public int saveAll(Collection> beans, Transaction transaction) throws OptimisticLockException {
return saveAllInternal(beans.iterator(), transaction);
}
@Override
public int saveAll(Collection> beans) throws OptimisticLockException {
return saveAllInternal(beans.iterator(), null);
}
/**
* Save all beans in the iterator with an explicit transaction.
*/
public int saveAllInternal(Iterator> it, Transaction t) {
TransWrapper wrap = initTransIfRequired(t);
try {
wrap.batchEscalateOnCollection();
SpiTransaction trans = wrap.transaction;
int saveCount = 0;
while (it.hasNext()) {
EntityBean bean = checkEntityBean(it.next());
persister.save(bean, trans);
saveCount++;
}
wrap.commitIfCreated();
wrap.flushBatchOnCollection();
return saveCount;
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
public int delete(Class> beanType, Object id) {
return delete(beanType, id, null);
}
public int delete(Class> beanType, Object id, Transaction t) {
return delete(beanType, id, t, false);
}
public int deletePermanent(Class> beanType, Object id) {
return delete(beanType, id, null, true);
}
public int deletePermanent(Class> beanType, Object id, Transaction t) {
return delete(beanType, id, t, true);
}
private int delete(Class> beanType, Object id, Transaction t, boolean permanent) {
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
int rowCount = persister.delete(beanType, id, trans, permanent);
wrap.commitIfCreated();
return rowCount;
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
@Override
public int deleteAll(Class> beanType, Collection> ids) {
return deleteAll(beanType, ids, null);
}
@Override
public int deleteAll(Class> beanType, Collection> ids, Transaction t) {
return deleteAll(beanType, ids, t, false);
}
@Override
public int deleteAllPermanent(Class> beanType, Collection> ids) {
return deleteAll(beanType, ids, null, true);
}
@Override
public int deleteAllPermanent(Class> beanType, Collection> ids, Transaction t) {
return deleteAll(beanType, ids, t, true);
}
private int deleteAll(Class> beanType, Collection> ids, Transaction t, boolean permanent) {
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
int count = persister.deleteMany(beanType, ids, trans, permanent);
wrap.commitIfCreated();
return count;
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
/**
* Delete the bean.
*/
public boolean delete(Object bean) {
return delete(bean, null);
}
/**
* Delete the bean with the explicit transaction.
*/
public boolean delete(Object bean, Transaction t) throws OptimisticLockException {
return persister.delete(checkEntityBean(bean), t, false);
}
@Override
public boolean deletePermanent(Object bean) throws OptimisticLockException {
return deletePermanent(bean, null);
}
@Override
public boolean deletePermanent(Object bean, Transaction t) throws OptimisticLockException {
return persister.delete(checkEntityBean(bean), t, true);
}
@Override
public int deleteAllPermanent(Collection> beans) {
return deleteAllInternal(beans.iterator(), null, true);
}
@Override
public int deleteAllPermanent(Collection> beans, Transaction t) {
return deleteAllInternal(beans.iterator(), t, true);
}
/**
* Delete all the beans in the collection.
*/
@Override
public int deleteAll(Collection> beans) {
return deleteAllInternal(beans.iterator(), null, false);
}
/**
* Delete all the beans in the collection.
*/
@Override
public int deleteAll(Collection> beans, Transaction t) {
return deleteAllInternal(beans.iterator(), t, false);
}
/**
* Delete all the beans in the iterator with an explicit transaction.
*/
private int deleteAllInternal(Iterator> it, Transaction t, boolean permanent) {
TransWrapper wrap = initTransIfRequired(t);
try {
wrap.batchEscalateOnCollection();
SpiTransaction trans = wrap.transaction;
int deleteCount = 0;
while (it.hasNext()) {
EntityBean bean = checkEntityBean(it.next());
persister.delete(bean, trans, permanent);
deleteCount++;
}
wrap.commitIfCreated();
wrap.flushBatchOnCollection();
return deleteCount;
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
/**
* Execute the CallableSql with an explicit transaction.
*/
public int execute(CallableSql callSql, Transaction t) {
return persister.executeCallable(callSql, t);
}
/**
* Execute the CallableSql.
*/
public int execute(CallableSql callSql) {
return execute(callSql, null);
}
/**
* Execute the updateSql with an explicit transaction.
*/
public int execute(SqlUpdate updSql, Transaction t) {
return persister.executeSqlUpdate(updSql, t);
}
/**
* Execute the updateSql.
*/
public int execute(SqlUpdate updSql) {
return execute(updSql, null);
}
/**
* Execute the updateSql with an explicit transaction.
*/
public int execute(Update> update, Transaction t) {
return persister.executeOrmUpdate(update, t);
}
/**
* Execute the orm update.
*/
public int execute(Update> update) {
return execute(update, null);
}
/**
* Return all the BeanDescriptors.
*/
public List> getBeanDescriptors() {
return beanDescriptorManager.getBeanDescriptorList();
}
public void register(BeanPersistController c) {
List> list = beanDescriptorManager.getBeanDescriptorList();
for (BeanDescriptor> aList : list) {
aList.register(c);
}
}
public void deregister(BeanPersistController c) {
List> list = beanDescriptorManager.getBeanDescriptorList();
for (BeanDescriptor> aList : list) {
aList.deregister(c);
}
}
public boolean isSupportedType(java.lang.reflect.Type genericType) {
TypeInfo typeInfo = ParamTypeHelper.getTypeInfo(genericType);
return typeInfo != null && getBeanDescriptor(typeInfo.getBeanType()) != null;
}
@Override
public Object setBeanId(Object bean, Object id) {
EntityBean eb = checkEntityBean(bean);
BeanDescriptor> desc = getBeanDescriptor(bean.getClass());
if (desc == null) {
throw new PersistenceException(bean.getClass().getName() + " is NOT an Entity Bean registered with this server?");
}
return desc.convertSetId(id, eb);
}
@Override
public Object getBeanId(Object bean) {
EntityBean eb = checkEntityBean(bean);
BeanDescriptor> desc = getBeanDescriptor(bean.getClass());
if (desc == null) {
throw new PersistenceException(bean.getClass().getName() + " is NOT an Entity Bean registered with this server?");
}
return desc.getId(eb);
}
/**
* Return the BeanDescriptor for a given type of bean.
*/
public BeanDescriptor getBeanDescriptor(Class beanClass) {
return beanDescriptorManager.getBeanDescriptor(beanClass);
}
/**
* Return the BeanDescriptor's for a given table name.
*/
public List> getBeanDescriptors(String tableName) {
return beanDescriptorManager.getBeanDescriptors(tableName);
}
/**
* Return all the SPI BeanTypes.
*/
public List extends BeanType>> getBeanTypes() {
return getBeanDescriptors();
}
/**
* Return the SPI bean types mapped to the given table.
*/
public List extends BeanType>> getBeanTypes(String tableName) {
return beanDescriptorManager.getBeanTypes(tableName);
}
@Override
public BeanType> getBeanTypeForQueueId(String queueId) {
return getBeanDescriptorByQueueId(queueId);
}
@Override
public BeanDescriptor> getBeanDescriptorByQueueId(String queueId) {
return beanDescriptorManager.getBeanDescriptorByQueueId(queueId);
}
/**
* Return the SPI bean types for the given bean class.
*/
@Override
public BeanType getBeanType(Class beanType) {
return getBeanDescriptor(beanType);
}
/**
* Return the BeanDescriptor using its class name.
*/
public BeanDescriptor> getBeanDescriptorById(String beanClassName) {
return beanDescriptorManager.getBeanDescriptorByClassName(beanClassName);
}
/**
* Another server in the cluster sent this event so that we can inform local
* BeanListeners of inserts updates and deletes that occurred remotely (on
* another server in the cluster).
*/
public void remoteTransactionEvent(RemoteTransactionEvent event) {
transactionManager.remoteTransactionEvent(event);
}
/**
* Create a transaction if one is not currently active in the
* TransactionThreadLocal.
*
* Returns a TransWrapper which contains the wasCreated flag. If this is true
* then the transaction was created for this request in which case it will
* need to be committed after the request has been processed.
*
*/
TransWrapper initTransIfRequired(Transaction t) {
if (t != null) {
return new TransWrapper((SpiTransaction) t, false);
}
boolean wasCreated = false;
SpiTransaction trans = transactionScopeManager.get();
if (trans == null) {
// create a transaction
trans = transactionManager.createTransaction(false, -1);
wasCreated = true;
}
return new TransWrapper(trans, wasCreated);
}
public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel) {
return transactionManager.createTransaction(isExplicit, isolationLevel);
}
public SpiTransaction createQueryTransaction(Object tenantId) {
return transactionManager.createQueryTransaction(tenantId);
}
/**
* Create a CallStack object.
*
* This trims off the avaje ebean part of the stack trace so that the first
* element in the CallStack should be application code.
*
*/
public CallStack createCallStack() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// ignore the first 6 as they are always avaje stack elements
int startIndex = IGNORE_LEADING_ELEMENTS;
// find the first non-avaje stackElement
for (; startIndex < stackTrace.length; startIndex++) {
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
break;
}
}
int stackLength = stackTrace.length - startIndex;
if (stackLength > maxCallStack) {
// maximum of maxCallStack stackTrace elements
stackLength = maxCallStack;
}
// create the 'interesting' part of the stackTrace
StackTraceElement[] finalTrace = new StackTraceElement[stackLength];
System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength);
if (stackLength < 1) {
// this should not really happen
throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace));
}
return callStackFactory.createCallStack(finalTrace);
}
@Override
public DocumentStore docStore() {
return documentStore;
}
@Override
public JsonContext json() {
// immutable thread safe so return shared instance
return jsonContext;
}
@Override
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
if (collectQueryStatsByNode) {
CObjectGraphNodeStatistics nodeStatistics = objectGraphStats.get(node);
if (nodeStatistics == null) {
// race condition here but I actually don't care too much if we miss a
// few early statistics - especially when the server is warming up etc
nodeStatistics = new CObjectGraphNodeStatistics(node);
objectGraphStats.put(node, nodeStatistics);
}
nodeStatistics.add(loadedBeanCount, timeMicros);
}
}
}