Refactor transaction logging to simple SLF4J use

This commit is contained in:
Robin Bygrave
2013-04-15 23:16:08 +12:00
parent b5ef07bab9
commit cbd7b5e912
60 changed files with 2976 additions and 4529 deletions
@@ -6,7 +6,6 @@ import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer;
/**
* Extends Transaction with additional API required on server.
@@ -16,6 +15,11 @@ import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer;
*/
public interface SpiTransaction extends Transaction {
/**
* Return the string prefix with the transactin id and label used in logging.
*/
public String getLogPrefix();
/**
* Return true if generated SQL and Bind values should be logged to the
* transaction log.
@@ -29,15 +33,14 @@ public interface SpiTransaction extends Transaction {
public boolean isLogSummary();
/**
* Log a comment to the transaction log for Ebean INTERNAL use. There should
* always be an external LogLevel check prior to calling this method.
* Log a message to the SQL logger.
*/
public void logInternal(String msg);
public void logSql(String msg);
/**
* Return the buffer containing transaction log messages.
* Log a message to the SUMMARY logger.
*/
public TransactionLogBuffer getLogBuffer();
public void logSummary(String msg);
/**
* Register a "Derived Relationship" (that requires an additional update).
@@ -69,8 +69,6 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
private transient boolean queryTuningAddVersion;
private transient AutofetchMode mode;
private transient boolean useFileLogging;
/**
* Server that owns this Profile Listener.
@@ -95,7 +93,6 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
AutofetchConfig autofetchConfig = serverConfig.getAutofetchConfig();
useFileLogging = autofetchConfig.isUseFileLogging();
queryTuning = autofetchConfig.isQueryTuning();
queryTuningAddVersion = autofetchConfig.isQueryTuningAddVersion();
profiling = autofetchConfig.isProfiling();
@@ -116,7 +113,7 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
String msg = "AutoFetch queryTuning[" + queryTuning + "] profiling[" + profiling
+ "] mode[" + mode + "] profiling rate[" + profilingRate
+ "] min[" + profilingMin + "] base[" + profilingBase + "]";
logging.logToJavaLogger(msg);
logging.logInfo(msg, null);
}
}
@@ -272,10 +269,10 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
* </p>
*/
public void shutdown() {
if (useFileLogging) {
//if (useFileLogging) {
collectUsageViaGC(-1);
serialize();
}
//}
}
/**
@@ -1,15 +1,13 @@
package com.avaje.ebeaninternal.server.autofetch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.AutofetchConfig;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.lib.BackgroundThread;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.transaction.log.SimpleLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.logging.Level;
/**
* Handles the logging aspects for the DefaultAutoFetchListener.
@@ -22,12 +20,8 @@ public class DefaultAutoFetchManagerLogging {
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class);
private final SimpleLogger fileLogger;
private final DefaultAutoFetchManager manager;
private final boolean useFileLogger;
private final boolean traceUsageCollection;
public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) {
@@ -37,21 +31,8 @@ public class DefaultAutoFetchManagerLogging {
AutofetchConfig autofetchConfig = serverConfig.getAutofetchConfig();
traceUsageCollection = GlobalProperties.getBoolean("ebean.autofetch.traceUsageCollection", false);
useFileLogger = autofetchConfig.isUseFileLogging();
if (!useFileLogger) {
fileLogger = null;
} else {
// a separate log file just like the transaction logging
// for putting the profiling log messages. The benefit is that
// this doesn't pollute the main log with heaps of messages.
String baseDir = serverConfig.getLoggingDirectoryWithEval();
fileLogger = new SimpleLogger(baseDir, "autofetch", true, "csv");
}
int updateFreqInSecs = autofetchConfig.getProfileUpdateFrequency();
BackgroundThread.add(updateFreqInSecs, new UpdateProfile());
}
@@ -61,63 +42,30 @@ public class DefaultAutoFetchManagerLogging {
}
}
private void logFile(String msg, Throwable e) {
if (useFileLogger) {
String errMsg = e == null ? "" : e.getMessage();
fileLogger.log("\"Error\",\"" + msg+" "+errMsg+"\",,,,");
}
}
public void logInfo(String msg, Throwable e) {
logFile(msg, e);
logger.info(msg, e);
}
public void logError(String msg, Throwable e) {
logFile(msg, e);
logger.error(msg, e);
}
@Deprecated
public void logError(Level level, String msg, Throwable e) {
logError(msg, e);
}
public void logToJavaLogger(String msg) {
logger.info(msg);
}
public void logSummary(String summaryInfo) {
String msg = "\"Summary\",\""+summaryInfo+"\",,,,";
if (useFileLogger) {
fileLogger.log(msg);
}
String msg = "\"Summary\",\""+summaryInfo+"\",,,,";
logger.debug(msg);
}
public void logChanged(TunedQueryInfo tunedFetch, OrmQueryDetail newQueryDetail) {
String msg = tunedFetch.getLogOutput(newQueryDetail);
if (useFileLogger) {
fileLogger.log(msg);
} else {
logger.debug(msg);
}
logger.debug(msg);
}
public void logNew(TunedQueryInfo tunedFetch) {
String msg = tunedFetch.getLogOutput(null);
if (useFileLogger) {
fileLogger.log(msg);
} else {
logger.debug(msg);
}
logger.debug(msg);
}
public boolean isTraceUsageCollection() {
@@ -30,18 +30,11 @@ public class OutgoingPacketsAcked {
private boolean resetGotAllMin() {
long tempMin;
if (recievedByMap.isEmpty()){
//System.out.println(" -- -- -- -- "+recievedByMap.isEmpty());
tempMin = Long.MAX_VALUE;
} else {
tempMin = Long.MAX_VALUE;
}
long tempMin = Long.MAX_VALUE;
for (GroupMemberAck groupMemAck : recievedByMap.values()) {
long memberMin = groupMemAck.getGotAllPacketId();
if (memberMin < tempMin){
//System.out.println(" -- new tmpMin "+memberMin);
if (memberMin < tempMin) {
tempMin = memberMin;
}
}
@@ -62,24 +55,19 @@ public class OutgoingPacketsAcked {
GroupMemberAck groupMemberAck = recievedByMap.get(groupMember);
if (groupMemberAck == null) {
//System.out.println(" -- new groupMemberAck");
groupMemberAck = new GroupMemberAck();
groupMemberAck.setIfBigger(ack.getGotAllPacketId());
recievedByMap.put(groupMember, groupMemberAck);
checkMin = true;
} else {
checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId;
//System.out.println(" -- existing groupMemberAck, checkMin:"+checkMin+" "+groupMemberAck.getGotAllPacketId());
groupMemberAck.setIfBigger(ack.getGotAllPacketId());
}
boolean minChanged = false;
//System.out.println(" -- checkMin:"+checkMin+" minimumGotAllPacketId:"+minimumGotAllPacketId);
if (checkMin || minimumGotAllPacketId == 0){
if (checkMin || minimumGotAllPacketId == 0){
minChanged = resetGotAllMin();
//System.out.println(" -- minChanged:"+minChanged+" minimumGotAllPacketId:"+minimumGotAllPacketId);
}
return minChanged ? minimumGotAllPacketId : 0;
@@ -5,6 +5,9 @@ import java.util.List;
import javax.persistence.EntityNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.BeanCollection;
@@ -21,8 +24,6 @@ import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper to handle lazy loading and refreshing of beans.
@@ -164,21 +165,6 @@ public class DefaultBeanLoader {
ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
loadManyInternal(parentBean, propertyName, null, false, node, onlyIds);
if (server.getAdminLogging().isDebugLazyLoad()) {
Class<?> cls = parentBean.getClass();
BeanDescriptor<?> desc = server.getBeanDescriptor(cls);
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) desc.getBeanProperty(propertyName);
StackTraceElement cause = debugLazyLoad.getStackTraceElement(cls);
String msg = "debug.lazyLoad " + many.getManyType() + " [" + desc + "][" + propertyName + "]";
if (cause != null) {
msg += " at: " + cause;
}
System.err.println(msg);
}
}
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
@@ -13,14 +13,16 @@ import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.FutureTask;
import java.util.ServiceLoader;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.AdminAutofetch;
import com.avaje.ebean.AdminLogging;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.BeanState;
import com.avaje.ebean.CallableSql;
@@ -108,8 +110,6 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.util.ParamTypeHelper;
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The default server side implementation of EbeanServer.
@@ -122,8 +122,6 @@ public final class DefaultServer implements SpiEbeanServer {
private final DatabasePlatform databasePlatform;
private final AdminLogging adminLogging;
private final AdminAutofetch adminAutofetch;
private final TransactionManager transactionManager;
@@ -224,7 +222,6 @@ public final class DefaultServer implements SpiEbeanServer {
this.queryBatchSize = config.getServerConfig().getQueryBatchSize();
this.cqueryEngine = config.getCQueryEngine();
this.expressionFactory = config.getExpressionFactory();
this.adminLogging = config.getLogControl();
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
this.beanDescriptorManager = config.getBeanDescriptorManager();
@@ -321,10 +318,6 @@ public final class DefaultServer implements SpiEbeanServer {
return ddlGenerator;
}
public AdminLogging getAdminLogging() {
return adminLogging;
}
public AdminAutofetch getAdminAutofetch() {
return adminAutofetch;
}
@@ -358,11 +351,9 @@ public final class DefaultServer implements SpiEbeanServer {
this.mbeanServer = mbeanServer;
this.mbeanName = "Ebean:server=" + serverName + uniqueServerId;
ObjectName adminName;
ObjectName autofethcName;
ObjectName autofetchName;
try {
adminName = new ObjectName(mbeanName + ",function=Logging");
autofethcName = new ObjectName(mbeanName + ",key=AutoFetch");
autofetchName = new ObjectName(mbeanName + ",key=AutoFetch");
} catch (Exception e) {
String msg = "Failed to register the JMX beans for Ebean server [" + serverName + "].";
logger.error(msg, e);
@@ -370,19 +361,15 @@ public final class DefaultServer implements SpiEbeanServer {
}
try {
mbeanServer.registerMBean(adminLogging, adminName);
mbeanServer.registerMBean(adminAutofetch, autofethcName);
mbeanServer.registerMBean(adminAutofetch, autofetchName);
} catch (InstanceAlreadyExistsException e) {
// tomcat webapp reloading
String msg = "JMX beans for Ebean server [" + serverName + "] already registered. Will try unregister/register" + e.getMessage();
logger.warn(msg);
try {
mbeanServer.unregisterMBean(adminName);
mbeanServer.unregisterMBean(autofethcName);
// re-register
mbeanServer.registerMBean(adminLogging, adminName);
mbeanServer.registerMBean(adminAutofetch, autofethcName);
mbeanServer.unregisterMBean(autofetchName);
mbeanServer.registerMBean(adminAutofetch, autofetchName);
} catch (Exception ae) {
String amsg = "Unable to unregister/register the JMX beans for Ebean server [" + serverName + "].";
@@ -398,7 +385,6 @@ public final class DefaultServer implements SpiEbeanServer {
public void run() {
try {
if (mbeanServer != null) {
mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",function=Logging"));
mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",key=AutoFetch"));
}
} catch (Exception e) {
@@ -674,16 +660,6 @@ public final class DefaultServer implements SpiEbeanServer {
return transactionManager.createTransaction(true, isolation.getLevel());
}
/**
* Log a comment to the transaction log (of the current transaction).
*/
public void logComment(String msg) {
Transaction t = transactionScopeManager.get();
if (t != null) {
t.log(msg);
}
}
public <T> T execute(TxCallable<T> c) {
return execute(null, c);
}
@@ -341,14 +341,6 @@ public class DefaultServerFactory implements BootupEbeanManager {
return bootupClassSearch.getBootupClasses().createCopy();
}
/**
* Execute the DDL if required.
*/
private void executeDDL(SpiEbeanServer server, boolean online) {
server.getDdlGenerator().execute(online);
}
/**
* Set the naming convention to underscore if it has not already been set.
*/
@@ -1,5 +1,8 @@
package com.avaje.ebeaninternal.server.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.ExternalTransactionManager;
@@ -19,7 +22,6 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.jmx.MAdminLogging;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -37,8 +39,6 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
import com.avaje.ebeaninternal.server.type.TypeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Used to extend the ServerConfig with additional objects used to configure and
@@ -72,8 +72,6 @@ public class InternalConfiguration {
private final BeanDescriptorManager beanDescriptorManager;
private final MAdminLogging logControl;
private final DebugLazyLoad debugLazyLoad;
private final TransactionManager transactionManager;
@@ -94,8 +92,9 @@ public class InternalConfiguration {
private final XmlConfig xmlConfig;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager, ServerCacheManager cacheManager,
SpiBackgroundExecutor backgroundExecutor, ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
this.xmlConfig = xmlConfig;
this.pstmtBatch = pstmtBatch;
@@ -123,20 +122,21 @@ public class InternalConfiguration {
this.debugLazyLoad = new DebugLazyLoad(serverConfig.isDebugLazyLoad());
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager,
this.getBootupClasses());
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
serverConfig, beanDescriptorManager, this.getBootupClasses());
this.logControl = new MAdminLogging(serverConfig, transactionManager);
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder,
backgroundExecutor);
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), logControl, binder, backgroundExecutor);
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
ExternalTransactionManager externalTransactionManager = serverConfig
.getExternalTransactionManager();
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
externalTransactionManager = new JtaTransactionManager();
}
if (externalTransactionManager != null) {
externalTransactionManager.setTransactionManager(transactionManager);
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager,
externalTransactionManager);
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
} else {
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
@@ -167,7 +167,7 @@ public class InternalConfiguration {
}
public RelationalQueryEngine createRelationalQueryEngine() {
return new DefaultRelationalQueryEngine(logControl, binder, serverConfig.getDatabaseBooleanTrue());
return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue());
}
public OrmQueryEngine createOrmQueryEngine() {
@@ -175,7 +175,7 @@ public class InternalConfiguration {
}
public Persister createPersister(SpiEbeanServer server) {
return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch);
}
@@ -239,10 +239,6 @@ public class InternalConfiguration {
return deployUtil;
}
public MAdminLogging getLogControl() {
return logControl;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
@@ -403,7 +403,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
public void logSql(String sql) {
if (transaction.isLogSql()) {
transaction.logInternal(sql);
transaction.logSql(sql);
}
}
@@ -576,13 +576,13 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
String name = beanDescriptor.getName();
switch (type) {
case INSERT:
transaction.logInternal("Inserted [" + name + "] [" + idValue + "]");
transaction.logSummary("Inserted [" + name + "] [" + idValue + "]");
break;
case UPDATE:
transaction.logInternal("Updated [" + name + "] [" + idValue + "]");
transaction.logSummary("Updated [" + name + "] [" + idValue + "]");
break;
case DELETE:
transaction.logInternal("Deleted [" + name + "] [" + idValue + "]");
transaction.logSummary("Deleted [" + name + "] [" + idValue + "]");
break;
default:
break;
@@ -90,7 +90,7 @@ public final class PersistRequestCallableSql extends PersistRequest {
if (transaction.isLogSummary()) {
String m = "CallableSql label[" + callableSql.getLabel() + "]" + " rows[" + rowCount+ "]" + " bind[" + bindLog + "]";
transaction.logInternal(m);
transaction.logSummary(m);
}
// register table modifications with the transaction event
@@ -93,7 +93,7 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
if (transaction.isLogSummary()) {
String m = ormUpdateType + " table[" + tableName + "] rows["+ rowCount + "] bind[" + bindLog + "]";
transaction.logInternal(m);
transaction.logSummary(m);
}
if (ormUpdate.isNotifyCache()) {
@@ -100,7 +100,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
if (transaction.isLogSummary()) {
String m = description + " table[" + tableName + "] rows["+ rowCount + "] bind[" + bindLog + "]";
transaction.logInternal(m);
transaction.logSummary(m);
}
if (updateSql.isAutoTableMod()) {
@@ -6,7 +6,6 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintStream;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -16,14 +15,15 @@ import java.util.List;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.SpiEbeanPlugin;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Controls the generation of DDL and potentially runs the resulting scripts.
@@ -1,116 +1,115 @@
package com.avaje.ebeaninternal.server.deploy;
/**
* Used to provide context during sql construction.
*/
public interface DbSqlContext {
/**
* Add a join to the sql query.
*/
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
/**
* Add a join to the sql query.
*/
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
public void pushSecondaryTableAlias(String alias);
public void pushSecondaryTableAlias(String alias);
/**
* Push the current table alias onto the stack.
*/
public void pushTableAlias(String tableAlias);
/**
* Push the current table alias onto the stack.
*/
public void pushTableAlias(String tableAlias);
/**
* Pop the current table alias from the stack.
*/
public void popTableAlias();
/**
* Pop the current table alias from the stack.
*/
public void popTableAlias();
/**
* Add an encrypted property which will require additional binding.
*/
public void addEncryptedProp(BeanProperty prop);
/**
* Add an encrypted property which will require additional binding.
*/
public void addEncryptedProp(BeanProperty prop);
/**
* Return a list of encrypted properties which require additional binding.
*/
public BeanProperty[] getEncryptedProps();
/**
* Return a list of encrypted properties which require additional binding.
*/
public BeanProperty[] getEncryptedProps();
/**
* Append a char directly to the SQL buffer.
*/
public DbSqlContext append(char s);
/**
* Append a char directly to the SQL buffer.
*/
public DbSqlContext append(char s);
/**
* Append a string directly to the SQL buffer.
*/
public DbSqlContext append(String s);
/**
* Append a string directly to the SQL buffer.
*/
public DbSqlContext append(String s);
/**
* Peek the current table alias.
*/
public String peekTableAlias();
/**
* Peek the current table alias.
*/
public String peekTableAlias();
/**
* Add a raw column to the sql.
*/
public void appendRawColumn(String rawcolumnWithTableAlias);
/**
* Add a raw column to the sql.
*/
public void appendRawColumn(String rawcolumnWithTableAlias);
/**
* Append a column with an explicit table alias.
*/
public void appendColumn(String tableAlias, String column);
/**
* Append a column with an explicit table alias.
*/
public void appendColumn(String tableAlias, String column);
/**
* Append a column with the current table alias.
*/
public void appendColumn(String column);
/**
* Append a column with the current table alias.
*/
public void appendColumn(String column);
/**
* Append a Sql Formula select. This converts the "${ta}" keyword to the
* current table alias.
*/
public void appendFormulaSelect(String sqlFormulaSelect);
/**
* Append a Sql Formula select. This converts the "${ta}" keyword to the
* current table alias.
*/
public void appendFormulaSelect(String sqlFormulaSelect);
/**
* Append a Sql Formula join. This converts the "${ta}" keyword to the
* current table alias.
*/
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin);
/**
* Append a Sql Formula join. This converts the "${ta}" keyword to the current
* table alias.
*/
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin);
/**
* Return the current content length.
*/
public int length();
/**
* Return the current content length.
*/
public int length();
/**
* Return the current context of the sql context.
*/
public String getContent();
/**
* Return the current context of the sql context.
*/
public String getContent();
/**
* Return the current join node.
*/
public String peekJoin();
/**
* Return the current join node.
*/
public String peekJoin();
/**
* Push a join node onto the stack.
*/
public void pushJoin(String prefix);
/**
* Push a join node onto the stack.
*/
public void pushJoin(String prefix);
/**
* Pop a join node off the stack.
*/
public void popJoin();
/**
* Pop a join node off the stack.
*/
public void popJoin();
/**
* Return a table alias without many where clause joins.
* Typically this is for the select clause (fetch joins).
*/
public String getTableAlias(String prefix);
/**
* Return a table alias without many where clause joins. Typically this is for
* the select clause (fetch joins).
*/
public String getTableAlias(String prefix);
/**
* Return a table alias that takes into account many where joins.
*/
public String getTableAliasManyWhere(String prefix);
/**
* Return a table alias that takes into account many where joins.
*/
public String getTableAliasManyWhere(String prefix);
public String getRelativePrefix(String propName);
public String getRelativePrefix(String propName);
}
@@ -15,194 +15,189 @@ import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
*/
public final class TableJoin {
public static final String NEW_LINE = "\n";
public static final String LEFT_OUTER = "left outer join";
public static final String LEFT_OUTER = "left outer join";
public static final String JOIN = "join";
/**
* Flag set when the imported key maps to the primary key.
* This occurs for intersection tables (ManyToMany).
*/
private final boolean importedPrimaryKey;
/**
* The joined table.
*/
private final String table;
/**
* The type of join. LEFT OUTER etc.
*/
private final String type;
public static final String JOIN = "join";
/**
* The persist cascade info.
*/
private final BeanCascadeInfo cascadeInfo;
/**
* Properties as an array.
*/
private final BeanProperty[] properties;
/**
* Columns as an array.
*/
private final TableJoinColumn[] columns;
/**
* Flag set when the imported key maps to the primary key. This occurs for
* intersection tables (ManyToMany).
*/
private final boolean importedPrimaryKey;
/**
* Create a TableJoin.
*/
public TableJoin(DeployTableJoin deploy, LinkedHashMap<String,BeanProperty> propMap) {
this.importedPrimaryKey = deploy.isImportedPrimaryKey();
this.table = InternString.intern(deploy.getTable());
this.type = InternString.intern(deploy.getType());
this.cascadeInfo = deploy.getCascadeInfo();
DeployTableJoinColumn[] deployCols = deploy.columns();
this.columns = new TableJoinColumn[deployCols.length];
for (int i = 0; i < deployCols.length; i++) {
this.columns[i] = new TableJoinColumn(deployCols[i]);
}
DeployBeanProperty[] deployProps = deploy.properties();
if (deployProps.length > 0 && propMap == null){
throw new NullPointerException("propMap is null?");
}
this.properties = new BeanProperty[deployProps.length];
for (int i = 0; i < deployProps.length; i++) {
BeanProperty prop = propMap.get(deployProps[i].getName());
this.properties[i] = prop;
}
/**
* The joined table.
*/
private final String table;
/**
* The type of join. LEFT OUTER etc.
*/
private final String type;
/**
* The persist cascade info.
*/
private final BeanCascadeInfo cascadeInfo;
/**
* Properties as an array.
*/
private final BeanProperty[] properties;
/**
* Columns as an array.
*/
private final TableJoinColumn[] columns;
/**
* Create a TableJoin.
*/
public TableJoin(DeployTableJoin deploy, LinkedHashMap<String, BeanProperty> propMap) {
this.importedPrimaryKey = deploy.isImportedPrimaryKey();
this.table = InternString.intern(deploy.getTable());
this.type = InternString.intern(deploy.getType());
this.cascadeInfo = deploy.getCascadeInfo();
DeployTableJoinColumn[] deployCols = deploy.columns();
this.columns = new TableJoinColumn[deployCols.length];
for (int i = 0; i < deployCols.length; i++) {
this.columns[i] = new TableJoinColumn(deployCols[i]);
}
/**
* Create a tableJoin based on this object but with different alias.
*/
public TableJoin createWithAlias(String localAlias, String foreignAlias) {
return new TableJoin(this, localAlias, foreignAlias);
}
/**
* Construct a copy but with different table alias'.
*/
private TableJoin(TableJoin join, String localAlias, String foreignAlias){
// copy the immutable fields
this.importedPrimaryKey = join.importedPrimaryKey;
this.table = join.table;
this.type = join.type;
this.cascadeInfo = join.cascadeInfo;
this.properties = join.properties;
this.columns = join.columns;
}
public String toString() {
StringBuilder sb = new StringBuilder(30);
sb.append(type).append(" ").append(table).append(" ");
for (int i = 0; i < columns.length; i++) {
sb.append(columns[i]).append(" ");
}
return sb.toString();
DeployBeanProperty[] deployProps = deploy.properties();
if (deployProps.length > 0 && propMap == null) {
throw new NullPointerException("propMap is null?");
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].appendSelect(ctx, subQuery);
}
}
public void load(SqlBeanLoad sqlBeanLoad) throws SQLException {
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].load(sqlBeanLoad);
}
}
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].readSet(ctx, bean, type);
}
return null;
}
/**
* Return true if the imported foreign key maps to the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
/**
* Return the persist info.
*/
public BeanCascadeInfo getCascadeInfo() {
return cascadeInfo;
this.properties = new BeanProperty[deployProps.length];
for (int i = 0; i < deployProps.length; i++) {
BeanProperty prop = propMap.get(deployProps[i].getName());
this.properties[i] = prop;
}
/**
* Return the join columns.
*/
public TableJoinColumn[] columns() {
return columns;
}
}
/**
* For secondary table joins returns the properties mapped to that table.
*/
public BeanProperty[] properties() {
return properties;
}
/**
* Create a tableJoin based on this object but with different alias.
*/
public TableJoin createWithAlias(String localAlias, String foreignAlias) {
/**
* Return the joined table name.
*/
public String getTable() {
return table;
}
return new TableJoin(this, localAlias, foreignAlias);
}
/**
* Return the type of join. LEFT OUTER JOIN etc.
*/
public String getType() {
return type;
}
/**
* Construct a copy but with different table alias'.
*/
private TableJoin(TableJoin join, String localAlias, String foreignAlias) {
/**
* Return true if this join is a left outer join.
*/
public boolean isOuterJoin() {
return type.equals(LEFT_OUTER);
}
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
// copy the immutable fields
this.importedPrimaryKey = join.importedPrimaryKey;
this.table = join.table;
this.type = join.type;
this.cascadeInfo = join.cascadeInfo;
this.properties = join.properties;
this.columns = join.columns;
}
String[] names = SplitName.split(prefix);
String a1 = ctx.getTableAlias(names[0]);
String a2 = ctx.getTableAlias(prefix);
public String toString() {
StringBuilder sb = new StringBuilder(30);
sb.append(type).append(" ").append(table).append(" ");
for (int i = 0; i < columns.length; i++) {
sb.append(columns[i]).append(" ");
}
return sb.toString();
}
return addJoin(forceOuterJoin, a1, a2, ctx);
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].appendSelect(ctx, subQuery);
}
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2);
return forceOuterJoin || LEFT_OUTER.equals(type);
}
public void load(SqlBeanLoad sqlBeanLoad) throws SQLException {
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].load(sqlBeanLoad);
}
/**
* Explicitly add a (non-outer) join.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
ctx.addJoin(JOIN, table, columns(), a1, a2);
}
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].readSet(ctx, bean, type);
}
return null;
}
/**
* Return true if the imported foreign key maps to the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
/**
* Return the persist info.
*/
public BeanCascadeInfo getCascadeInfo() {
return cascadeInfo;
}
/**
* Return the join columns.
*/
public TableJoinColumn[] columns() {
return columns;
}
/**
* For secondary table joins returns the properties mapped to that table.
*/
public BeanProperty[] properties() {
return properties;
}
/**
* Return the joined table name.
*/
public String getTable() {
return table;
}
/**
* Return the type of join. LEFT OUTER JOIN etc.
*/
public String getType() {
return type;
}
/**
* Return true if this join is a left outer join.
*/
public boolean isOuterJoin() {
return type.equals(LEFT_OUTER);
}
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
String[] names = SplitName.split(prefix);
String a1 = ctx.getTableAlias(names[0]);
String a2 = ctx.getTableAlias(prefix);
return addJoin(forceOuterJoin, a1, a2, ctx);
}
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
ctx.addJoin(forceOuterJoin ? LEFT_OUTER : type, table, columns(), a1, a2);
return forceOuterJoin || LEFT_OUTER.equals(type);
}
/**
* Explicitly add a (non-outer) join.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
ctx.addJoin(JOIN, table, columns(), a1, a2);
}
}
@@ -1,56 +0,0 @@
package com.avaje.ebeaninternal.server.jmx;
import com.avaje.ebean.AdminLogging;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.LogLevel;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
/**
* Implementation of the LogControl.
* <p>
* This is accessible via {@link EbeanServer#getAdminLogging()} or via JMX MBean.
* </p>
*/
public class MAdminLogging implements MAdminLoggingMBean, AdminLogging {
private final TransactionManager transactionManager;
private boolean debugSql;
private boolean debugLazyLoad;
/**
* Configure from serverConfig properties.
*/
public MAdminLogging(ServerConfig serverConfig, TransactionManager txManager) {
this.transactionManager = txManager;
this.debugSql = serverConfig.isDebugSql();
this.debugLazyLoad = serverConfig.isDebugLazyLoad();
}
public void setLogLevel(LogLevel logLevel){
transactionManager.setTransactionLogLevel(logLevel);
}
public LogLevel getLogLevel() {
return transactionManager.getTransactionLogLevel();
}
public boolean isDebugGeneratedSql() {
return debugSql;
}
public void setDebugGeneratedSql(boolean debugSql) {
this.debugSql = debugSql;
}
public boolean isDebugLazyLoad() {
return debugLazyLoad;
}
public void setDebugLazyLoad(boolean debugLazyLoad) {
this.debugLazyLoad = debugLazyLoad;
}
}
@@ -1,37 +0,0 @@
package com.avaje.ebeaninternal.server.jmx;
import com.avaje.ebean.LogLevel;
public interface MAdminLoggingMBean {
/**
* The current log level .
*/
public LogLevel getLogLevel();
/**
* Set the log level for native sql queries.
*/
public void setLogLevel(LogLevel logLevel);
/**
* If true Log generated sql to the console.
*/
public boolean isDebugGeneratedSql();
/**
* Set to true to Log generated sql to the console.
*/
public void setDebugGeneratedSql(boolean debugSql);
/**
* Return true if lazy loading should be debugged.
*/
public boolean isDebugLazyLoad();
/**
* Set the debugging on lazy loading.
*/
public void setDebugLazyLoad(boolean debugLazyLoad);
}
@@ -237,7 +237,7 @@ public final class BatchControl {
Arrays.sort(bsArray, depthComparator);
if (transaction.isLogSummary()) {
transaction.logInternal("BatchControl flush " + Arrays.toString(bsArray));
transaction.logSummary("BatchControl flush " + Arrays.toString(bsArray));
}
for (int i = 0; i < bsArray.length; i++) {
BatchedBeanHolder bs = bsArray[i];
@@ -498,7 +498,7 @@ public final class DefaultPersister implements Persister {
if (idList != null) {
q.where().idIn(idList);
if (t.isLogSummary()) {
t.logInternal("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values");
t.logSummary("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values");
}
List<?> beanList = server.findList(q, t);
deleteList(beanList, t);
@@ -507,7 +507,7 @@ public final class DefaultPersister implements Persister {
} else {
q.where().idEq(id);
if (t.isLogSummary()) {
t.logInternal("-- DeleteById of " + descriptor.getName() + " id[" + id + "] requires fetch of foreign key values");
t.logSummary("-- DeleteById of " + descriptor.getName() + " id[" + id + "] requires fetch of foreign key values");
}
Object bean = server.findUnique(q, t);
if (bean == null) {
@@ -555,7 +555,7 @@ public final class DefaultPersister implements Persister {
for (int i = 0; i < manys.length; i++) {
SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList);
if (t.isLogSummary()) {
t.logInternal("-- Deleting intersection table entries: " + manys[i].getFullBeanName());
t.logSummary("-- Deleting intersection table entries: " + manys[i].getFullBeanName());
}
executeSqlUpdate(sqlDelete, t);
}
@@ -563,7 +563,7 @@ public final class DefaultPersister implements Persister {
// delete the bean(s)
SqlUpdate deleteById = descriptor.deleteById(id, idList);
if (t.isLogSummary()) {
t.logInternal("-- Deleting " + descriptor.getName() + " Ids" + idList);
t.logSummary("-- Deleting " + descriptor.getName() + " Ids" + idList);
}
// use Id's to update L2 cache rather than Bulk table event
@@ -1020,7 +1020,7 @@ public final class DefaultPersister implements Persister {
if (deletions != null && deletions.remove(otherBean)) {
String m = "Inserting and Deleting same object? " + otherBean;
if (t.isLogSummary()) {
t.logInternal(m);
t.logSummary(m);
}
logger.warn(m);
@@ -68,7 +68,7 @@ public class DeleteUnloadedForeignKeys {
SpiTransaction t = request.getTransaction();
if (t.isLogSummary()) {
t.logInternal("-- Ebean fetching foreign key values for delete of " + descriptor.getName() + " id:" + id);
t.logSummary("-- Ebean fetching foreign key values for delete of " + descriptor.getName() + " id:" + id);
}
beanWithForeignKeys = server.findUnique(q, t);
}
@@ -95,7 +95,7 @@ public class ExeCallableSql {
} else {
if (logSql){
t.logInternal(sql);
t.logSql(sql);
}
cstmt = pstmtFactory.getCstmt(t, sql);
}
@@ -123,7 +123,7 @@ public class ExeOrmUpdate {
} else {
if (logSql){
t.logInternal(sql);
t.logSql(sql);
}
pstmt = pstmtFactory.getPstmt(t, sql);
}
@@ -115,7 +115,7 @@ public class ExeUpdateSql {
} else {
if (logSql){
t.logInternal(sql);
t.logSql(sql);
}
pstmt = pstmtFactory.getPstmt(t, sql);
}
@@ -54,7 +54,7 @@ public class PstmtFactory {
}
if (logSql){
t.logInternal(sql);
t.logSql(sql);
}
Connection conn = t.getInternalConnection();
@@ -83,7 +83,7 @@ public class PstmtFactory {
}
if (logSql){
t.logInternal(sql);
t.logSql(sql);
}
Connection conn = t.getInternalConnection();
@@ -36,23 +36,14 @@ public class DeleteHandler extends DmlHandler {
PreparedStatement pstmt;
if (isBatch) {
pstmt = getPstmt(t, sql, persistRequest, false);
} else {
logSql(sql);
pstmt = getPstmt(t, sql, false);
}
dataBind = new DataBind(pstmt);
bindLogAppend("Binding Delete [");
bindLogAppend(meta.getTableName());
bindLogAppend("] where[");
meta.bind(persistRequest, this);
bindLogAppend("]");
// log the binding to transaction log if requested
logBinding();
logSql(sql);
}
/**
@@ -91,7 +91,7 @@ public final class DmlBeanPersister implements BeanPersister {
String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r","\n"}, "\\n ");
String msg = "ERROR executing DML bindLog["+handler.getBindLog()+"] error["+errMsg+"]";
if (request.getTransaction().isLogSummary()) {
request.getTransaction().logInternal(msg);
request.getTransaction().logSummary(msg);
}
throw new PersistenceException(msg, e);
@@ -16,47 +16,43 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.persist.BatchedPstmt;
import com.avaje.ebeaninternal.server.persist.BatchedPstmtHolder;
import com.avaje.ebeaninternal.server.persist.dmlbind.BindableRequest;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.type.DataBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Base class for Handler implementations.
*/
public abstract class DmlHandler implements PersistHandler, BindableRequest {
private static final Logger logger = LoggerFactory.getLogger(DmlHandler.class);
/**
* The originating request.
*/
protected final PersistRequestBean<?> persistRequest;
protected final StringBuilder bindLog;
/**
* The originating request.
*/
protected final PersistRequestBean<?> persistRequest;
protected final Set<String> loadedProps;
protected final StringBuilder bindLog;
protected final SpiTransaction transaction;
protected final boolean emptyStringToNull;
protected final boolean logLevelSql;
protected final Set<String> loadedProps;
/**
* The PreparedStatement used for the dml.
*/
protected DataBind dataBind;
protected String sql;
protected ArrayList<UpdateGenValue> updateGenValues;
private Set<String> additionalProps;
protected final SpiTransaction transaction;
// private boolean checkDelta;
//
// private BeanDelta deltaBean;
protected final boolean emptyStringToNull;
protected final boolean logLevelSql;
/**
* The PreparedStatement used for the dml.
*/
protected DataBind dataBind;
protected String sql;
protected ArrayList<UpdateGenValue> updateGenValues;
private Set<String> additionalProps;
protected DmlHandler(PersistRequestBean<?> persistRequest, boolean emptyStringToNull) {
this.persistRequest = persistRequest;
@@ -71,329 +67,308 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
}
}
// protected void setCheckDelta(boolean checkDelta) {
// this.checkDelta = checkDelta;
// }
public PersistRequestBean<?> getPersistRequest() {
return persistRequest;
}
/**
* Get the sql and bind the statement.
*/
public abstract void bind() throws SQLException;
/**
* Execute now for non-batch execution.
*/
public abstract void execute() throws SQLException;
/**
* Get the sql and bind the statement.
*/
public abstract void bind() throws SQLException;
/**
* Check the rowCount.
*/
protected void checkRowCount(int rowCount) throws SQLException, OptimisticLockException {
try {
persistRequest.checkRowCount(rowCount);
persistRequest.postExecute();
} catch (OptimisticLockException e){
// add the SQL and bind values to error message
String m = e.getMessage()+" sql["+sql+"] bind["+bindLog+"]";
persistRequest.getTransaction().log("OptimisticLockException:"+m);
throw new OptimisticLockException(m, null, e.getEntity());
}
}
/**
* Add this for batch execution.
*/
public void addBatch() throws SQLException {
PstmtBatch pstmtBatch = persistRequest.getPstmtBatch();
if (pstmtBatch != null){
pstmtBatch.addBatch(dataBind.getPstmt());
} else {
dataBind.getPstmt().addBatch();
}
}
/**
* Execute now for non-batch execution.
*/
public abstract void execute() throws SQLException;
/**
* Close the underlying statement.
*/
public void close() {
try {
if (dataBind != null){
dataBind.close();
}
} catch (SQLException ex) {
logger.error(null, ex);
}
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog == null ? "" : bindLog.toString();
}
/**
* Set the Id value that was bound. This value is used for logging summary
* level information.
*/
public void setIdValue(Object idValue) {
persistRequest.setBoundId(idValue);
}
/**
* Log the bind information to the transaction log.
*/
protected void logBinding() {
if (logLevelSql) {
transaction.logInternal(bindLog.toString());
}
}
/**
* Log the sql to the transaction log.
*/
protected void logSql(String sql) {
if (logLevelSql) {
transaction.logInternal(sql);
}
}
public boolean isIncluded(BeanProperty prop) {
return (loadedProps == null || loadedProps.contains(prop.getName()));
}
public boolean isIncludedWhere(BeanProperty prop) {
if (prop.isDbEncrypted()){
// update without a version property ...
// for encrypted properties only include if it was
// also an updated/modified property
return isIncluded(prop);
}
return prop.isDbUpdatable() && (loadedProps == null || loadedProps.contains(prop.getName()));
/**
* Check the rowCount.
*/
protected void checkRowCount(int rowCount) throws SQLException, OptimisticLockException {
try {
persistRequest.checkRowCount(rowCount);
persistRequest.postExecute();
} catch (OptimisticLockException e) {
// add the SQL and bind values to error message
String m = e.getMessage() + " sql[" + sql + "] bind[" + bindLog + "]";
persistRequest.getTransaction().logSummary("OptimisticLockException:" + m);
throw new OptimisticLockException(m, null, e.getEntity());
}
/**
* Bind a raw value. Used to bind the discriminator column.
*/
public Object bind(String propName, Object value, int sqlType) throws SQLException {
if (logLevelSql) {
bindLog.append(propName).append("=");
bindLog.append(value).append(", ");
}
dataBind.setObject(value, sqlType);
return value;
}
}
public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
if (logLevelSql) {
bindLog.append(logPlaceHolder).append(" ");
}
dataBind.setObject(value, sqlType);
return value;
/**
* Add this for batch execution.
*/
public void addBatch() throws SQLException {
PstmtBatch pstmtBatch = persistRequest.getPstmtBatch();
if (pstmtBatch != null) {
pstmtBatch.addBatch(dataBind.getPstmt());
} else {
dataBind.getPstmt().addBatch();
}
}
/**
* Bind the value to the preparedStatement.
*/
public Object bind(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException {
return bindInternal(logLevelSql, value, prop, propName, bindNull);
/**
* Close the underlying statement.
*/
public void close() {
try {
if (dataBind != null) {
dataBind.close();
}
} catch (SQLException ex) {
logger.error(null, ex);
}
/**
* Bind the value to the preparedStatement without logging.
*/
public Object bindNoLog(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException {
return bindInternal(false, value, prop, propName, bindNull);
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog == null ? "" : bindLog.toString();
}
/**
* Set the Id value that was bound. This value is used for logging summary
* level information.
*/
public void setIdValue(Object idValue) {
persistRequest.setBoundId(idValue);
}
/**
* Log the sql to the transaction log.
*/
protected void logSql(String sql) {
if (logLevelSql) {
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
sql += "; --bind(" + bindLog + ")";
}
transaction.logSql(sql);
}
private Object bindInternal(boolean log, Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException {
if (!bindNull){
if (emptyStringToNull && (value instanceof String) && ((String)value).length() == 0){
// support Oracle conversion of empty string to null
//value = prop.getDbNullValue(value);
value = null;
}
}
}
if (!bindNull && value == null) {
// where will have IS NULL clause so don't actually bind
if (log) {
bindLog.append(propName).append("=");
bindLog.append("null, ");
}
} else {
if (log) {
bindLog.append(propName).append("=");
if (prop.isLob()){
bindLog.append("[LOB]");
} else {
String sv = String.valueOf(value);
if (sv.length() > 50){
sv = sv.substring(0,47)+"...";
}
bindLog.append(sv);
}
bindLog.append(", ");
}
// do the actual binding to PreparedStatement
prop.bind(dataBind, value);
// if (checkDelta) {
// if (!prop.isId() && prop.isDeltaRequired()){
// if (deltaBean == null){
// deltaBean = persistRequest.createDeltaBean();
// transaction.getEvent().addBeanDelta(deltaBean);
// }
// deltaBean.add(prop, value);
// }
// }
}
return value;
}
/**
* Add the comment to the bind information log.
*/
protected void bindLogAppend(String comment) {
if (logLevelSql) {
bindLog.append(comment);
}
}
public boolean isIncluded(BeanProperty prop) {
return (loadedProps == null || loadedProps.contains(prop.getName()));
}
/**
* For generated properties set on insert register as additional
* loaded properties if required.
*/
public final void registerAdditionalProperty(String propertyName) {
if (loadedProps != null && !loadedProps.contains(propertyName)){
if (additionalProps == null){
additionalProps = new HashSet<String>();
}
additionalProps.add(propertyName);
}
public boolean isIncludedWhere(BeanProperty prop) {
if (prop.isDbEncrypted()) {
// update without a version property ...
// for encrypted properties only include if it was
// also an updated/modified property
return isIncluded(prop);
}
return prop.isDbUpdatable() && (loadedProps == null || loadedProps.contains(prop.getName()));
}
/**
* Set any additional (generated) properties to the set of loaded properties
* if required.
*/
protected void setAdditionalProperties() {
if (additionalProps != null){
// additional generated properties set on insert
// added to the set of loaded properties
additionalProps.addAll(loadedProps);
persistRequest.setLoadedProps(additionalProps);
}
}
/**
* Register a generated value on a update. This can not be set to the bean
* until after the where clause has been bound for concurrency checking.
* <p>
* GeneratedProperty values are likely going to be used for optimistic
* concurrency checking. This includes 'counter' and 'update timestamp'
* generation.
* </p>
*/
public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value) {
if (updateGenValues == null) {
updateGenValues = new ArrayList<UpdateGenValue>();
}
updateGenValues.add(new UpdateGenValue(prop, bean, value));
registerAdditionalProperty(prop.getName());
}
/**
* Set any update generated values to the bean. Must be called after where
* clause has been bound.
*/
public void setUpdateGenValues() {
if (updateGenValues != null) {
for (int i = 0; i < updateGenValues.size(); i++) {
UpdateGenValue updGenVal = updateGenValues.get(i);
updGenVal.setValue();
}
}
}
/**
* Check with useGeneratedKeys to get appropriate PreparedStatement.
*/
protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys) throws SQLException {
Connection conn = t.getInternalConnection();
if (genKeys) {
// the Id generated is always the first column
// Required to stop Oracle10 giving us Oracle rowId??
// Other jdbc drivers seem fine without this hint.
int[] columns = {1};
return conn.prepareStatement(sql, columns);
/**
* Bind a raw value. Used to bind the discriminator column.
*/
public Object bind(String propName, Object value, int sqlType) throws SQLException {
if (logLevelSql) {
if (value == null) {
bindLog.append("null");
} else {
String sval = value.toString();
if (sval.length() > 50) {
bindLog.append(sval.substring(0, 47)).append("...");
} else {
return conn.prepareStatement(sql);
bindLog.append(sval);
}
}
}
bindLog.append(",");
}
dataBind.setObject(value, sqlType);
return value;
}
/**
* Return a prepared statement taking into account batch requirements.
*/
protected PreparedStatement getPstmt(SpiTransaction t, String sql, PersistRequestBean<?> request, boolean genKeys)
throws SQLException {
public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
if (logLevelSql) {
bindLog.append(logPlaceHolder).append(" ");
}
dataBind.setObject(value, sqlType);
return value;
}
BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder();
PreparedStatement stmt = batch.getStmt(sql, request);
/**
* Bind the value to the preparedStatement.
*/
public Object bind(Object value, BeanProperty prop, String propName, boolean bindNull)
throws SQLException {
return bindInternal(logLevelSql, value, prop, propName, bindNull);
}
if (stmt != null) {
return stmt;
}
/**
* Bind the value to the preparedStatement without logging.
*/
public Object bindNoLog(Object value, BeanProperty prop, String propName, boolean bindNull)
throws SQLException {
return bindInternal(false, value, prop, propName, bindNull);
}
if (logLevelSql){
t.logInternal(sql);
}
stmt = getPstmt(t, sql, genKeys);
private Object bindInternal(boolean log, Object value, BeanProperty prop, String propName,
boolean bindNull) throws SQLException {
PstmtBatch pstmtBatch = request.getPstmtBatch();
if (pstmtBatch != null){
pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize());
if (!bindNull) {
if (emptyStringToNull && (value instanceof String) && ((String) value).length() == 0) {
// support Oracle conversion of empty string to null
// value = prop.getDbNullValue(value);
value = null;
}
}
if (!bindNull && value == null) {
// where will have IS NULL clause so don't actually bind
if (log) {
bindLog.append("null, ");
}
} else {
if (log) {
if (prop.isLob()) {
bindLog.append("[LOB]");
} else {
String sv = String.valueOf(value);
if (sv.length() > 50) {
sv = sv.substring(0, 47) + "...";
}
bindLog.append(sv);
}
BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, request.getPstmtBatch(), true);
batch.addStmt(bs, request);
return stmt;
}
/**
* Hold the values from GeneratedValue that need to be set to the bean
* property after the where clause has been built.
*/
private static final class UpdateGenValue {
bindLog.append(",");
}
// do the actual binding to PreparedStatement
prop.bind(dataBind, value);
}
return value;
}
private final BeanProperty property;
/**
* For generated properties set on insert register as additional loaded
* properties if required.
*/
public final void registerAdditionalProperty(String propertyName) {
if (loadedProps != null && !loadedProps.contains(propertyName)) {
if (additionalProps == null) {
additionalProps = new HashSet<String>();
}
additionalProps.add(propertyName);
}
}
private final Object bean;
/**
* Set any additional (generated) properties to the set of loaded properties
* if required.
*/
protected void setAdditionalProperties() {
if (additionalProps != null) {
// additional generated properties set on insert
// added to the set of loaded properties
additionalProps.addAll(loadedProps);
persistRequest.setLoadedProps(additionalProps);
}
}
private final Object value;
/**
* Register a generated value on a update. This can not be set to the bean
* until after the where clause has been bound for concurrency checking.
* <p>
* GeneratedProperty values are likely going to be used for optimistic
* concurrency checking. This includes 'counter' and 'update timestamp'
* generation.
* </p>
*/
public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value) {
if (updateGenValues == null) {
updateGenValues = new ArrayList<UpdateGenValue>();
}
updateGenValues.add(new UpdateGenValue(prop, bean, value));
registerAdditionalProperty(prop.getName());
}
private UpdateGenValue(BeanProperty property, Object bean, Object value) {
this.property = property;
this.bean = bean;
this.value = value;
}
/**
* Set any update generated values to the bean. Must be called after where
* clause has been bound.
*/
public void setUpdateGenValues() {
if (updateGenValues != null) {
for (int i = 0; i < updateGenValues.size(); i++) {
UpdateGenValue updGenVal = updateGenValues.get(i);
updGenVal.setValue();
}
}
}
/**
* Set the value to the bean property.
*/
private void setValue() {
// support PropertyChangeSupport
property.setValueIntercept(bean, value);
}
}
/**
* Check with useGeneratedKeys to get appropriate PreparedStatement.
*/
protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys)
throws SQLException {
Connection conn = t.getInternalConnection();
if (genKeys) {
// the Id generated is always the first column
// Required to stop Oracle10 giving us Oracle rowId??
// Other jdbc drivers seem fine without this hint.
int[] columns = { 1 };
return conn.prepareStatement(sql, columns);
} else {
return conn.prepareStatement(sql);
}
}
/**
* Return a prepared statement taking into account batch requirements.
*/
protected PreparedStatement getPstmt(SpiTransaction t, String sql, PersistRequestBean<?> request,
boolean genKeys) throws SQLException {
BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder();
PreparedStatement stmt = batch.getStmt(sql, request);
if (stmt != null) {
return stmt;
}
if (logLevelSql) {
t.logSql(sql);
}
stmt = getPstmt(t, sql, genKeys);
PstmtBatch pstmtBatch = request.getPstmtBatch();
if (pstmtBatch != null) {
pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize());
}
BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, request.getPstmtBatch(), true);
batch.addStmt(bs, request);
return stmt;
}
/**
* Hold the values from GeneratedValue that need to be set to the bean
* property after the where clause has been built.
*/
private static final class UpdateGenValue {
private final BeanProperty property;
private final Object bean;
private final Object value;
private UpdateGenValue(BeanProperty property, Object bean, Object value) {
this.property = property;
this.bean = bean;
this.value = value;
}
/**
* Set the value to the bean property.
*/
private void setValue() {
// support PropertyChangeSupport
property.setValueIntercept(bean, value);
}
}
}
@@ -101,22 +101,15 @@ public class InsertHandler extends DmlHandler {
PreparedStatement pstmt;
if (isBatch) {
pstmt = getPstmt(t, sql, persistRequest, useGeneratedKeys);
} else {
logSql(sql);
pstmt = getPstmt(t, sql, useGeneratedKeys);
}
dataBind = new DataBind(pstmt);
bindLogAppend("Binding Insert [");
bindLogAppend(desc.getBaseTable());
bindLogAppend("] set[");
// bind the bean property values
meta.bind(this, bean, withId);
bindLogAppend("]");
logBinding();
logSql(sql);
}
/**
@@ -52,23 +52,16 @@ public class UpdateHandler extends DmlHandler {
PreparedStatement pstmt;
if (isBatch) {
pstmt = getPstmt(t, sql, persistRequest, false);
} else {
logSql(sql);
pstmt = getPstmt(t, sql, false);
}
dataBind = new DataBind(pstmt);
bindLogAppend("Binding Update [");
bindLogAppend(meta.getTableName());
bindLogAppend("] ");
meta.bind(persistRequest, this, updatePlan);
setUpdateGenValues();
bindLogAppend("]");
logBinding();
logSql(sql);
}
@Override
@@ -74,12 +74,8 @@ public final class UpdateMeta {
Object bean = persist.getBean();
bind.bindLogAppend(" set[");
// bind.setCheckDelta(true);
updatePlan.bindSet(bind, bean);
// bind.setCheckDelta(false);
bind.bindLogAppend("] where[");
id.dmlBind(bind, false, bean);
switch (persist.getConcurrencyMode()) {
File diff suppressed because it is too large Load Diff
@@ -319,8 +319,7 @@ public class CQueryBuilder implements Constants {
sb.append(select.getSelectSql());
}
sb.append(" ").append(NEW_LINE);
sb.append("from ");
sb.append(" from ");
// build the from clause potentially with joins
// required only for the predicates
@@ -330,7 +329,7 @@ public class CQueryBuilder implements Constants {
boolean hasWhere = false;
if (inheritanceWhere.length() > 0) {
sb.append(" ").append(NEW_LINE).append("where");
sb.append(" where");
sb.append(inheritanceWhere);
hasWhere = true;
}
@@ -339,7 +338,7 @@ public class CQueryBuilder implements Constants {
if (hasWhere) {
sb.append(" and ");
} else {
sb.append(NEW_LINE).append("where ");
sb.append(" where ");
}
BeanDescriptor<?> desc = request.getBeanDescriptor();
@@ -356,9 +355,9 @@ public class CQueryBuilder implements Constants {
if (!isEmpty(dbWhere)) {
if (!hasWhere) {
hasWhere = true;
sb.append(" ").append(NEW_LINE).append("where ");
sb.append(" where ");
} else {
sb.append("and ");
sb.append(" and ");
}
sb.append(dbWhere);
}
@@ -366,7 +365,7 @@ public class CQueryBuilder implements Constants {
String dbFilterMany = predicates.getDbFilterMany();
if (!isEmpty(dbFilterMany)) {
if (!hasWhere) {
sb.append(" ").append(NEW_LINE).append("where ");
sb.append(" where ");
} else {
sb.append("and ");
}
@@ -375,8 +374,7 @@ public class CQueryBuilder implements Constants {
String dbOrderBy = predicates.getDbOrderBy();
if (dbOrderBy != null) {
sb.append(" ").append(NEW_LINE);
sb.append("order by ").append(dbOrderBy);
sb.append(" order by ").append(dbOrderBy);
}
if (useSqlLimiter) {
@@ -60,7 +60,6 @@ public class CQueryBuilderRawSql implements Constants {
StringBuilder sb = new StringBuilder();
sb.append(sql.getPreFrom());
sb.append(" ");
sb.append(NEW_LINE);
String s = sql.getPreWhere();
BindParams bindParams = request.getQuery().getBindParams();
@@ -95,11 +94,10 @@ public class CQueryBuilderRawSql implements Constants {
}
if (!isEmpty(dynamicWhere)) {
sb.append(NEW_LINE);
if (sql.isAndWhereExpr()) {
sb.append("and ");
sb.append(" and ");
} else {
sb.append("where ");
sb.append(" where ");
}
sb.append(dynamicWhere);
sb.append(" ");
@@ -107,7 +105,6 @@ public class CQueryBuilderRawSql implements Constants {
String preHaving = sql.getPreHaving();
if (!isEmpty(preHaving)) {
sb.append(NEW_LINE);
sb.append(preHaving);
sb.append(" ");
}
@@ -115,7 +112,6 @@ public class CQueryBuilderRawSql implements Constants {
String dbHaving = predicates.getDbHaving();
if (!isEmpty(dbHaving)) {
sb.append(" ");
sb.append(NEW_LINE);
if (sql.isAndHavingExpr()) {
sb.append("and ");
} else {
@@ -126,7 +122,6 @@ public class CQueryBuilderRawSql implements Constants {
}
if (!isEmpty(orderBy)) {
sb.append(NEW_LINE);
sb.append(" order by ").append(orderBy);
}
@@ -3,6 +3,9 @@ package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.concurrent.FutureTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
@@ -12,10 +15,8 @@ import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.jmx.MAdminLogging;
import com.avaje.ebeaninternal.server.persist.Binder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
/**
* Handles the Object Relational fetching.
@@ -26,15 +27,12 @@ public class CQueryEngine {
private final CQueryBuilder queryBuilder;
private final MAdminLogging logControl;
private final BackgroundExecutor backgroundExecutor;
private final int defaultSecondaryQueryBatchSize = 100;
public CQueryEngine(DatabasePlatform dbPlatform, MAdminLogging logControl, Binder binder, BackgroundExecutor backgroundExecutor) {
public CQueryEngine(DatabasePlatform dbPlatform, Binder binder, BackgroundExecutor backgroundExecutor) {
this.logControl = logControl;
this.backgroundExecutor = backgroundExecutor;
this.queryBuilder = new CQueryBuilder(backgroundExecutor, dbPlatform, binder);
}
@@ -51,18 +49,19 @@ public class CQueryEngine {
CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request);
try {
String sql = rcQuery.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
if (logControl.isDebugGeneratedSql()) {
System.out.println(sql);
}
request.logSql(sql);
BeanIdList list = rcQuery.findIds();
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql += "; --bind("+rcQuery.getBindLog()+")";
}
request.logSql(logSql);
}
if (request.isLogSummary()) {
request.getTransaction().logInternal(rcQuery.getSummary());
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (!list.isFetchingInBackground() && request.getQuery().isFutureFetch()) {
@@ -85,19 +84,19 @@ public class CQueryEngine {
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
try {
String sql = rcQuery.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
if (logControl.isDebugGeneratedSql()) {
System.out.println(sql);
}
request.logSql(sql);
int rowCount = rcQuery.findRowCount();
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql += "; --bind("+rcQuery.getBindLog()+")";
}
request.logSql(logSql);
}
if (request.isLogSummary()) {
request.getTransaction().logInternal(rcQuery.getSummary());
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (request.getQuery().isFutureFetch()) {
@@ -123,20 +122,16 @@ public class CQueryEngine {
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
QueryIterator<T> readIterate = cquery.readIterate(iterateBufferSize, request);
@@ -164,19 +159,15 @@ public class CQueryEngine {
request.setCancelableQuery(cquery);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
if (request.isLogSql()) {
logSql(cquery);
}
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
BeanCollection<T> beanCollection = cquery.readCollection();
@@ -208,10 +199,8 @@ public class CQueryEngine {
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);// request, e,
// cquery.getBindLog(),
// cquery.getGeneratedSql());
throw cquery.createPersistenceException(e);
} finally {
if (useBackgroundToContinueFetch) {
// left closing resources to BackgroundFetch...
@@ -239,15 +228,12 @@ public class CQueryEngine {
CQuery<T> cquery = queryBuilder.buildQuery(request);
try {
if (logControl.isDebugGeneratedSql()) {
logSqlToConsole(cquery);
}
cquery.prepareBindExecuteQuery();
if (request.isLogSql()) {
logSql(cquery);
}
cquery.prepareBindExecuteQuery();
if (cquery.readBean()) {
bean = cquery.getLoadedBean();
}
@@ -268,46 +254,16 @@ public class CQueryEngine {
}
}
/**
* Log the generated SQL to the console.
*/
private void logSqlToConsole(CQuery<?> cquery) {
SpiQuery<?> query = cquery.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String sql = cquery.getGeneratedSql();
String summary = cquery.getSummary();
StringBuilder sb = new StringBuilder(1000);
sb.append("<sql ");
if (query.isAutofetchTuned()) {
sb.append("tuned='true' ");
}
if (loadMode != null) {
sb.append("mode='").append(loadMode).append("' ");
}
sb.append("summary='").append(summary);
if (loadDesc != null) {
sb.append("' load='").append(loadDesc);
}
sb.append("' >");
sb.append(Constants.NEW_LINE);
sb.append(sql);
sb.append(Constants.NEW_LINE).append("</sql>");
System.out.println(sb.toString());
}
/**
* Log the generated SQL to the transaction log.
*/
private void logSql(CQuery<?> query) {
String sql = query.getGeneratedSql();
sql = sql.replace(Constants.NEW_LINE, ' ');
query.getTransaction().logInternal(sql);
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
sql += "; --bind("+query.getBindLog()+")";
}
query.getTransaction().logSql(sql);
}
/**
@@ -349,7 +305,7 @@ public class CQueryEngine {
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logInternal(msg.toString());
q.getTransaction().logSummary(msg.toString());
}
/**
@@ -394,6 +350,6 @@ public class CQueryEngine {
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logInternal(msg.toString());
q.getTransaction().logSummary(msg.toString());
}
}
@@ -151,7 +151,7 @@ public class CQueryPredicates {
Object bindValue = whereExprBindValues.get(i);
binder.bindObject(dataBind, bindValue);
if (i > 0 || idValue != null) {
bindLog.append(", ");
bindLog.append(",");
}
bindLog.append(bindValue);
}
@@ -163,7 +163,7 @@ public class CQueryPredicates {
Object bindValue = filterManyExprBindValues.get(i);
binder.bindObject(dataBind, bindValue);
if (i > 0 || idValue != null) {
bindLog.append(", ");
bindLog.append(",");
}
bindLog.append(bindValue);
}
@@ -182,7 +182,7 @@ public class CQueryPredicates {
Object bindValue = havingExprBindValues.get(i);
binder.bindObject(dataBind, bindValue);
if (i > 0) {
bindLog.append(", ");
bindLog.append(",");
}
bindLog.append(bindValue);
}
@@ -215,10 +215,8 @@ public class CQueryPredicates {
boolean hasRaw = !"".equals(whereRawSql);
if (hasRaw && parseRaw) {
// parse with encrypted property awareness. This means that if we have
// an
// encrypted property we will insert special named parameter place
// holders
// for binding the encryption key values
// an encrypted property we will insert special named parameter place
// holders for binding the encryption key values
parser.setEncrypted(true);
whereRawSql = parser.parse(whereRawSql);
parser.setEncrypted(false);
@@ -259,7 +257,6 @@ public class CQueryPredicates {
public void prepare(boolean buildSql) {
DeployParser deployParser = request.createDeployParser();
prepare(buildSql, true, deployParser);
}
@@ -5,19 +5,6 @@ package com.avaje.ebeaninternal.server.query;
*/
public interface Constants {
/**
* the new line character used.
* <p>
* Note that this is removed for logging sql to the transaction log.
* </p>
*/
public static final char NEW_LINE = '\n';
/**
* The carriage return character.
*/
public static final char CARRIAGE_RETURN = '\r';
/**
* literal used for SQL LIMIT in MySql and Postgres.
*/
@@ -11,264 +11,261 @@ import com.avaje.ebeaninternal.server.util.ArrayStack;
public class DefaultDbSqlContext implements DbSqlContext {
private static final String NEW_LINE = "\n";
private static final String COMMA = ", ";
private static final String COMMA = ", ";
private static final String PERIOD = ".";
private static final String PERIOD = ".";
private final String tableAliasPlaceHolder;
private final String tableAliasPlaceHolder;
private final String columnAliasPrefix;
private final String columnAliasPrefix;
private final ArrayStack<String> tableAliasStack = new ArrayStack<String>();
private final ArrayStack<String> tableAliasStack = new ArrayStack<String>();
private final ArrayStack<String> joinStack = new ArrayStack<String>();
private final ArrayStack<String> joinStack = new ArrayStack<String>();
private final ArrayStack<String> prefixStack = new ArrayStack<String>();
private final ArrayStack<String> prefixStack = new ArrayStack<String>();
private final boolean useColumnAlias;
private final boolean useColumnAlias;
private int columnIndex;
private int columnIndex;
private StringBuilder sb = new StringBuilder(140);
private StringBuilder sb = new StringBuilder(140);
/**
* A Set used to make sure formula joins are only added once to a query.
*/
private HashSet<String> formulaJoins;
/**
* A Set used to make sure formula joins are only added once to a query.
*/
private HashSet<String> formulaJoins;
private HashSet<String> tableJoins;
private HashSet<String> tableJoins;
private SqlTreeAlias alias;
private SqlTreeAlias alias;
private String currentPrefix;
private String currentPrefix;
private ArrayList<BeanProperty> encryptedProps;
/**
* Construct for FROM clause (no column alias used).
*/
public DefaultDbSqlContext(SqlTreeAlias alias, String tableAliasPlaceHolder) {
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
this.columnAliasPrefix = null;
this.useColumnAlias = false;
this.alias = alias;
private ArrayList<BeanProperty> encryptedProps;
/**
* Construct for FROM clause (no column alias used).
*/
public DefaultDbSqlContext(SqlTreeAlias alias, String tableAliasPlaceHolder) {
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
this.columnAliasPrefix = null;
this.useColumnAlias = false;
this.alias = alias;
}
/**
* Construct for SELECT clause (with column alias settings).
*/
public DefaultDbSqlContext(SqlTreeAlias alias, String tableAliasPlaceHolder,
String columnAliasPrefix, boolean alwaysUseColumnAlias) {
this.alias = alias;
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
this.columnAliasPrefix = columnAliasPrefix;
this.useColumnAlias = alwaysUseColumnAlias;
}
public void addEncryptedProp(BeanProperty p) {
if (encryptedProps == null) {
encryptedProps = new ArrayList<BeanProperty>();
}
encryptedProps.add(p);
}
public BeanProperty[] getEncryptedProps() {
if (encryptedProps == null) {
return null;
}
/**
* Construct for SELECT clause (with column alias settings).
*/
public DefaultDbSqlContext(SqlTreeAlias alias, String tableAliasPlaceHolder, String columnAliasPrefix,
boolean alwaysUseColumnAlias) {
this.alias = alias;
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
this.columnAliasPrefix = columnAliasPrefix;
this.useColumnAlias = alwaysUseColumnAlias;
return encryptedProps.toArray(new BeanProperty[encryptedProps.size()]);
}
public String peekJoin() {
return joinStack.peek();
}
public void popJoin() {
joinStack.pop();
}
public void pushJoin(String node) {
joinStack.push(node);
}
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2) {
if (tableJoins == null) {
tableJoins = new HashSet<String>();
}
public void addEncryptedProp(BeanProperty p) {
if (encryptedProps == null){
encryptedProps = new ArrayList<BeanProperty>();
}
encryptedProps.add(p);
}
public BeanProperty[] getEncryptedProps() {
if (encryptedProps == null){
return null;
}
return encryptedProps.toArray(new BeanProperty[encryptedProps.size()]);
String joinKey = table + "-" + a1 + "-" + a2;
if (tableJoins.contains(joinKey)) {
return;
}
public String peekJoin() {
return joinStack.peek();
tableJoins.add(joinKey);
sb.append(" ");
sb.append(type);
sb.append(" ").append(table).append(" ");
sb.append(a2);
sb.append(" on ");
for (int i = 0; i < cols.length; i++) {
TableJoinColumn pair = cols[i];
if (i > 0) {
sb.append(" and ");
}
sb.append(a2);
sb.append(".").append(pair.getForeignDbColumn());
sb.append(" = ");
sb.append(a1);
sb.append(".").append(pair.getLocalDbColumn());
}
public void popJoin() {
joinStack.pop();
sb.append(" ");
}
public String getTableAlias(String prefix) {
return alias.getTableAlias(prefix);
}
public String getTableAliasManyWhere(String prefix) {
return alias.getTableAliasManyWhere(prefix);
}
public void pushSecondaryTableAlias(String alias) {
tableAliasStack.push(alias);
}
public String getRelativePrefix(String propName) {
return currentPrefix == null ? propName : currentPrefix + "." + propName;
}
public void pushTableAlias(String prefix) {
// store the currentPrefix on a stack
prefixStack.push(currentPrefix);
currentPrefix = prefix;
tableAliasStack.push(getTableAlias(prefix));
}
public void popTableAlias() {
tableAliasStack.pop();
// pop the currentPrefix from the stack
currentPrefix = prefixStack.pop();
;
}
public StringBuilder getBuffer() {
return sb;
}
public DefaultDbSqlContext append(String s) {
sb.append(s);
return this;
}
public DefaultDbSqlContext append(char s) {
sb.append(s);
return this;
}
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin) {
// replace ${ta} place holder with the real table alias...
String tableAlias = tableAliasStack.peek();
String converted = StringHelper
.replaceString(sqlFormulaJoin, tableAliasPlaceHolder, tableAlias);
if (formulaJoins == null) {
formulaJoins = new HashSet<String>();
} else if (formulaJoins.contains(converted)) {
// skip adding a formula join because
// the same join has already been added.
return;
}
public void pushJoin(String node) {
joinStack.push(node);
// we only want to add this join once
formulaJoins.add(converted);
sb.append(" ");
if (forceOuterJoin) {
if ("join".equals(sqlFormulaJoin.substring(0, 4).toLowerCase())) {
// prepend left outer as we are in the 'many' part
append(" left outer ");
}
}
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2) {
sb.append(converted);
sb.append(" ");
}
if (tableJoins == null) {
tableJoins = new HashSet<String>();
}
public void appendFormulaSelect(String sqlFormulaSelect) {
String joinKey = table + "-" + a1 + "-" + a2;
if (tableJoins.contains(joinKey)) {
return;
}
String tableAlias = tableAliasStack.peek();
String converted = StringHelper.replaceString(sqlFormulaSelect, tableAliasPlaceHolder,
tableAlias);
tableJoins.add(joinKey);
sb.append(COMMA);
sb.append(converted);
}
sb.append(NEW_LINE);
sb.append(type);
public void appendColumn(String column) {
appendColumn(tableAliasStack.peek(), column);
}
sb.append(" ").append(table).append(" ");
sb.append(a2);
public void appendColumn(String tableAlias, String column) {
sb.append(COMMA);
sb.append(" on ");
for (int i = 0; i < cols.length; i++) {
TableJoinColumn pair = cols[i];
if (i > 0) {
sb.append(" and ");
}
sb.append(a2);
sb.append(".").append(pair.getForeignDbColumn());
sb.append(" = ");
sb.append(a1);
sb.append(".").append(pair.getLocalDbColumn());
}
sb.append(" ");
if (column.indexOf("${}") > -1) {
// support DB functions such as lower() etc
// with the use of secondary columns
String x = StringHelper.replaceString(column, "${}", tableAlias);
sb.append(x);
} else {
sb.append(tableAlias);
sb.append(PERIOD);
sb.append(column);
}
public String getTableAlias(String prefix) {
return alias.getTableAlias(prefix);
if (useColumnAlias) {
sb.append(" ");
sb.append(columnAliasPrefix);
sb.append(columnIndex);
}
columnIndex++;
}
public String getTableAliasManyWhere(String prefix) {
return alias.getTableAliasManyWhere(prefix);
public String peekTableAlias() {
return tableAliasStack.peek();
}
public void appendRawColumn(String rawcolumnWithTableAlias) {
sb.append(COMMA);
sb.append(rawcolumnWithTableAlias);
if (useColumnAlias) {
sb.append(" ");
sb.append(columnAliasPrefix);
sb.append(columnIndex);
}
columnIndex++;
}
public void pushSecondaryTableAlias(String alias) {
tableAliasStack.push(alias);
}
public int length() {
return sb.length();
}
public String getRelativePrefix(String propName) {
public String getContent() {
String s = sb.toString();
sb = new StringBuilder();
return s;
}
return currentPrefix == null ? propName : currentPrefix + "." + propName;
}
public void pushTableAlias(String prefix) {
// store the currentPrefix on a stack
prefixStack.push(currentPrefix);
currentPrefix = prefix;
tableAliasStack.push(getTableAlias(prefix));
}
public void popTableAlias() {
tableAliasStack.pop();
// pop the currentPrefix from the stack
currentPrefix = prefixStack.pop();;
}
public StringBuilder getBuffer() {
return sb;
}
public DefaultDbSqlContext append(String s) {
sb.append(s);
return this;
}
public DefaultDbSqlContext append(char s) {
sb.append(s);
return this;
}
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin) {
// replace ${ta} place holder with the real table alias...
String tableAlias = tableAliasStack.peek();
String converted = StringHelper.replaceString(sqlFormulaJoin, tableAliasPlaceHolder, tableAlias);
if (formulaJoins == null) {
formulaJoins = new HashSet<String>();
} else if (formulaJoins.contains(converted)) {
// skip adding a formula join because
// the same join has already been added.
return;
}
// we only want to add this join once
formulaJoins.add(converted);
sb.append(NEW_LINE);
if (forceOuterJoin) {
if ("join".equals(sqlFormulaJoin.substring(0, 4).toLowerCase())) {
// prepend left outer as we are in the 'many' part
append(" left outer ");
}
}
sb.append(converted);
sb.append(" ");
}
public void appendFormulaSelect(String sqlFormulaSelect) {
String tableAlias = tableAliasStack.peek();
String converted = StringHelper.replaceString(sqlFormulaSelect, tableAliasPlaceHolder, tableAlias);
sb.append(COMMA);
sb.append(converted);
}
public void appendColumn(String column) {
appendColumn(tableAliasStack.peek(), column);
}
public void appendColumn(String tableAlias, String column) {
sb.append(COMMA);
if (column.indexOf("${}") > -1){
// support DB functions such as lower() etc
// with the use of secondary columns
String x = StringHelper.replaceString(column, "${}", tableAlias);
sb.append(x);
} else {
sb.append(tableAlias);
sb.append(PERIOD);
sb.append(column);
}
if (useColumnAlias) {
sb.append(" ");
sb.append(columnAliasPrefix);
sb.append(columnIndex);
}
columnIndex++;
}
public String peekTableAlias() {
return tableAliasStack.peek();
}
public void appendRawColumn(String rawcolumnWithTableAlias) {
sb.append(COMMA);
sb.append(rawcolumnWithTableAlias);
if (useColumnAlias) {
sb.append(" ");
sb.append(columnAliasPrefix);
sb.append(columnIndex);
}
columnIndex++;
}
public int length() {
return sb.length();
}
public String getContent() {
String s = sb.toString();
sb = new StringBuilder();
return s;
}
public String toString() {
return "DefaultDbSqlContext: "+sb.toString();
}
public String toString() {
return "DefaultDbSqlContext: " + sb.toString();
}
}
@@ -9,6 +9,9 @@ import java.util.ArrayList;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.SqlQueryListener;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.bean.BeanCollection;
@@ -19,12 +22,10 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.RelationalQueryEngine;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.jmx.MAdminLogging;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Perform native sql fetches.
@@ -39,7 +40,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
private final String dbTrueValue;
public DefaultRelationalQueryEngine(MAdminLogging logControl, Binder binder, String dbTrueValue) {
public DefaultRelationalQueryEngine(Binder binder, String dbTrueValue) {
this.binder = binder;
this.defaultMaxRows = GlobalProperties.getInt("nativesql.defaultmaxrows",100000);
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
@@ -94,9 +95,11 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
}
if (request.isLogSql()) {
String sOut = sql.replace(Constants.NEW_LINE, ' ');
sOut = sOut.replace(Constants.CARRIAGE_RETURN, ' ');
t.logInternal(sOut);
String logSql = sql;
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql += "; --bind("+bindLog+")";
}
t.logSql(logSql);
}
rset = pstmt.executeQuery();
@@ -178,7 +181,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind["
+ bindLog + "] finished[" + beanColl.isFinishedFetch() + "]";
t.logInternal(msg);
t.logSummary(msg);
}
if (query.isCancelled()){
@@ -13,160 +13,158 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
*/
public class SqlTree {
private SqlTreeNode rootNode;
private SqlTreeNode rootNode;
/**
* Property if resultSet contains master and detail rows.
*/
private BeanPropertyAssocMany<?> manyProperty;
private String manyPropertyName;
private ElPropertyValue manyPropEl;
/**
* Property if resultSet contains master and detail rows.
*/
private BeanPropertyAssocMany<?> manyProperty;
private String manyPropertyName;
private ElPropertyValue manyPropEl;
private Set<String> includes;
private Set<String> includes;
/**
* Summary of the select being generated.
*/
private String summary;
/**
* Summary of the select being generated.
*/
private String summary;
private String selectSql;
private String selectSql;
private String fromSql;
private String fromSql;
/**
* Encrypted Properties require additional binding.
*/
private BeanProperty[] encryptedProps;
/**
* Where clause for inheritance.
*/
private String inheritanceWhereSql;
/**
* Encrypted Properties require additional binding.
*/
private BeanProperty[] encryptedProps;
/**
* Create the SqlSelectClause.
*/
public SqlTree() {
}
public List<String> buildSelectExpressionChain() {
ArrayList<String> list = new ArrayList<String>();
rootNode.buildSelectExpressionChain(list);
return list;
}
/**
* Return the includes. Associated beans lists etc.
*/
public Set<String> getIncludes() {
return includes;
}
/**
* Set the association includes (Ones and Many's).
*/
public void setIncludes(Set<String> includes) {
this.includes = includes;
}
/**
* Set the manyProperty used for this query.
*/
public void setManyProperty(BeanPropertyAssocMany<?> manyProperty, String manyPropertyName, ElPropertyValue manyPropEl) {
this.manyProperty = manyProperty;
this.manyPropertyName = manyPropertyName;
this.manyPropEl = manyPropEl;
}
/**
* Where clause for inheritance.
*/
private String inheritanceWhereSql;
/**
* Return the String for the actual SQL.
*/
public String getSelectSql() {
return selectSql;
}
/**
* Create the SqlSelectClause.
*/
public SqlTree() {
}
/**
* Set the select sql clause.
*/
public void setSelectSql(String selectSql) {
this.selectSql = selectSql;
}
public List<String> buildSelectExpressionChain() {
ArrayList<String> list = new ArrayList<String>();
rootNode.buildSelectExpressionChain(list);
return list;
}
public String getFromSql() {
return fromSql;
}
/**
* Return the includes. Associated beans lists etc.
*/
public Set<String> getIncludes() {
return includes;
}
public void setFromSql(String fromSql) {
this.fromSql = fromSql;
}
/**
* Return the where clause for inheritance.
*/
public String getInheritanceWhereSql() {
return inheritanceWhereSql;
}
/**
* Set the association includes (Ones and Many's).
*/
public void setIncludes(Set<String> includes) {
this.includes = includes;
}
/**
* Set where clause(s) for inheritance.
*/
public void setInheritanceWhereSql(String whereSql) {
this.inheritanceWhereSql = whereSql;
}
/**
* Set the manyProperty used for this query.
*/
public void setManyProperty(BeanPropertyAssocMany<?> manyProperty, String manyPropertyName,
ElPropertyValue manyPropEl) {
this.manyProperty = manyProperty;
this.manyPropertyName = manyPropertyName;
this.manyPropEl = manyPropEl;
}
/**
* Set the summary description of the query.
*/
public void setSummary(String summary) {
this.summary = summary;
}
/**
* Return the String for the actual SQL.
*/
public String getSelectSql() {
return selectSql;
}
/**
* Return a summary of the select clause.
*/
public String getSummary() {
return summary;
}
public SqlTreeNode getRootNode() {
return rootNode;
}
public void setRootNode(SqlTreeNode rootNode) {
this.rootNode = rootNode;
}
/**
* Set the select sql clause.
*/
public void setSelectSql(String selectSql) {
this.selectSql = selectSql;
}
/**
* Return the property that is associated with the many. There can only be
* one per SqlSelect. This can be null.
*/
public BeanPropertyAssocMany<?> getManyProperty() {
return manyProperty;
}
public String getFromSql() {
return fromSql;
}
public String getManyPropertyName() {
return manyPropertyName;
}
public void setFromSql(String fromSql) {
this.fromSql = fromSql;
}
public ElPropertyValue getManyPropertyEl() {
return manyPropEl;
}
/**
* Return the where clause for inheritance.
*/
public String getInheritanceWhereSql() {
return inheritanceWhereSql;
}
/**
* Return true if this query includes a Many association.
*/
public boolean isManyIncluded() {
return (manyProperty != null);
}
/**
* Set where clause(s) for inheritance.
*/
public void setInheritanceWhereSql(String whereSql) {
this.inheritanceWhereSql = whereSql;
}
public BeanProperty[] getEncryptedProps() {
return encryptedProps;
}
/**
* Set the summary description of the query.
*/
public void setSummary(String summary) {
this.summary = summary;
}
public void setEncryptedProps(BeanProperty[] encryptedProps) {
this.encryptedProps = encryptedProps;
}
/**
* Return a summary of the select clause.
*/
public String getSummary() {
return summary;
}
public SqlTreeNode getRootNode() {
return rootNode;
}
public void setRootNode(SqlTreeNode rootNode) {
this.rootNode = rootNode;
}
/**
* Return the property that is associated with the many. There can only be one
* per SqlSelect. This can be null.
*/
public BeanPropertyAssocMany<?> getManyProperty() {
return manyProperty;
}
public String getManyPropertyName() {
return manyPropertyName;
}
public ElPropertyValue getManyPropertyEl() {
return manyPropEl;
}
/**
* Return true if this query includes a Many association.
*/
public boolean isManyIncluded() {
return (manyProperty != null);
}
public BeanProperty[] getEncryptedProps() {
return encryptedProps;
}
public void setEncryptedProps(BeanProperty[] encryptedProps) {
this.encryptedProps = encryptedProps;
}
}
File diff suppressed because it is too large Load Diff
@@ -8,48 +8,40 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
public interface SqlTreeNode {
/**
* the new line character used.
* <p>
* Note that this is removed for logging sql to the transaction log.
* </p>
*/
public static final char NEW_LINE = '\n';
public static final String PERIOD = ".";
public static final String PERIOD = ".";
public static final String COMMA = ", ";
public static final String COMMA = ", ";
public static final int NORMAL = 0;
public static final int SHARED = 1;
public static final int READONLY = 2;
public static final int NORMAL = 0;
public static final int SHARED = 1;
public static final int READONLY = 2;
public void buildSelectExpressionChain(List<String> selectChain);
public void buildSelectExpressionChain(List<String> selectChain);
/**
* Append the required column information to the SELECT part of the sql
* statement.
*/
public void appendSelect(DbSqlContext ctx, boolean subQuery);
/**
* Append the required column information to the SELECT part of the sql
* statement.
*/
public void appendSelect(DbSqlContext ctx, boolean subQuery);
/**
* Append to the FROM part of the sql.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin);
/**
* Append to the FROM part of the sql.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin);
/**
* Append any where predicates for inheritance.
*/
public void appendWhere(DbSqlContext ctx);
/**
* Append any where predicates for inheritance.
*/
public void appendWhere(DbSqlContext ctx);
/**
* Load the appropriate information from the SqlSelectReader.
* <p>
* At a high level this actually controls the reading of the data from the
* jdbc resultSet and putting it into the bean etc.
* </p>
*
*/
public void load(DbReadContext ctx, Object parentBean) throws SQLException;
/**
* Load the appropriate information from the SqlSelectReader.
* <p>
* At a high level this actually controls the reading of the data from the
* jdbc resultSet and putting it into the bean etc.
* </p>
*
*/
public void load(DbReadContext ctx, Object parentBean) throws SQLException;
}
@@ -27,496 +27,490 @@ import com.avaje.ebeaninternal.server.lib.util.StringHelper;
*/
public class SqlTreeNodeBean implements SqlTreeNode {
private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0];
private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0];
final BeanDescriptor<?> desc;
final IdBinder idBinder;
final BeanDescriptor<?> desc;
/**
* The children which will be other SelectBean or SelectProxyBean.
*/
final SqlTreeNode[] children;
final IdBinder idBinder;
final boolean readOnlyLeaf;
/**
* Set to true if this is a partial object fetch.
*/
final boolean partialObject;
/**
* The set of properties explicitly included in the query.
* We actually add the manyProp names to this as they are
* references/proxies we add via createListProxies().
*/
final Set<String> partialProps;
/**
* The hash of the partialProps (calculate once).
*/
final int partialHash;
final BeanProperty[] properties;
/**
* Extra where clause added by Where annotation on associated many.
*/
final String extraWhere;
final BeanPropertyAssoc<?> nodeBeanProp;
/**
* The children which will be other SelectBean or SelectProxyBean.
*/
final SqlTreeNode[] children;
final TableJoin[] tableJoins;
final boolean readOnlyLeaf;
/**
* False if report bean and has no id property.
*/
final boolean readId;
final boolean disableLazyLoad;
final InheritInfo inheritInfo;
final String prefix;
final Set<String> includedProps;
/**
* Set to true if this is a partial object fetch.
*/
final boolean partialObject;
final Map<String,String> pathMap;
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp,
SqlTreeProperties props, List<SqlTreeNode> myChildren, boolean withId) {
this(prefix, beanProp, beanProp.getTargetDescriptor(),props, myChildren, withId);
}
/**
* Create with the appropriate node.
*/
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, BeanDescriptor<?> desc,
SqlTreeProperties props, List<SqlTreeNode> myChildren, boolean withId) {
/**
* The set of properties explicitly included in the query. We actually add the
* manyProp names to this as they are references/proxies we add via
* createListProxies().
*/
final Set<String> partialProps;
this.prefix = prefix;
this.nodeBeanProp = beanProp;
this.desc = desc;
this.inheritInfo = desc.getInheritInfo();
this.extraWhere = (beanProp == null) ? null : beanProp.getExtraWhere();
this.idBinder = desc.getIdBinder();
// the bean has an Id property and we want to use it
this.readId = withId && (desc.propertiesId().length > 0);
this.disableLazyLoad = !readId || desc.isSqlSelectBased();
this.tableJoins = props.getTableJoins();
this.partialObject = props.isPartialObject();
this.partialProps = props.getIncludedProperties();
this.partialHash = partialObject ? partialProps.hashCode() : 0;
this.readOnlyLeaf = props.isReadOnly();
this.properties = props.getProps();
if (partialObject){
// merge the explicit partialProps with the implicitly added
// list proxies (that are added by createListProxies()) to get
// the full set of 'loaded' properties for this bean.
includedProps = LoadedPropertiesCache.get(partialHash, partialProps, desc);
} else {
includedProps = null;
}
if (myChildren == null) {
children = NO_CHILDREN;
} else {
children = myChildren.toArray(new SqlTreeNode[myChildren.size()]);
}
pathMap = createPathMap(prefix, desc);
}
private Map<String,String> createPathMap(String prefix, BeanDescriptor<?> desc) {
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
HashMap<String,String> m = new HashMap<String,String>();
for (int i = 0; i < manys.length; i++) {
String name = manys[i].getName();
m.put(name, getPath(prefix, name));
}
return m;
}
private String getPath(String prefix, String propertyName){
if (prefix == null){
return propertyName;
} else {
return prefix+"."+propertyName;
}
}
/**
* The hash of the partialProps (calculate once).
*/
final int partialHash;
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) {
}
final BeanProperty[] properties;
public void buildSelectExpressionChain(List<String> selectChain){
if (readId){
idBinder.buildSelectExpressionChain(prefix, selectChain);
}
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].buildSelectExpressionChain(prefix, selectChain);
}
// recursively continue reading...
for (int i = 0; i < children.length; i++) {
// read each child... and let them set their
// values back to this localBean
children[i].buildSelectExpressionChain(selectChain);
}
}
/**
* read the properties from the resultSet.
*/
public void load(DbReadContext ctx, Object parentBean) throws SQLException {
/**
* Extra where clause added by Where annotation on associated many.
*/
final String extraWhere;
// bean already existing in the persistence context
Object contextBean = null;
Class<?> localType;
BeanDescriptor<?> localDesc;
IdBinder localIdBinder;
Object localBean;
if (inheritInfo != null){
InheritInfo localInfo = inheritInfo.readType(ctx);
if (localInfo == null){
// the bean must be null
localIdBinder = idBinder;
localBean = null;
localType = null;
localDesc = desc;
} else {
localBean = localInfo.createBean(ctx.isVanillaMode());
localType = localInfo.getType();
localIdBinder = localInfo.getIdBinder();
localDesc = localInfo.getBeanDescriptor();
}
} else {
localType = null;
localDesc = desc;
localBean = desc.createBean(ctx.isVanillaMode());
localIdBinder = idBinder;
}
Mode queryMode = ctx.getQueryMode();
PersistenceContext persistenceContext = ctx.getPersistenceContext();
Object id = null;
if (!readId){
// report type bean... or perhaps excluding the id for SqlSelect?
} else {
id = localIdBinder.readSet(ctx, localBean);
if (id == null){
// bean must be null...
localBean = null;
} else {
// check the PersistenceContext to see if the bean already exists
contextBean = persistenceContext.putIfAbsent(id, localBean);
if (contextBean == null){
// bean just added to the persistenceContext
contextBean = localBean;
} else {
// bean already exists in persistenceContext
if (queryMode.isLoadContextBean()){
// refresh it anyway (lazy loading for example)
localBean = contextBean;
if (localBean instanceof EntityBean){
// temporarily turn off interception during load
((EntityBean)localBean)._ebean_getIntercept().setIntercepting(false);
}
} else {
// ignore the DB data...
localBean = null;
}
}
}
}
final BeanPropertyAssoc<?> nodeBeanProp;
ctx.setCurrentPrefix(prefix, pathMap);
ctx.propagateState(localBean);
SqlBeanLoad sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
if (inheritInfo == null){
// normal behaviour with no inheritance
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].load(sqlBeanLoad);
}
} else {
// take account of inheritance and due to subclassing approach
// need to get a 'local' version of the property
for (int i = 0, x = properties.length; i < x; i++) {
// get a local version of the BeanProperty
BeanProperty p = localDesc.getBeanProperty(properties[i].getName());
if (p != null){
p.load(sqlBeanLoad);
} else {
properties[i].loadIgnore(ctx);
}
}
}
for (int i = 0, x = tableJoins.length; i < x; i++) {
tableJoins[i].load(sqlBeanLoad);
}
final TableJoin[] tableJoins;
boolean lazyLoadMany = false;
if (localBean == null && queryMode.equals(Mode.LAZYLOAD_MANY)){
// batch lazy load many into existing contextBean
localBean = contextBean;
lazyLoadMany = true;
}
// recursively continue reading...
for (int i = 0; i < children.length; i++) {
// read each child... and let them set their
// values back to this localBean
children[i].load(ctx, localBean);
}
/**
* False if report bean and has no id property.
*/
final boolean readId;
if (lazyLoadMany){
// special case where we load children
} else if (localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
if (!ctx.isVanillaMode()){
// only create lazy loading collection proxies
// when not in vanilla mode
createListProxies(localDesc, ctx, localBean);
}
localDesc.postLoad(localBean, includedProps);
final boolean disableLazyLoad;
final InheritInfo inheritInfo;
final String prefix;
final Set<String> includedProps;
final Map<String, String> pathMap;
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, SqlTreeProperties props,
List<SqlTreeNode> myChildren, boolean withId) {
this(prefix, beanProp, beanProp.getTargetDescriptor(), props, myChildren, withId);
}
/**
* Create with the appropriate node.
*/
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, BeanDescriptor<?> desc,
SqlTreeProperties props, List<SqlTreeNode> myChildren, boolean withId) {
this.prefix = prefix;
this.nodeBeanProp = beanProp;
this.desc = desc;
this.inheritInfo = desc.getInheritInfo();
this.extraWhere = (beanProp == null) ? null : beanProp.getExtraWhere();
this.idBinder = desc.getIdBinder();
// the bean has an Id property and we want to use it
this.readId = withId && (desc.propertiesId().length > 0);
this.disableLazyLoad = !readId || desc.isSqlSelectBased();
this.tableJoins = props.getTableJoins();
this.partialObject = props.isPartialObject();
this.partialProps = props.getIncludedProperties();
this.partialHash = partialObject ? partialProps.hashCode() : 0;
this.readOnlyLeaf = props.isReadOnly();
this.properties = props.getProps();
if (partialObject) {
// merge the explicit partialProps with the implicitly added
// list proxies (that are added by createListProxies()) to get
// the full set of 'loaded' properties for this bean.
includedProps = LoadedPropertiesCache.get(partialHash, partialProps, desc);
} else {
includedProps = null;
}
if (myChildren == null) {
children = NO_CHILDREN;
} else {
children = myChildren.toArray(new SqlTreeNode[myChildren.size()]);
}
pathMap = createPathMap(prefix, desc);
}
private Map<String, String> createPathMap(String prefix, BeanDescriptor<?> desc) {
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
HashMap<String, String> m = new HashMap<String, String>();
for (int i = 0; i < manys.length; i++) {
String name = manys[i].getName();
m.put(name, getPath(prefix, name));
}
return m;
}
private String getPath(String prefix, String propertyName) {
if (prefix == null) {
return propertyName;
} else {
return prefix + "." + propertyName;
}
}
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) {
}
public void buildSelectExpressionChain(List<String> selectChain) {
if (readId) {
idBinder.buildSelectExpressionChain(prefix, selectChain);
}
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].buildSelectExpressionChain(prefix, selectChain);
}
// recursively continue reading...
for (int i = 0; i < children.length; i++) {
// read each child... and let them set their
// values back to this localBean
children[i].buildSelectExpressionChain(selectChain);
}
}
/**
* read the properties from the resultSet.
*/
public void load(DbReadContext ctx, Object parentBean) throws SQLException {
// bean already existing in the persistence context
Object contextBean = null;
Class<?> localType;
BeanDescriptor<?> localDesc;
IdBinder localIdBinder;
Object localBean;
if (inheritInfo != null) {
InheritInfo localInfo = inheritInfo.readType(ctx);
if (localInfo == null) {
// the bean must be null
localIdBinder = idBinder;
localBean = null;
localType = null;
localDesc = desc;
} else {
localBean = localInfo.createBean(ctx.isVanillaMode());
localType = localInfo.getType();
localIdBinder = localInfo.getIdBinder();
localDesc = localInfo.getBeanDescriptor();
}
} else {
localType = null;
localDesc = desc;
localBean = desc.createBean(ctx.isVanillaMode());
localIdBinder = idBinder;
}
Mode queryMode = ctx.getQueryMode();
PersistenceContext persistenceContext = ctx.getPersistenceContext();
Object id = null;
if (!readId) {
// report type bean... or perhaps excluding the id for SqlSelect?
} else {
id = localIdBinder.readSet(ctx, localBean);
if (id == null) {
// bean must be null...
localBean = null;
} else {
// check the PersistenceContext to see if the bean already exists
contextBean = persistenceContext.putIfAbsent(id, localBean);
if (contextBean == null) {
// bean just added to the persistenceContext
contextBean = localBean;
} else {
// bean already exists in persistenceContext
if (queryMode.isLoadContextBean()) {
// refresh it anyway (lazy loading for example)
localBean = contextBean;
if (localBean instanceof EntityBean) {
EntityBeanIntercept ebi = ((EntityBean)localBean)._ebean_getIntercept();
ebi.setPersistenceContext(persistenceContext);
ebi.setLoadedProps(includedProps);
if (Mode.LAZYLOAD_BEAN.equals(queryMode)) {
// Lazy Load does not reset the dirty state
ebi.setLoadedLazy();
} else {
// normal bean loading
ebi.setLoaded();
}
if (partialObject) {
ctx.register(null, ebi);
}
if (disableLazyLoad) {
// bean does not have an Id or is SqlSelect based
ebi.setDisableLazyLoad(true);
}
if (ctx.isAutoFetchProfiling()) {
// collect autofetch profiling for this bean...
ctx.profileBean(ebi, prefix);
}
// temporarily turn off interception during load
((EntityBean) localBean)._ebean_getIntercept().setIntercepting(false);
}
}
if (parentBean != null && contextBean != null) {
// set this back to the parentBean
nodeBeanProp.setValue(parentBean, contextBean);
}
} else {
// ignore the DB data...
localBean = null;
}
}
}
}
if (!readId){
// a bean with no Id (never found in context)
postLoad(ctx, localBean, id);
} else {
// return the contextBean which is either the localBean
// read from the resultSet and put into the context OR
// the 'matching' bean that already existed in the context
postLoad(ctx, contextBean, id);
}
}
ctx.setCurrentPrefix(prefix, pathMap);
/**
* Create lazy loading proxies for the Many's except for the one that is
* included in the actual query.
*/
private void createListProxies(BeanDescriptor<?> localDesc, DbReadContext ctx, Object localBean) {
BeanPropertyAssocMany<?> fetchedMany = ctx.getManyProperty();
ctx.propagateState(localBean);
// load the List/Set/Map proxy objects (deferred fetching of lists)
BeanPropertyAssocMany<?>[] manys = localDesc.propertiesMany();
for (int i = 0; i < manys.length; i++) {
SqlBeanLoad sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
if (fetchedMany != null && fetchedMany.equals(manys[i])) {
// this many property is included in the query...
// it is being loaded with real row data (result[1])
} else {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = manys[i].createReferenceIfNull(localBean);
if (ref != null){
ctx.register(manys[i].getName(), ref);
}
}
}
}
if (inheritInfo == null) {
// normal behavior with no inheritance
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].load(sqlBeanLoad);
}
/**
* Append the property columns to the buffer.
*/
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
ctx.pushJoin(prefix);
ctx.pushTableAlias(prefix);
} else {
// take account of inheritance and due to subclassing approach
// need to get a 'local' version of the property
for (int i = 0, x = properties.length; i < x; i++) {
// get a local version of the BeanProperty
BeanProperty p = localDesc.getBeanProperty(properties[i].getName());
if (p != null) {
p.load(sqlBeanLoad);
} else {
properties[i].loadIgnore(ctx);
}
}
}
if (nodeBeanProp != null) {
ctx.append(NEW_LINE).append(" ");
}
if (!subQuery && inheritInfo != null){
ctx.appendColumn(inheritInfo.getDiscriminatorColumn());
}
if (readId) {
appendSelect(ctx, false, idBinder.getProperties());
}
appendSelect(ctx, subQuery, properties);
appendSelectTableJoins(ctx);
for (int i = 0, x = tableJoins.length; i < x; i++) {
tableJoins[i].load(sqlBeanLoad);
}
for (int i = 0; i < children.length; i++) {
// read each child... and let them set their
// values back to this localBean
children[i].appendSelect(ctx, subQuery);
}
ctx.popTableAlias();
ctx.popJoin();
}
boolean lazyLoadMany = false;
if (localBean == null && queryMode.equals(Mode.LAZYLOAD_MANY)) {
// batch lazy load many into existing contextBean
localBean = contextBean;
lazyLoadMany = true;
}
private void appendSelectTableJoins(DbSqlContext ctx) {
// recursively continue reading...
for (int i = 0; i < children.length; i++) {
// read each child... and let them set their
// values back to this localBean
children[i].load(ctx, localBean);
}
String baseAlias = ctx.getTableAlias(prefix);
for (int i = 0; i < tableJoins.length; i++) {
TableJoin join = tableJoins[i];
if (lazyLoadMany) {
// special case where we load children
String alias = baseAlias+i;
} else if (localBean != null) {
ctx.pushSecondaryTableAlias(alias);
join.appendSelect(ctx, false);
ctx.popTableAlias();
}
}
ctx.setCurrentPrefix(prefix, pathMap);
if (!ctx.isVanillaMode()) {
// only create lazy loading collection proxies
// when not in vanilla mode
createListProxies(localDesc, ctx, localBean);
}
/**
* Append the properties to the buffer.
*/
private void appendSelect(DbSqlContext ctx, boolean subQuery, BeanProperty[] props) {
localDesc.postLoad(localBean, includedProps);
for (int i = 0; i < props.length; i++) {
props[i].appendSelect(ctx, subQuery);
}
}
public void appendWhere(DbSqlContext ctx) {
if (localBean instanceof EntityBean) {
EntityBeanIntercept ebi = ((EntityBean) localBean)._ebean_getIntercept();
ebi.setPersistenceContext(persistenceContext);
ebi.setLoadedProps(includedProps);
if (Mode.LAZYLOAD_BEAN.equals(queryMode)) {
// Lazy Load does not reset the dirty state
ebi.setLoadedLazy();
} else {
// normal bean loading
ebi.setLoaded();
}
if (inheritInfo != null) {
if (inheritInfo.isRoot()) {
// at root of hierarchy so don't bother
// adding a where clause because we want
// all the types...
} else {
// restrict to this type and
// sub types of this type.
if (ctx.length() > 0){
ctx.append(" and");
}
ctx.append(" ").append(ctx.getTableAlias(prefix)).append(".");//tableAlias
ctx.append(inheritInfo.getWhere()).append(" ");
}
}
if (extraWhere != null){
if (ctx.length() > 0){
ctx.append(" and");
}
String ta = ctx.getTableAlias(prefix);
String ew = StringHelper.replaceString(extraWhere, "${ta}", ta);
ctx.append(" ").append(ew).append(" ");
}
for (int i = 0; i < children.length; i++) {
// recursively add to the where clause any
// fixed predicates (extraWhere etc)
children[i].appendWhere(ctx);
}
}
/**
* Append to the FROM clause for this node.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
if (partialObject) {
ctx.register(null, ebi);
}
ctx.pushJoin(prefix);
ctx.pushTableAlias(prefix);
if (disableLazyLoad) {
// bean does not have an Id or is SqlSelect based
ebi.setDisableLazyLoad(true);
}
if (ctx.isAutoFetchProfiling()) {
// collect autofetch profiling for this bean...
ctx.profileBean(ebi, prefix);
}
}
forceOuterJoin = appendFromBaseTable(ctx, forceOuterJoin);
for (int i = 0; i < properties.length; i++) {
// usually nothing... except for 1-1 Exported
properties[i].appendFrom(ctx, forceOuterJoin);
}
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, forceOuterJoin);
}
ctx.popTableAlias();
ctx.popJoin();
}
/**
* Join to base table for this node. This includes a join to
* the intersection table if this is a ManyToMany node.
*/
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
if (nodeBeanProp instanceof BeanPropertyAssocMany<?>){
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>)nodeBeanProp;
if (manyProp.isManyToMany()){
String alias = ctx.getTableAlias(prefix);
String[] split = SplitName.split(prefix);
String parentAlias = ctx.getTableAlias(split[0]);
String alias2 = alias+"z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx);
return nodeBeanProp.addJoin(forceOuterJoin, alias2, alias, ctx);
}
}
return nodeBeanProp.addJoin(forceOuterJoin, prefix, ctx);
}
}
if (parentBean != null && contextBean != null) {
// set this back to the parentBean
nodeBeanProp.setValue(parentBean, contextBean);
}
/**
* Summary description.
*/
public String toString() {
return "SqlTreeNodeBean: " + desc;
}
if (!readId) {
// a bean with no Id (never found in context)
postLoad(ctx, localBean, id);
} else {
// return the contextBean which is either the localBean
// read from the resultSet and put into the context OR
// the 'matching' bean that already existed in the context
postLoad(ctx, contextBean, id);
}
}
/**
* Create lazy loading proxies for the Many's except for the one that is
* included in the actual query.
*/
private void createListProxies(BeanDescriptor<?> localDesc, DbReadContext ctx, Object localBean) {
BeanPropertyAssocMany<?> fetchedMany = ctx.getManyProperty();
// load the List/Set/Map proxy objects (deferred fetching of lists)
BeanPropertyAssocMany<?>[] manys = localDesc.propertiesMany();
for (int i = 0; i < manys.length; i++) {
if (fetchedMany != null && fetchedMany.equals(manys[i])) {
// this many property is included in the query...
// it is being loaded with real row data (result[1])
} else {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = manys[i].createReferenceIfNull(localBean);
if (ref != null) {
ctx.register(manys[i].getName(), ref);
}
}
}
}
/**
* Append the property columns to the buffer.
*/
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
ctx.pushJoin(prefix);
ctx.pushTableAlias(prefix);
if (!subQuery && inheritInfo != null) {
ctx.appendColumn(inheritInfo.getDiscriminatorColumn());
}
if (readId) {
appendSelect(ctx, false, idBinder.getProperties());
}
appendSelect(ctx, subQuery, properties);
appendSelectTableJoins(ctx);
for (int i = 0; i < children.length; i++) {
// read each child... and let them set their
// values back to this localBean
children[i].appendSelect(ctx, subQuery);
}
ctx.popTableAlias();
ctx.popJoin();
}
private void appendSelectTableJoins(DbSqlContext ctx) {
String baseAlias = ctx.getTableAlias(prefix);
for (int i = 0; i < tableJoins.length; i++) {
TableJoin join = tableJoins[i];
String alias = baseAlias + i;
ctx.pushSecondaryTableAlias(alias);
join.appendSelect(ctx, false);
ctx.popTableAlias();
}
}
/**
* Append the properties to the buffer.
*/
private void appendSelect(DbSqlContext ctx, boolean subQuery, BeanProperty[] props) {
for (int i = 0; i < props.length; i++) {
props[i].appendSelect(ctx, subQuery);
}
}
public void appendWhere(DbSqlContext ctx) {
if (inheritInfo != null) {
if (inheritInfo.isRoot()) {
// at root of hierarchy so don't bother
// adding a where clause because we want
// all the types...
} else {
// restrict to this type and
// sub types of this type.
if (ctx.length() > 0) {
ctx.append(" and");
}
ctx.append(" ").append(ctx.getTableAlias(prefix)).append(".");// tableAlias
ctx.append(inheritInfo.getWhere()).append(" ");
}
}
if (extraWhere != null) {
if (ctx.length() > 0) {
ctx.append(" and");
}
String ta = ctx.getTableAlias(prefix);
String ew = StringHelper.replaceString(extraWhere, "${ta}", ta);
ctx.append(" ").append(ew).append(" ");
}
for (int i = 0; i < children.length; i++) {
// recursively add to the where clause any
// fixed predicates (extraWhere etc)
children[i].appendWhere(ctx);
}
}
/**
* Append to the FROM clause for this node.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
ctx.pushJoin(prefix);
ctx.pushTableAlias(prefix);
forceOuterJoin = appendFromBaseTable(ctx, forceOuterJoin);
for (int i = 0; i < properties.length; i++) {
// usually nothing... except for 1-1 Exported
properties[i].appendFrom(ctx, forceOuterJoin);
}
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, forceOuterJoin);
}
ctx.popTableAlias();
ctx.popJoin();
}
/**
* Join to base table for this node. This includes a join to the intersection
* table if this is a ManyToMany node.
*/
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
if (nodeBeanProp instanceof BeanPropertyAssocMany<?>) {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) nodeBeanProp;
if (manyProp.isManyToMany()) {
String alias = ctx.getTableAlias(prefix);
String[] split = SplitName.split(prefix);
String parentAlias = ctx.getTableAlias(split[0]);
String alias2 = alias + "z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx);
return nodeBeanProp.addJoin(forceOuterJoin, alias2, alias, ctx);
}
}
return nodeBeanProp.addJoin(forceOuterJoin, prefix, ctx);
}
/**
* Summary description.
*/
public String toString() {
return "SqlTreeNodeBean: " + desc;
}
}
@@ -9,33 +9,32 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
public SqlTreeNodeManyRoot(String prefix, BeanPropertyAssocMany<?> prop, SqlTreeProperties props, List<SqlTreeNode> myList) {
super(prefix, prop, prop.getTargetDescriptor(), props, myList, true);
}
public SqlTreeNodeManyRoot(String prefix, BeanPropertyAssocMany<?> prop, SqlTreeProperties props, List<SqlTreeNode> myList) {
super(prefix, prop, prop.getTargetDescriptor(), props, myList, true);
}
@Override
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) {
// put the localBean into the manyValue so that it
// is added to the collection/map
cquery.setLoadedManyBean(loadedBean);
}
@Override
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) {
@Override
public void load(DbReadContext cquery, Object parentBean) throws SQLException {
// pass in null for parentBean because the localBean
// that is built is added to a collection rather than
// being set to the parentBean directly
super.load(cquery, null);
}
// put the localBean into the manyValue so that it
// is added to the collection/map
cquery.setLoadedManyBean(loadedBean);
}
@Override
public void load(DbReadContext cquery, Object parentBean) throws SQLException {
// pass in null for parentBean because the localBean
// that is built is added to a collection rather than
// being set to the parentBean directly
super.load(cquery, null);
}
/**
* Force outer join for everything after the many property.
*/
@Override
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
super.appendFrom(ctx, true);
}
/**
* Force outer join for everything after the many property.
*/
@Override
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
super.appendFrom(ctx, true);
}
}
@@ -5,13 +5,16 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.LogLevel;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.DerivedRelationshipData;
@@ -19,8 +22,6 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEvent;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.ebeaninternal.server.transaction.TransactionManager.OnQueryOnly;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JDBC Connection based transaction.
@@ -29,6 +30,8 @@ public class JdbcTransaction implements SpiTransaction {
private static final Logger logger = LoggerFactory.getLogger(JdbcTransaction.class);
private static final Object PLACEHOLDER = new Object();
private static final String illegalStateMessage = "Transaction is Inactive";
/**
@@ -110,20 +113,18 @@ public class JdbcTransaction implements SpiTransaction {
Boolean batchFlushOnMixed;
String logPrefix;
/**
* The depth used by batch processing to help the ordering of statements.
*/
int depth = 0;
HashSet<Object> persistingBeans = new HashSet<Object>();
IdentityHashMap<Object,Object> persistingBeans;
HashSet<Integer> deletingBeansHash;
HashMap<String,String> m2mIntersectionSave;
TransactionLogBuffer logBuffer;
HashMap<Integer, List<DerivedRelationshipData>> derivedRelMap;
private final Map<String, Object> userObjects = new ConcurrentHashMap<String, Object>();
Map<String, Object> userObjects;
/**
* Create a new JdbcTransaction.
@@ -132,6 +133,7 @@ public class JdbcTransaction implements SpiTransaction {
try {
this.active = true;
this.id = id;
this.logPrefix = deriveLogPrefix(id,null);
this.explicit = explicit;
this.logLevel = logLevel;
this.manager = manager;
@@ -143,15 +145,34 @@ public class JdbcTransaction implements SpiTransaction {
this.onQueryOnly = manager == null ? OnQueryOnly.ROLLBACK : manager.getOnQueryOnly();
this.persistenceContext = new DefaultPersistenceContext();
this.logBuffer = new TransactionLogBuffer(50, id);
} catch (Exception e) {
throw new PersistenceException(e);
}
}
private static String deriveLogPrefix(String id, String label) {
StringBuilder sb = new StringBuilder();
sb.append("txn[");
if (id != null) {
sb.append(id);
}
sb.append("] ");
if (label != null) {
sb.append("label[").append(label).append("] ");
}
return sb.toString();
}
public void setLabel(String label) {
this.logPrefix = deriveLogPrefix(id,label);
}
public String getLogPrefix() {
return logPrefix;
}
public String toString() {
return "Trans[" + id + "]";
return logPrefix;
}
public List<DerivedRelationshipData> getDerivedRelationship(Object bean) {
@@ -215,13 +236,16 @@ public class JdbcTransaction implements SpiTransaction {
public void unregisterBean(Object bean) {
persistingBeans.remove(bean);
}
/**
* Return true if this is a bean that has already been saved. This will
* register the bean if it is not already.
*/
public boolean isRegisteredBean(Object bean) {
return !persistingBeans.add(bean);
if (persistingBeans == null) {
persistingBeans = new IdentityHashMap<Object,Object>();
}
return (persistingBeans.put(bean,PLACEHOLDER) != null);
}
/**
@@ -447,11 +471,11 @@ public class JdbcTransaction implements SpiTransaction {
}
public boolean isLogSql() {
return logLevel.ordinal() >= LogLevel.SQL.ordinal();
return TransactionManager.SQL_LOGGER.isDebugEnabled();
}
public boolean isLogSummary() {
return logLevel.ordinal() >= LogLevel.SUMMARY.ordinal();
return TransactionManager.SUM_LOGGER.isDebugEnabled();
}
public LogLevel getLogLevel() {
@@ -461,28 +485,13 @@ public class JdbcTransaction implements SpiTransaction {
public void setLogLevel(LogLevel logLevel) {
this.logLevel = logLevel;
}
/**
* Log a message to the transaction log - for PUBLIC use.
*/
public void log(String msg) {
if (isLogSummary()) {
logInternal(msg);
}
public void logSql(String msg) {
TransactionManager.SQL_LOGGER.trace(logPrefix+msg);
}
/**
* Log a message to the transaction log - for Ebean INTERNAL use. The LogLevel
* should be explicitly checked before calling this method.
*/
public void logInternal(String msg) {
if (manager != null) {
if (logBuffer.add(msg)) {
// buffer full so flush it
manager.log(logBuffer);
logBuffer = logBuffer.newBuffer();
}
}
public void logSummary(String msg) {
TransactionManager.SUM_LOGGER.debug(logPrefix+msg);
}
/**
@@ -538,10 +547,6 @@ public class JdbcTransaction implements SpiTransaction {
active = false;
}
public TransactionLogBuffer getLogBuffer() {
return logBuffer;
}
/**
* Notify the transaction manager.
*/
@@ -682,10 +687,16 @@ public class JdbcTransaction implements SpiTransaction {
}
public void putUserObject(String name, Object value) {
if (userObjects == null) {
userObjects = new HashMap<String,Object>();
}
userObjects.put(name, value);
}
public Object getUserObject(String name) {
if (userObjects == null) {
return null;
}
return userObjects.get(name);
}
@@ -1,102 +0,0 @@
package com.avaje.ebeaninternal.server.transaction;
import java.util.ArrayList;
import java.util.List;
/**
* Buffer of transaction messages.
* <p>
* For performance reasons we add all the transaction messages to an instance of
* TransactionLogBuffer and then when the buffer is full or the transaction ends
* we send the buffer to the transaction manager to log.
* </p>
*
* @author rbygrave
*
*/
public class TransactionLogBuffer {
private final String transactionId;
private final ArrayList<LogEntry> buffer;
private final int maxSize;
private int currentSize;
/**
* Create the buffer with a maxSize and transaction id.
*/
public TransactionLogBuffer(int maxSize, String transactionId) {
this.maxSize = maxSize;
this.transactionId = transactionId;
this.buffer = new ArrayList<LogEntry>(maxSize);
}
/**
* Create new buffer using the same configuration.
*/
public TransactionLogBuffer newBuffer() {
return new TransactionLogBuffer(maxSize, transactionId);
}
/**
* Return the transaction id.
*/
public String getTransactionId() {
return transactionId;
}
/**
* Add a message to the buffer.
*/
public boolean add(String msg) {
buffer.add(new LogEntry(msg));
return ++currentSize >= maxSize;
}
/**
* Return true if the buffer is empty.
*/
public boolean isEmpty() {
return buffer.isEmpty();
}
/**
* Return all the messages.
*/
public List<LogEntry> messages() {
return buffer;
}
/**
* Entry in the buffer.
*/
public class LogEntry {
private final long timestamp;
private final String msg;
/**
* Construct an entry.
*/
public LogEntry(String msg) {
this.timestamp = System.currentTimeMillis();
this.msg = msg;
}
/**
* Return the time the entry was put into the buffer.
*/
public long getTimestamp() {
return timestamp;
}
/**
* Return the text of the message.
*/
public String getMsg() {
return msg;
}
}
}
@@ -1,39 +0,0 @@
package com.avaje.ebeaninternal.server.transaction;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.transaction.log.FileTransactionLoggerWrapper;
import com.avaje.ebeaninternal.server.transaction.log.JuliTransactionLogger;
/**
* Manages the transaction logs.
*/
public class TransactionLogManager {
private final TransactionLogWriter logWriter;
/**
* Create the TransactionLogger.
* <p>
* DevNote: This registers a shutdown hook to flush and close the
* sharedLogger. Alternate option would be to flush() the log after each
* write to the log.
* </p>
*/
public TransactionLogManager(ServerConfig serverConfig) {
if (serverConfig.isLoggingToJavaLogger()){
logWriter = new JuliTransactionLogger();
} else {
logWriter = new FileTransactionLoggerWrapper(serverConfig);
}
}
public void shutdown() {
logWriter.shutdown();
}
public void log(TransactionLogBuffer logBuffer) {
logWriter.log(logBuffer);
}
}
@@ -1,18 +0,0 @@
package com.avaje.ebeaninternal.server.transaction;
/**
* Write transaction log events to a file or other destination.
*/
public interface TransactionLogWriter {
/**
* Log all the messages in the buffer.
*/
public void log(TransactionLogBuffer logBuffer);
/**
* Shutdown the writer.
*/
public void shutdown();
}
@@ -8,9 +8,11 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.LogLevel;
import com.avaje.ebean.TxIsolation;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.event.TransactionEventListener;
@@ -21,8 +23,6 @@ import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Manages transactions.
@@ -35,6 +35,12 @@ public class TransactionManager {
private static final Logger logger = LoggerFactory.getLogger(TransactionManager.class);
public static final Logger SQL_LOGGER = LoggerFactory.getLogger("org.avaje.ebean.SQL");
public static final Logger SUM_LOGGER = LoggerFactory.getLogger("org.avaje.ebean.SUM");
public static final Logger TXN_LOGGER = LoggerFactory.getLogger("org.avaje.ebean.TXN");
/**
* The behaviour desired when ending a query only transaction.
*/
@@ -60,11 +66,6 @@ public class TransactionManager {
private LogLevel logLevel;
/**
* The logger.
*/
private final TransactionLogManager transLogger;
/**
* Prefix for transaction id's (logging).
*/
@@ -92,7 +93,7 @@ public class TransactionManager {
private final ClusterManager clusterManager;
private final int commitDebugLevel;
//private final int commitDebugLevel;
private final String serverName;
@@ -118,7 +119,6 @@ public class TransactionManager {
this.serverName = config.getName();
this.logLevel = config.getLoggingLevel();
this.transLogger = new TransactionLogManager(config);
this.backgroundExecutor = backgroundExecutor;
this.dataSource = config.getDataSource();
this.bulkEventListenerMap = new BulkEventListenerMap(config.getBulkTableEventListeners());
@@ -127,7 +127,6 @@ public class TransactionManager {
this.transactionEventListeners = transactionEventListeners.toArray(new TransactionEventListener[transactionEventListeners.size()]);
// log some transaction events using a java util logger
this.commitDebugLevel = GlobalProperties.getInt("ebean.commit.debuglevel", 0);
this.clusterDebugLevel = GlobalProperties.getInt("ebean.cluster.debuglevel", 0);
this.defaultBatchMode = config.isPersistBatching();
@@ -140,7 +139,7 @@ public class TransactionManager {
}
public void shutdown() {
transLogger.shutdown();
// Nothing to do
}
public BeanDescriptorManager getBeanDescriptorManager() {
@@ -251,19 +250,6 @@ public class TransactionManager {
return onQueryOnly;
}
/**
* Return the TransactionLogger used by this TransactionManager.
*/
public TransactionLogManager getLogger() {
return transLogger;
}
public void log(TransactionLogBuffer logBuffer){
if (!logBuffer.isEmpty()){
transLogger.log(logBuffer);
}
}
/**
* Wrap the externally supplied Connection.
*/
@@ -307,15 +293,6 @@ public class TransactionManager {
if (isolationLevel > -1) {
c.setTransactionIsolation(isolationLevel);
}
if (commitDebugLevel >= 3){
String msg = "Transaction ["+t.getId()+"] begin";
if (isolationLevel > -1){
TxIsolation txi = TxIsolation.fromLevel(isolationLevel);
msg += " isolationLevel["+txi+"]";
}
logger.info(msg);
}
return t;
@@ -345,10 +322,6 @@ public class TransactionManager {
if (defaultBatchMode){
t.setBatchMode(true);
}
if (commitDebugLevel >= 3){
logger.info("Transaction ["+t.getId()+"] begin - queryOnly");
}
return t;
@@ -375,61 +348,30 @@ public class TransactionManager {
public void notifyOfRollback(SpiTransaction transaction, Throwable cause) {
try {
for (TransactionEventListener listener : transactionEventListeners) {
if (TXN_LOGGER.isInfoEnabled()) {
String msg = transaction.getLogPrefix()+"Rollback";
if (cause != null){
msg += " error: "+formatThrowable(cause);
}
TXN_LOGGER.info(msg);
}
for (TransactionEventListener listener : transactionEventListeners) {
listener.postTransactionRollback(transaction, cause);
}
if (transaction.isLogSummary() || commitDebugLevel >= 1) {
String msg = "Rollback";
if (cause != null){
msg += " error: "+formatThrowable(cause);
}
if (transaction.isLogSummary()) {
transaction.logInternal(msg);
}
if (commitDebugLevel >= 1){
logger.info("Transaction ["+transaction.getId()+"] "+msg);
}
}
log(transaction.getLogBuffer());
} catch (Exception ex) {
String m = "Potentially Transaction Log incomplete due to error:";
logger.error(m, ex);
logger.error("Error while notifying TransactionEventListener of rollback event", ex);
}
}
/**
* Query only transaction in read committed isolation.
*/
public void notifyOfQueryOnly(boolean onCommit, SpiTransaction transaction, Throwable cause) {
try {
if (commitDebugLevel >= 2){
String msg;
if (onCommit){
msg = "Commit queryOnly";
} else {
msg = "Rollback queryOnly";
if (cause != null){
msg += " error: "+formatThrowable(cause);
}
}
if (transaction.isLogSummary()) {
transaction.logInternal(msg);
}
logger.info("Transaction ["+transaction.getId()+"] "+msg);
}
log(transaction.getLogBuffer());
} catch (Exception ex) {
String m = "Potentially Transaction Log incomplete due to error:";
logger.error(m, ex);
}
}
/**
* Query only transaction in read committed isolation.
*/
public void notifyOfQueryOnly(boolean onCommit, SpiTransaction transaction, Throwable cause) {
// Nothing that interesting here
}
private String formatThrowable(Throwable e){
if (e == null){
@@ -461,11 +403,16 @@ public class TransactionManager {
public void notifyOfCommit(SpiTransaction transaction) {
try {
log(transaction.getLogBuffer());
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction,
transaction.getEvent());
if (transaction.isExplicit()) {
if (TXN_LOGGER.isInfoEnabled()) {
TXN_LOGGER.info(transaction.getLogPrefix()+"Commit");
}
} else if (TXN_LOGGER.isDebugEnabled()) {
TXN_LOGGER.debug(transaction.getLogPrefix()+"Commit");
}
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction, transaction.getEvent());
postCommit.notifyLocalCacheIndex();
postCommit.notifyCluster();
@@ -476,10 +423,7 @@ public class TransactionManager {
for (TransactionEventListener listener : transactionEventListeners) {
listener.postTransactionCommit(transaction);
}
if (commitDebugLevel >= 1) {
logger.info("Transaction ["+transaction.getId()+"] commit");
}
} catch (Exception ex) {
String m = "NotifyOfCommit failed. Cache/Lucene potentially not notified.";
logger.error(m, ex);
@@ -1,382 +0,0 @@
package com.avaje.ebeaninternal.server.transaction.log;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer;
import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer.LogEntry;
import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default transaction logger implementation.
* <p>
* File based logger that can switch daily. It will include the date in the
* files name.
* </p>
* <p>
* Administration Note: If log file switching fails it will send the error to
* standard out and standard err print streams. This is assumed to be rather
* unlikely but possible.
* </p>
*/
public class FileTransactionLogger implements Runnable, TransactionLogWriter {
private static final Logger logger = LoggerFactory.getLogger(FileTransactionLogger.class);
/**
* Used to print stack trace.
*/
private static final String atString = " at ";
/**
* The newLineChar used instead of NL or CRNL for printing stack traces.
*/
private final String newLinePlaceholder = "\\r\\n";
/**
* The maximum number of stack lines to output. This is just for transaction
* logging so 5 is fine.
*/
private final int maxStackTraceLines = 5;
/**
* Queue that transactions put their LogBuffers onto and this LogBufferWriter
* pulls LogBuffers from.
*/
private final ConcurrentLinkedQueue<TransactionLogBuffer> logBufferQueue = new ConcurrentLinkedQueue<TransactionLogBuffer>();
private final Object queueMonitor = new Object();
/**
* Thread that is the sole writer of logBuffer entries to the file.
*/
private final Thread logWriterThread;
private final String threadName;
/**
* The path of the log file.
*/
private final String filepath;
/**
* The delimiter to use.
*/
private final String deliminator = ", ";
/**
* The prefix of the log file name.
*/
private final String logFileName;
/**
* The file suffix for the logs.
*/
private final String logFileSuffix;
/**
* Shutdown flag.
*/
private volatile boolean shutdown;
private volatile boolean shutdownComplete;
/**
* The output stream.
*/
private PrintStream out;
/**
* The current file path.
*/
private String currentPath;
/**
* Counter thats incremented when maxBytesPerFile is hit.
*/
private int fileCounter;
/**
* Roughly the max bytes written to a file before we switch. Switch Daily and
* on hitting max bytes.
*/
private long maxBytesPerFile;
/**
* Roughly the bytes written to a file.
*/
private long bytesWritten;
public FileTransactionLogger(String threadName, String dir, String logFileName, int maxBytesPerFile) {
this(threadName, dir, logFileName, "log", maxBytesPerFile);
}
public FileTransactionLogger(String threadName, String dir, String logFileName, String suffix, int maxBytesPerFile) {
this.threadName = threadName;
this.logFileName = logFileName;
this.logFileSuffix = "." + suffix;
this.maxBytesPerFile = maxBytesPerFile;
try {
// get the directory where the log files are going to go
filepath = makeDirIfRequired(dir);
switchFile(LogTime.nextDay());
} catch (Exception e) {
System.out.println("FATAL ERROR: init of FileLogger: " + e.getMessage());
System.err.println("FATAL ERROR: init of FileLogger: " + e.getMessage());
throw new RuntimeException(e);
}
logWriterThread = new Thread(this, threadName);
logWriterThread.setDaemon(true);
}
protected void finalize() throws Throwable {
close();
super.finalize();
}
public void start() {
logWriterThread.start();
}
public void shutdown() {
shutdown = true;
synchronized (logWriterThread) {
try {
// wait max 20 seconds
logWriterThread.wait(20000);
logger.debug("Shutdown LogBufferWriter " + threadName + " shutdownComplete:" + shutdownComplete);
} catch (InterruptedException e) {
logger.debug("InterruptedException:" + e);
}
}
if (!shutdownComplete) {
String m = "WARNING: Shutdown of LogBufferWriter " + threadName + " not completed.";
System.err.println(m);
logger.warn(m);
}
}
public void run() {
int missCount = 0;
while (!shutdown || missCount < 10) {
if (missCount > 50) {
if (out != null) {
out.flush();
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
logger.info("Interrupted TxnLogBufferWriter", e);
}
}
synchronized (queueMonitor) {
if (logBufferQueue.isEmpty()) {
++missCount;
} else {
TransactionLogBuffer buffer = logBufferQueue.remove();
write(buffer);
missCount = 0;
}
}
}
close();
shutdownComplete = true;
synchronized (logWriterThread) {
logWriterThread.notifyAll();
}
}
public void log(TransactionLogBuffer logBuffer) {
logBufferQueue.add(logBuffer);
}
private void write(TransactionLogBuffer logBuffer) {
// check to see if we need to switch file?
LogTime logTime = LogTime.get();
if (logTime.isNextDay()) {
logTime = LogTime.nextDay();
switchFile(logTime);
}
if (bytesWritten > maxBytesPerFile) {
++fileCounter;
switchFile(logTime);
}
String txnId = logBuffer.getTransactionId();
List<LogEntry> messages = logBuffer.messages();
for (int i = 0; i < messages.size(); i++) {
LogEntry msg = messages.get(i);
printMessage(logTime, txnId, msg);
}
}
private void printMessage(LogTime logTime, String txnId, LogEntry logEntry) {
String msg = logEntry.getMsg();
int len = msg.length();
if (len == 0) {
return;
}
// add overhead + content
bytesWritten += 16;
bytesWritten += len;
if (txnId != null) {
bytesWritten += 7;
bytesWritten += txnId.length();
out.append("txn[");
out.append(txnId);
out.append("]");
out.append(deliminator);
}
out.append(logTime.getTimestamp(logEntry.getTimestamp()));
out.append(deliminator);
out.append(msg).append(" ");
out.append("\n");
}
/**
* Recursively output the Throwable stack trace to the log.
*
* @param sb
* the buffer to write the stack trace to
* @param e
* the source throwable
* @param isCause
* flag to indicate if this is the top level throwable or a cause
*/
protected void printThrowable(StringBuilder sb, Throwable e, boolean isCause) {
if (e != null) {
if (isCause) {
sb.append("Caused by: ");
}
sb.append(e.getClass().getName());
sb.append(":");
sb.append(e.getMessage()).append(newLinePlaceholder);
StackTraceElement[] ste = e.getStackTrace();
int outputStackLines = ste.length;
int notShownCount = 0;
if (ste.length > maxStackTraceLines) {
outputStackLines = maxStackTraceLines;
notShownCount = ste.length - outputStackLines;
}
for (int i = 0; i < outputStackLines; i++) {
sb.append(atString);
sb.append(ste[i].toString()).append(newLinePlaceholder);
}
if (notShownCount > 0) {
sb.append(" ... ");
sb.append(notShownCount);
sb.append(" more").append(newLinePlaceholder);
}
Throwable cause = e.getCause();
if (cause != null) {
printThrowable(sb, cause, true);
}
}
}
private String newFileName(LogTime logTime) {
return filepath + File.separator + logFileName + logTime.getYMD() + "-" + fileCounter + logFileSuffix;
}
/**
* Switch the file to log to.
*/
protected void switchFile(LogTime logTime) {
try {
long currentFileLength = 0;
String newFilePath = null;
// skip a file if it already has max bytes
do {
newFilePath = newFileName(logTime);
File f = new File(newFilePath);
if (!f.exists()) {
currentFileLength = 0;
} else {
if (f.length() < maxBytesPerFile * 0.8) {
currentFileLength = f.length();
} else {
++fileCounter;
newFilePath = null;
}
}
} while (newFilePath == null);
if (!newFilePath.equals(currentPath)) {
PrintStream newOut = new PrintStream(new BufferedOutputStream(new FileOutputStream(newFilePath, true)));
close();
bytesWritten = currentFileLength;
currentPath = newFilePath;
out = newOut;
}
} catch (IOException e) {
e.printStackTrace();
logger.error("Error switch log file", e);
}
}
/**
* Close the file output stream being used for logging.
*/
private void close() {
if (out != null) {
out.flush();
out.close();
}
}
/**
* Returns the directory path of the log file.
*/
protected String makeDirIfRequired(String dir) {
File f = new File(dir);
if (f.exists()) {
if (!f.isDirectory()) {
String msg = "Transaction logs directory is a file? " + dir;
throw new PersistenceException(msg);
}
} else {
if (!f.mkdirs()) {
String msg = "Failed to create transaction logs directory " + dir;
logger.error(msg);
}
}
return dir;
}
}
@@ -1,82 +0,0 @@
package com.avaje.ebeaninternal.server.transaction.log;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer;
import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Wraps a FileTransactionLogger to provide delayed initialisation.
* <p>
* This means that it should only create the log file WHEN there is something
* actually logged. This means the logLevel can start at NONE and then change at
* runtime (and the log file will then be initialised). NOTE that a volatile
* with double checked locking is used to make this transition thread safe.
* </p>
*
* @author rbygrave
*
*/
public class FileTransactionLoggerWrapper implements TransactionLogWriter {
private static final Logger logger = LoggerFactory.getLogger(FileTransactionLoggerWrapper.class);
private final String serverName;
private final String dir;
private final int maxFileSize;
private volatile FileTransactionLogger logWriter;
public FileTransactionLoggerWrapper(ServerConfig serverConfig) {
String evalDir = serverConfig.getLoggingDirectoryWithEval();
this.dir = evalDir != null ? evalDir : "logs";
this.maxFileSize = GlobalProperties.getInt("ebean.logging.maxFileSize", 100 * 1024 * 1024);
this.serverName = serverConfig.getName();
}
private FileTransactionLogger initialiseLogger() {
synchronized (this) {
// double check locking here so logWriter NEEDS to be volatile!!
FileTransactionLogger writer = this.logWriter;
if (writer != null) {
return writer;
}
String middleName = GlobalProperties.get("ebean.logging.filename", "_txn_");
String logPrefix = serverName + middleName;
String threadName = "Ebean-" + serverName + "-TxnLogWriter";
// create the real logger and start it
FileTransactionLogger newLogWriter = new FileTransactionLogger(threadName, dir, logPrefix, maxFileSize);
// assignment of volatile field
this.logWriter = newLogWriter;
// start background thread for the writer
newLogWriter.start();
logger.info("Transaction logs in: " + dir);
return newLogWriter;
}
}
public void log(TransactionLogBuffer logBuffer) {
// volatile read
FileTransactionLogger writer = this.logWriter;
if (writer == null) {
writer = initialiseLogger();
}
writer.log(logBuffer);
}
public void shutdown() {
if (logWriter != null) {
logWriter.shutdown();
}
}
}
@@ -1,46 +0,0 @@
package com.avaje.ebeaninternal.server.transaction.log;
import java.util.List;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer;
import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer.LogEntry;
import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter;
import java.util.logging.Logger;
/**
* A transactionLogger that uses a java.util.logging.Logger.
* <p>
* See {@link ServerConfig#setUseJuliTransactionLogger(boolean)}
* </p>
* @author rbygrave
*/
public class JuliTransactionLogger implements TransactionLogWriter {
private static final Logger logger = Logger.getLogger(JuliTransactionLogger.class.getName());
public void log(TransactionLogBuffer logBuffer) {
String txnId = logBuffer.getTransactionId();
List<LogEntry> messages = logBuffer.messages();
for (int i = 0; i < messages.size(); i++) {
LogEntry logEntry = messages.get(i);
log(txnId, logEntry);
}
}
public void shutdown() {
}
private void log(String txnId, LogEntry entry) {
String message = entry.getMsg();
if (txnId != null && message != null && !message.startsWith("Trans[")){
message = "Trans["+txnId+"] "+message;
}
logger.info(message);
}
}
@@ -1,173 +0,0 @@
package com.avaje.ebeaninternal.server.transaction.log;
import java.util.Calendar;
import java.util.GregorianCalendar;
/**
* Utility object used to in logging time.
* <p>
* Used in place of SimpleDateFormat which requires synchronization. Determines
* if the day has changed and returns a format of the current time and day.
* </p>
*/
public class LogTime {
private static final String[] sep = { ":", "." };
private static LogTime day;
static {
day = new LogTime();
}
public static LogTime get() {
return day;
}
public static LogTime nextDay() {
LogTime d = new LogTime();
day = d;
return d;
}
public static LogTime getWithCheck() {
LogTime d = day;
if (d.isNextDay()) {
return nextDay();
} else {
return d;
}
}
private final String ymd;
private final long startMidnight;
private final long startTomorrow;
/**
* Because every variable is private final the constructor should be thread
* safe (In JDK5+).
*/
private LogTime() {
GregorianCalendar now = new GregorianCalendar();
now.set(Calendar.HOUR_OF_DAY, 0);
now.set(Calendar.MINUTE, 0);
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
this.startMidnight = now.getTime().getTime();
this.ymd = getDayDerived(now);
now.add(Calendar.DATE, 1);
this.startTomorrow = now.getTime().getTime();
}
/**
* Return true if we have moved into tomorrow. This is used to trigger log
* switching if required.
*/
public boolean isNextDay() {
return (System.currentTimeMillis() >= startTomorrow);
}
/**
* Return the Year Month Day for today.
*/
public String getYMD() {
return ymd;
}
/**
* Return the current time specify the separators.
* <p>
* The separators is a String[2] with the first string separating hours
* minutes and seconds and the second separating seconds from millis.
* </p>
* <p>
* The default is {":","."}
* </p>
*/
public String getNow(String[] separators) {
return getTimestamp(System.currentTimeMillis(), separators);
}
public String getTimestamp(long systime) {
StringBuilder sb = new StringBuilder();
getTime(sb, systime, startMidnight, sep);
return sb.toString();
}
public String getTimestamp(long systime, String[] separators) {
StringBuilder sb = new StringBuilder();
getTime(sb, systime, startMidnight, separators);
return sb.toString();
}
/**
* Returns the current time.
* <p>
* Format used is hours:minutes:seconds.millis
* </p>
*/
public String getNow() {
return getNow(sep);
}
/**
* Set the derived day information.
*/
private String getDayDerived(Calendar now) {
int nowyear = now.get(Calendar.YEAR);
int nowmonth = now.get(Calendar.MONTH);
int nowday = now.get(Calendar.DAY_OF_MONTH);
nowmonth++;
StringBuilder sb = new StringBuilder();
format(sb, nowyear, 4);
format(sb, nowmonth, 2);
format(sb, nowday, 2);
return sb.toString();
}
private void getTime(StringBuilder sb, long time, long midnight, String[] separator) {
long rem = time - midnight;// startMidnight;
long millis = rem % 1000;
rem = rem / 1000;
long secs = rem % 60;
rem = rem / 60;
long mins = rem % 60;
rem = rem / 60;
long hrs = rem;
format(sb, hrs, 2);
sb.append(separator[0]);
format(sb, mins, 2);
sb.append(separator[0]);
format(sb, secs, 2);
sb.append(separator[1]);
format(sb, millis, 3);
}
private void format(StringBuilder sb, long value, int places) {
String format = Long.toString(value);
int pad = places - format.length();
for (int i = 0; i < pad; i++) {
sb.append("0");
}
sb.append(format);
}
}
@@ -1,304 +0,0 @@
package com.avaje.ebeaninternal.server.transaction.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import javax.persistence.PersistenceException;
/**
* File based logger that can switch daily. It will include the date in the
* files name.
* <p>
* Administration Note: If log file switching fails it will send the error to
* standard out and standard err print streams. This is assumed to be rather
* unlikely but possible.
* </p>
*/
public class SimpleLogger {
private static final Logger logger = LoggerFactory.getLogger(SimpleLogger.class);
/**
* Used to print stack trace.
*/
private static final String atString = " at ";
/**
* The output stream.
*/
private PrintStream out;
/**
* Whether to append or replace.
*/
private boolean doAppend = true;
/**
* Flag to indicate the logger if the logger has been closed.
*/
private boolean open = true;
/**
* The current file path.
*/
private String currentPath;
/**
* The path of the log file.
*/
private final String filepath;
/**
* Set to true if using daily file switching.
*/
private final boolean useFileSwitching;
/**
* The maximum number of stack lines to output.
* This is just for transaction logging so 5 is fine.
*/
private final int maxStackTraceLines = 5;
/**
* The delimiter to use.
*/
private final String deliminator;
/**
* Object used to synch the file switching.
*/
private final Object fileMonitor = new Object();
/**
* The prefix of the log file name.
*/
private final String logFileName;
/**
* The file suffix for the logs.
*/
private final String logFileSuffix;
/**
* The newLineChar used instead of NL or CRNL.
*/
private final String newLineChar = "\\r\\n";
private final boolean csv;
/**
* Create a logger with a logFileName and useFileSwitching flag.
*
* @param dir
* the sub directory to put the log. Can be null.
* @param logFileName
* the prefix log file name.
* @param useFileSwitching
* if true then use daily file switching.
*/
public SimpleLogger(String dir, String logFileName, boolean useFileSwitching, String suffix) {
this.logFileName = logFileName;
this.useFileSwitching = useFileSwitching;
this.logFileSuffix = "."+suffix;
csv = "csv".equalsIgnoreCase(suffix);
deliminator = csv ? "," : ", ";
try {
// get the directory where the log files are going to go
filepath = makeDirIfRequired(dir);
switchFile(LogTime.nextDay());
} catch (Exception e) {
// Not going to use logger to show logger exceptions...
// Using standard out and standard err instead.
System.out.println("FATAL ERROR: init of FileLogger: " + e.getMessage());
System.err.println("FATAL ERROR: init of FileLogger: " + e.getMessage());
throw new RuntimeException(e);
}
}
public SimpleLogger(String dir, String logFileName, boolean useFileSwitching) {
this(dir, logFileName, useFileSwitching, "log");
}
protected void finalize() throws Throwable {
close();
super.finalize();
}
/**
* Close the file output stream being used for logging.
*/
public void close() {
if (open) {
out.flush();
out.close();
open = false;
}
}
public void log(String msg) {
log(null, msg, null);
}
public void log(String msg, Throwable e) {
log(null, msg, e);
}
/**
* Log the event to the file.
*/
public void log(String transId, String msg, Throwable e) {
// check to see if we need to switch file?
LogTime logTime = LogTime.get();
if (logTime.isNextDay()) {
logTime = LogTime.nextDay();
try {
switchFile(logTime);
} catch (Exception ex) {
// This is a pretty serious error... not logging it though as
// this could be recursive so just going to send to std err
ex.printStackTrace();
}
}
// prefix of transID and timestamp ~ 40 chars
int roughSize = 40;
if (msg != null) {
roughSize += msg.length();
}
if (e != null) {
roughSize += 200;
}
StringBuilder line = new StringBuilder(roughSize);
if (transId != null){
line.append("trans[").append(transId).append("]").append(deliminator);
}
if (csv){
line.append("\"'");
}
line.append(logTime.getNow());
if (csv){
line.append("'\"");
}
line.append(deliminator);
if (msg != null) {
line.append(msg).append(" ");
}
printThrowable(line, e, false);
String lineString = line.toString();
synchronized (fileMonitor) {
out.println(lineString);
// without flush automatic close() *MUST* be called
out.flush();
}
}
/**
* Recursively output the Throwable stack trace to the log.
*
* @param sb
* the buffer to write the stack trace to
* @param e
* the source throwable
* @param isCause
* flag to indicate if this is the top level throwable or a cause
*/
protected void printThrowable(StringBuilder sb, Throwable e, boolean isCause) {
if (e != null) {
if (isCause) {
sb.append("Caused by: ");
}
sb.append(e.getClass().getName());
sb.append(":");
sb.append(e.getMessage()).append(newLineChar);
StackTraceElement[] ste = e.getStackTrace();
int outputStackLines = ste.length;
int notShownCount = 0;
if (ste.length > maxStackTraceLines) {
outputStackLines = maxStackTraceLines;
notShownCount = ste.length - outputStackLines;
}
for (int i = 0; i < outputStackLines; i++) {
sb.append(atString);
sb.append(ste[i].toString()).append(newLineChar);
}
if (notShownCount > 0) {
sb.append(" ... ");
sb.append(notShownCount);
sb.append(" more").append(newLineChar);
}
Throwable cause = e.getCause();
if (cause != null) {
printThrowable(sb, cause, true);
}
}
}
/**
* Creates a new file and sets the file logging output to be directed to the
* new file.
*
* @exception Exception
* indicates a problem writing to the new log file.
*/
protected void switchFile(LogTime logTime) throws Exception {
String newFilePath = filepath + File.separator + logFileName;
if (useFileSwitching) {
// For file switching include the date in the file name
newFilePath = newFilePath + logTime.getYMD() + logFileSuffix;
} else {
newFilePath = newFilePath + logFileSuffix;
}
// Try to open an output stream to the file
synchronized (fileMonitor) {
if (!newFilePath.equals(currentPath)) {
currentPath = newFilePath;
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(newFilePath,
doAppend)));
}
}
}
/**
* Returns the directory path of the log file.
*/
protected String makeDirIfRequired(String dir) {
File f = new File(dir);
if (f.exists()){
if (!f.isDirectory()){
String msg = "Transaction logs directory is a file? "+dir;
throw new PersistenceException(msg);
}
} else {
if (!f.mkdirs()) {
String msg = "Failed to create transaction logs directory "+dir;
logger.error(msg);
}
}
return dir;
}
}
@@ -1,10 +0,0 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
<TITLE>Transaction Logging</TITLE>
</HEAD>
<Body BGCOLOR="#ffffff">
Transaction logging
</Body>
</HTML>
@@ -19,7 +19,6 @@ import com.avaje.ebean.config.ServerConfig;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.ResetBasicData;
import com.avaje.tests.model.basic.TOne;
import com.mysql.jdbc.PreparedStatement;
public class MainFutureList {
@@ -21,7 +21,7 @@ public class TestLogTransLogOnError extends TestCase {
Transaction t = Ebean.beginTransaction();
try {
t.log("--- hello");
//t.log("--- hello");
Ebean.find(Customer.class).findList();
Ebean.find(Order.class).where().gt("id", 1).findList();
@@ -31,7 +31,7 @@ public class TestLogTransLogOnError extends TestCase {
//Ebean.save(newBean);
t.log("--- next query should error");
//t.log("--- next query should error");
List<Customer> list = Ebean.find(Customer.class)
.where().eq("id", "NotAnInt!!")
.findList();
@@ -55,7 +55,7 @@ public class TestLogTransLogOnError extends TestCase {
Transaction t = Ebean.beginTransaction();
try {
t.log("--- hello testPersistError");
//t.log("--- hello testPersistError");
Ebean.find(Customer.class).findList();
EBasicVer newBean = new EBasicVer();
@@ -65,7 +65,7 @@ public class TestLogTransLogOnError extends TestCase {
+"dfjksdjflsjdflsjdflksjdfkjd fsjdfkjsdkfjsdkfjskdjfskjdf sjdf sdjflksjdfkjsdlfkjsdkfjs ");
newBean.setName("aName");
t.log("--- next insert should error");
//t.log("--- next insert should error");
Ebean.save(newBean);
// never get here
@@ -38,11 +38,11 @@ public class TestDuplcateKeyException extends TestCase {
try {
Ebean.execute(new TxRunnable() {
public void run() {
Ebean.currentTransaction().log("-- saving holder first time");
//Ebean.currentTransaction().log("-- saving holder first time");
// Alternatively turn off cascade Persist for this transaction
//Ebean.currentTransaction().setPersistCascade(false);
Ebean.save(holder);
Ebean.currentTransaction().log("-- saving holder second time");
//Ebean.currentTransaction().log("-- saving holder second time");
// we don't get this far before failing
//Ebean.save(holder);
}
@@ -66,7 +66,7 @@ public class TestSubQuery extends TestCase {
String sql = pq.getGeneratedSql();
String golden = "(t0.one_key) in (select t0.one_key from ckey_parent t0) ";
String golden = "(t0.one_key) in (select t0.one_key from ckey_parent t0) ";
if (sql.indexOf(golden) < 0)
{
@@ -131,7 +131,7 @@ public class TestSubQuery extends TestCase {
// TODO: If, after bugfixing, the system still join against vehicle I do not know now, in our case, it is not necessary if not
// using it in the where clause
String golden = "(t0.id) in (select t0.vehicle_id from vehicle_driver t0 left outer join vehicle t1 on t1.id = t0.vehicle_id )";
String golden = "(t0.id) in (select t0.vehicle_id from vehicle_driver t0 left outer join vehicle t1 on t1.id = t0.vehicle_id )";
if (sql.indexOf(golden) < 0)
{
System.out.println("failed sql:"+sql);
@@ -164,7 +164,7 @@ public class TestSubQuery extends TestCase {
String sql = pq.getGeneratedSql();
System.err.println(sql);
String golden = "(t0.id) in (select t0.vehicle_id from vehicle_driver t0 left outer join vehicle t1 on t1.id = t0.vehicle_id where t1.license_number = ? )";
String golden = "(t0.id) in (select t0.vehicle_id from vehicle_driver t0 left outer join vehicle t1 on t1.id = t0.vehicle_id where t1.license_number = ? )";
if (sql.indexOf(golden) < 0)
{
System.out.println("failed sql:"+sql);
@@ -195,7 +195,7 @@ public class TestSubQuery extends TestCase {
String sql = pq.getGeneratedSql();
System.err.println(sql);
String golden = "(t0.id) in (select t0.vehicle_id from vehicle_driver t0 left outer join vehicle t1 on t1.id = t0.vehicle_id )";
String golden = "(t0.id) in (select t0.vehicle_id from vehicle_driver t0 left outer join vehicle t1 on t1.id = t0.vehicle_id )";
// OR without join
// String golden = "(t0.id) in (select t0.vehicle_id from vehicle_driver t0)";
if (sql.indexOf(golden) < 0)
+7 -4
View File
@@ -8,7 +8,6 @@
</encoder>
</appender>
<!--
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/ebean.log</File>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
@@ -22,12 +21,16 @@
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
-->
<root level="WARN">
<!--<appender-ref ref="APPLICATION"/>-->
<appender-ref ref="APPLICATION"/>
<appender-ref ref="STDOUT"/>
</root>
<logger name="nz.ac.auckland" level="DEBUG"/>
<logger name="com.avaje.ebean" level="INFO"/>
<logger name="org.avaje.ebean" level="INFO"/>
<logger name="org.avaje.ebean.SQL" level="TRACE"/>
<logger name="org.avaje.ebean.TXN" level="TRACE"/>
<logger name="org.avaje.ebean.SUM" level="TRACE"/>
</configuration>