diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java index 6b8b8bed4..67304e390 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java @@ -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). diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java index 8f64eabb6..0443d99e4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java @@ -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 { *

*/ public void shutdown() { - if (useFileLogging) { + //if (useFileLogging) { collectUsageViaGC(-1); serialize(); - } + //} } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java index fd36c5028..1fbe15737 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java @@ -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() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java index f20405e00..f0bc221fb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java @@ -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; diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java index 21987174e..724e1438b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -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) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index ce8b36d39..bc1b9d356 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -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 execute(TxCallable c) { return execute(null, c); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServerFactory.java index d33f4e910..3c5f6598c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServerFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServerFactory.java @@ -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. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index c9fb29e36..67604b76a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -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; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java index f3a06e745..72cb538f2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java @@ -403,7 +403,7 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe */ public void logSql(String sql) { if (transaction.isLogSql()) { - transaction.logInternal(sql); + transaction.logSql(sql); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index 3d58118e3..15d279fe9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -576,13 +576,13 @@ public class PersistRequestBean 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; diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java index 2c93ef31e..7c0c200b5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java @@ -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 diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java index d8cc19353..d3364b4b9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java @@ -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()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java index 008c3a861..bc0d10947 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java @@ -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()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenerator.java b/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenerator.java index 675d6351a..fd7a9e4ce 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenerator.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenerator.java @@ -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. diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DbSqlContext.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DbSqlContext.java index d78aef344..482d2c958 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DbSqlContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DbSqlContext.java @@ -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); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java index 7fc9e1f0f..9c23588eb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java @@ -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 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 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); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLogging.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLogging.java deleted file mode 100644 index f052fbd4f..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLogging.java +++ /dev/null @@ -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. - *

- * This is accessible via {@link EbeanServer#getAdminLogging()} or via JMX MBean. - *

- */ -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; - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLoggingMBean.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLoggingMBean.java deleted file mode 100644 index 44c3e3c51..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLoggingMBean.java +++ /dev/null @@ -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); - -} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java index b1adbe1b6..bdd16c903 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java @@ -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]; diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index cb86a0f87..3a560f435 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -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); diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java index fd3285285..3b69596a9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java @@ -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); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java index 2d83b2913..600718fbd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java @@ -95,7 +95,7 @@ public class ExeCallableSql { } else { if (logSql){ - t.logInternal(sql); + t.logSql(sql); } cstmt = pstmtFactory.getCstmt(t, sql); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java index 52b78a9b6..d3fba3418 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java @@ -123,7 +123,7 @@ public class ExeOrmUpdate { } else { if (logSql){ - t.logInternal(sql); + t.logSql(sql); } pstmt = pstmtFactory.getPstmt(t, sql); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java index 0f3ea2784..d374fdf0e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java @@ -115,7 +115,7 @@ public class ExeUpdateSql { } else { if (logSql){ - t.logInternal(sql); + t.logSql(sql); } pstmt = pstmtFactory.getPstmt(t, sql); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java b/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java index 5a1cf50ac..7dd0f30e6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java @@ -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(); diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java index 5d1a38c1b..69b3f5701 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java @@ -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); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java index 693dadcbe..fd30a83f9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java @@ -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); diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java index 9de126b0b..fe6b2b226 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java @@ -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 loadedProps; + protected final StringBuilder bindLog; - protected final SpiTransaction transaction; - - protected final boolean emptyStringToNull; - - protected final boolean logLevelSql; + protected final Set loadedProps; - /** - * The PreparedStatement used for the dml. - */ - protected DataBind dataBind; - - protected String sql; - - protected ArrayList updateGenValues; - - private Set 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 updateGenValues; + + private Set 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(); - } - 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. - *

- * GeneratedProperty values are likely going to be used for optimistic - * concurrency checking. This includes 'counter' and 'update timestamp' - * generation. - *

- */ - public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value) { - if (updateGenValues == null) { - updateGenValues = new ArrayList(); - } - 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(); + } + 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. + *

+ * GeneratedProperty values are likely going to be used for optimistic + * concurrency checking. This includes 'counter' and 'update timestamp' + * generation. + *

+ */ + public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value) { + if (updateGenValues == null) { + updateGenValues = new ArrayList(); + } + 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); + } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java index 07f231c75..d83af7013 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java @@ -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); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java index 6b42f6200..78e57bf41 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java @@ -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 diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java index ecc7b4056..56782351a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java @@ -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()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index c82acd7ad..de9e1b1d7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -56,805 +56,786 @@ import org.slf4j.LoggerFactory; */ public class CQuery implements DbReadContext, CancelableQuery { - private static final Logger logger = LoggerFactory.getLogger(CQuery.class); + private static final Logger logger = LoggerFactory.getLogger(CQuery.class); - private static final int GLOBAL_ROW_LIMIT = 1000000; + private static final int GLOBAL_ROW_LIMIT = 1000000; - /** - * The resultSet rows read. - */ - private int rowCount; + /** + * The resultSet rows read. + */ + private int rowCount; - /** - * The number of master EntityBeans loaded. - */ - private int loadedBeanCount; + /** + * The number of master EntityBeans loaded. + */ + private int loadedBeanCount; - /** - * Flag set when no more rows are in the resultSet. - */ - private boolean noMoreRows; - /** - * Id of loaded 'master' bean. - */ - private Object loadedBeanId; - /** - * Flag set when 'master' bean changed. - */ - boolean loadedBeanChanged; - /** - * The 'master' bean just loaded. - */ - private Object loadedBean; + /** + * Flag set when no more rows are in the resultSet. + */ + private boolean noMoreRows; + /** + * Id of loaded 'master' bean. + */ + private Object loadedBeanId; + /** + * Flag set when 'master' bean changed. + */ + boolean loadedBeanChanged; + /** + * The 'master' bean just loaded. + */ + private Object loadedBean; - /** - * Holds the previous loaded bean. - */ - private Object prevLoadedBean; + /** + * Holds the previous loaded bean. + */ + private Object prevLoadedBean; - /** - * The detail bean just loaded. - */ - private Object loadedManyBean; + /** + * The detail bean just loaded. + */ + private Object loadedManyBean; - /** - * The previous 'detail' collection remembered so that for manyToMany we can - * turn on the modify listening. - */ - private Object prevDetailCollection; + /** + * The previous 'detail' collection remembered so that for manyToMany we can + * turn on the modify listening. + */ + private Object prevDetailCollection; - /** - * The current 'detail' collection being populated. - */ - private Object currentDetailCollection; + /** + * The current 'detail' collection being populated. + */ + private Object currentDetailCollection; - /** - * The 'master' collection being populated. - */ - private final BeanCollection collection; - /** - * The help for the 'master' collection. - */ - private final BeanCollectionHelp help; + /** + * The 'master' collection being populated. + */ + private final BeanCollection collection; + /** + * The help for the 'master' collection. + */ + private final BeanCollectionHelp help; - /** - * The overall find request wrapper object. - */ - private final OrmQueryRequest request; + /** + * The overall find request wrapper object. + */ + private final OrmQueryRequest request; - private final BeanDescriptor desc; + private final BeanDescriptor desc; - private final SpiQuery query; - - private final QueryListener queryListener; - - private Map currentPathMap; + private final SpiQuery query; - private String currentPrefix; - - /** - * Flag set true when reading 'master' and 'detail' beans. - */ - private final boolean manyIncluded; + private final QueryListener queryListener; - /** - * Where clause predicates. - */ - private final CQueryPredicates predicates; + private Map currentPathMap; - /** - * Object handling the SELECT generation and reading. - */ - private final SqlTree sqlTree; + private String currentPrefix; - private final boolean rawSql; + /** + * Flag set true when reading 'master' and 'detail' beans. + */ + private final boolean manyIncluded; - /** - * The final sql that is generated. - */ - private final String sql; + /** + * Where clause predicates. + */ + private final CQueryPredicates predicates; - /** - * Where clause to show in logs when using an existing query plan. - */ - private final String logWhereSql; + /** + * Object handling the SELECT generation and reading. + */ + private final SqlTree sqlTree; - /** - * Set to true if the row number column is included in the sql. - */ - private final boolean rowNumberIncluded; + private final boolean rawSql; - /** - * Tree that knows how to build the master and detail beans from the - * resultSet. - */ - private final SqlTreeNode rootNode; + /** + * The final sql that is generated. + */ + private final String sql; - /** - * For master detail query. - */ - private final BeanPropertyAssocMany manyProperty; + /** + * Where clause to show in logs when using an existing query plan. + */ + private final String logWhereSql; - /** - * The many property Expression language object. - */ - private final ElPropertyValue manyPropertyEl; - - private final int backgroundFetchAfter; + /** + * Set to true if the row number column is included in the sql. + */ + private final boolean rowNumberIncluded; - private final int maxRowsLimit; + /** + * Tree that knows how to build the master and detail beans from the + * resultSet. + */ + private final SqlTreeNode rootNode; - /** - * Flag set when backgroundFetchAfter limit is hit. - */ - private boolean hasHitBackgroundFetchAfter; + /** + * For master detail query. + */ + private final BeanPropertyAssocMany manyProperty; - private final PersistenceContext persistenceContext; + /** + * The many property Expression language object. + */ + private final ElPropertyValue manyPropertyEl; - private DataReader dataReader; + private final int backgroundFetchAfter; - /** - * The statement used to create the resultSet. - */ - private PreparedStatement pstmt; + private final int maxRowsLimit; - private boolean cancelled; - - private String bindLog; + /** + * Flag set when backgroundFetchAfter limit is hit. + */ + private boolean hasHitBackgroundFetchAfter; - private final CQueryPlan queryPlan; + private final PersistenceContext persistenceContext; - private long startNano; - - private final Mode queryMode; - - private final boolean autoFetchProfiling; - - private final ObjectGraphNode autoFetchParentNode; - - private final AutoFetchManager autoFetchManager; - private final WeakReference autoFetchManagerRef; - - private int executionTimeMicros; - - private final Boolean readOnly; - - private final SpiExpressionList filterMany; - - /** - * Create the Sql select based on the request. - */ - @SuppressWarnings("unchecked") - public CQuery(OrmQueryRequest request, CQueryPredicates predicates, CQueryPlan queryPlan) { - this.request = request; - this.queryPlan = queryPlan; - this.query = request.getQuery(); - this.queryMode = query.getMode(); - - this.readOnly = request.isReadOnly(); - - this.autoFetchManager = query.getAutoFetchManager(); - this.autoFetchProfiling = autoFetchManager != null; - this.autoFetchParentNode = autoFetchProfiling ? query.getParentNode() : null; - this.autoFetchManagerRef = autoFetchProfiling ? new WeakReference(autoFetchManager) : null; - - // set the generated sql back to the query - // so its available to the user... - query.setGeneratedSql(queryPlan.getSql()); + private DataReader dataReader; - this.sqlTree = queryPlan.getSqlTree(); - this.rootNode = sqlTree.getRootNode(); - - this.manyProperty = sqlTree.getManyProperty(); - this.manyPropertyEl = sqlTree.getManyPropertyEl(); - this.manyIncluded = sqlTree.isManyIncluded(); - if (manyIncluded) { - // get filter to put on the collection for reuse with refresh - String manyPropertyName = sqlTree.getManyPropertyName(); - OrmQueryProperties chunk = query.getDetail().getChunk(manyPropertyName, false); - this.filterMany = chunk.getFilterMany(); - } else { - this.filterMany = null; - } + /** + * The statement used to create the resultSet. + */ + private PreparedStatement pstmt; - this.sql = queryPlan.getSql(); - this.rawSql = queryPlan.isRawSql(); - this.rowNumberIncluded = queryPlan.isRowNumberIncluded(); - this.logWhereSql = queryPlan.getLogWhereSql(); - this.desc = request.getBeanDescriptor(); - this.predicates = predicates; + private boolean cancelled; - this.queryListener = query.getListener(); - if (queryListener == null) { - // normal, use the one from the transaction - this.persistenceContext = request.getPersistenceContext(); - } else { - // 'Row Level Transaction Context'... - // local transaction context that will be reset - // after each 'master' bean is sent to the listener - this.persistenceContext = new DefaultPersistenceContext(); - } + private String bindLog; - this.maxRowsLimit = query.getMaxRows() > 0 ? query.getMaxRows() : GLOBAL_ROW_LIMIT; - this.backgroundFetchAfter = query.getBackgroundFetchAfter() > 0 ? query.getBackgroundFetchAfter() : Integer.MAX_VALUE; + private final CQueryPlan queryPlan; - this.help = createHelp(request); - this.collection = (BeanCollection)(help != null ? help.createEmpty(false) : null); - } + private long startNano; - private BeanCollectionHelp createHelp(OrmQueryRequest request) { - if (request.isFindById()) { - return null; - } else { - SpiQuery.Type manyType = request.getQuery().getType(); - if (manyType == null){ - // subQuery compiled for InQueryExpression - return null; - } - return BeanCollectionHelpFactory.create(request); - } - } - - public Boolean isReadOnly() { - return readOnly; + private final Mode queryMode; + + private final boolean autoFetchProfiling; + + private final ObjectGraphNode autoFetchParentNode; + + private final AutoFetchManager autoFetchManager; + private final WeakReference autoFetchManagerRef; + + private int executionTimeMicros; + + private final Boolean readOnly; + + private final SpiExpressionList filterMany; + + /** + * Create the Sql select based on the request. + */ + @SuppressWarnings("unchecked") + public CQuery(OrmQueryRequest request, CQueryPredicates predicates, CQueryPlan queryPlan) { + this.request = request; + this.queryPlan = queryPlan; + this.query = request.getQuery(); + this.queryMode = query.getMode(); + + this.readOnly = request.isReadOnly(); + + this.autoFetchManager = query.getAutoFetchManager(); + this.autoFetchProfiling = autoFetchManager != null; + this.autoFetchParentNode = autoFetchProfiling ? query.getParentNode() : null; + this.autoFetchManagerRef = autoFetchProfiling ? new WeakReference( + autoFetchManager) : null; + + // set the generated sql back to the query + // so its available to the user... + query.setGeneratedSql(queryPlan.getSql()); + + this.sqlTree = queryPlan.getSqlTree(); + this.rootNode = sqlTree.getRootNode(); + + this.manyProperty = sqlTree.getManyProperty(); + this.manyPropertyEl = sqlTree.getManyPropertyEl(); + this.manyIncluded = sqlTree.isManyIncluded(); + if (manyIncluded) { + // get filter to put on the collection for reuse with refresh + String manyPropertyName = sqlTree.getManyPropertyName(); + OrmQueryProperties chunk = query.getDetail().getChunk(manyPropertyName, false); + this.filterMany = chunk.getFilterMany(); + } else { + this.filterMany = null; } - public void propagateState(Object e) { - if (Boolean.TRUE.equals(readOnly)){ - if (e instanceof EntityBean){ - ((EntityBean)e)._ebean_getIntercept().setReadOnly(true); - } - } - } - - public DataReader getDataReader() { - return dataReader; + this.sql = queryPlan.getSql(); + this.rawSql = queryPlan.isRawSql(); + this.rowNumberIncluded = queryPlan.isRowNumberIncluded(); + this.logWhereSql = queryPlan.getLogWhereSql(); + this.desc = request.getBeanDescriptor(); + this.predicates = predicates; + + this.queryListener = query.getListener(); + if (queryListener == null) { + // normal, use the one from the transaction + this.persistenceContext = request.getPersistenceContext(); + } else { + // 'Row Level Transaction Context'... + // local transaction context that will be reset + // after each 'master' bean is sent to the listener + this.persistenceContext = new DefaultPersistenceContext(); } - public Mode getQueryMode() { - return queryMode; - } + this.maxRowsLimit = query.getMaxRows() > 0 ? query.getMaxRows() : GLOBAL_ROW_LIMIT; + this.backgroundFetchAfter = query.getBackgroundFetchAfter() > 0 ? query + .getBackgroundFetchAfter() : Integer.MAX_VALUE; - /** - * Return true if we want to return vanilla (not enhanced) objects. - */ - public boolean isVanillaMode() { - return request.isVanillaMode(); + this.help = createHelp(request); + this.collection = (BeanCollection) (help != null ? help.createEmpty(false) : null); + } + + private BeanCollectionHelp createHelp(OrmQueryRequest request) { + if (request.isFindById()) { + return null; + } else { + SpiQuery.Type manyType = request.getQuery().getType(); + if (manyType == null) { + // subQuery compiled for InQueryExpression + return null; + } + return BeanCollectionHelpFactory.create(request); } + } - public CQueryPredicates getPredicates() { - return predicates; - } - - public LoadContext getGraphContext() { - return request.getGraphContext(); - } + public Boolean isReadOnly() { + return readOnly; + } - public SpiOrmQueryRequest getQueryRequest() { - return request; - } - - public void cancel() { - synchronized (this) { - this.cancelled = true; - if (pstmt != null){ - try { - pstmt.cancel(); - } catch (SQLException e){ - String msg = "Error cancelling query"; - throw new PersistenceException(msg, e); - } - } - } - } - - public boolean prepareBindExecuteQuery() throws SQLException { - - synchronized (this) { - if (cancelled || query.isCancelled()){ - // cancelled before we started - cancelled = true; - return false; - } - - startNano = System.nanoTime(); - - // prepare - SpiTransaction t = request.getTransaction(); - Connection conn = t.getInternalConnection(); - pstmt = conn.prepareStatement(sql); - - if (query.getTimeout() > 0){ - pstmt.setQueryTimeout(query.getTimeout()); - } - if (query.getBufferFetchSizeHint() > 0){ - pstmt.setFetchSize(query.getBufferFetchSizeHint()); - } - - DataBind dataBind = new DataBind(pstmt); - - // bind keys for encrypted properties - queryPlan.bindEncryptedProperties(dataBind); - - bindLog = predicates.bind(dataBind); - - // executeQuery - ResultSet rset = pstmt.executeQuery(); - dataReader = queryPlan.createDataReader(rset); - - return true; - } - } - - /** - * Close the resources. - *

- * The jdbc resultSet and statement need to be closed. Its important that - * this method is called. - *

- */ - public void close() { - try { - if (dataReader != null) { - dataReader.close(); - dataReader = null; - } - } catch (SQLException e) { - logger.error(null, e); - } - try { - if (pstmt != null) { - pstmt.close(); - pstmt = null; - } - } catch (SQLException e) { - logger.error(null, e); - } - } - -// /** -// * Return the reference options used to define cache use. -// */ -// public ReferenceOptions getReferenceOptionsFor(BeanPropertyAssocOne beanProp) { -// -// String beanPropName = beanProp.getName(); -// if (currentPrefix != null){ -// beanPropName = currentPrefix+"."+beanPropName; -// } -// //ReferenceOptions opt = referenceOptionsMap.get(beanPropName); -// if (opt == null){ -// OrmQueryProperties chunk = queryDetail.getChunk(beanPropName, false); -// if (chunk != null) { -// // get the options from the query -// opt = chunk.getReferenceOptions(); -// } -// if (opt == null){ -// // get the default options defined for the target bean type -// opt = beanProp.getTargetDescriptor().getReferenceOptions(); -// } -// referenceOptionsMap.put(beanPropName, opt); -// } -// -// return opt; -// } - - /** - * Return the persistence context. - */ - public PersistenceContext getPersistenceContext(){ - return persistenceContext; - } - - public void setLoadedBean(Object bean, Object id) { - if (id != null && id.equals(loadedBeanId)) { - // master/detail loading with master bean - // unchanged. NB Using id to avoid any issue - // with equals not being implemented - - } else { - if (manyIncluded) { - if (rowCount > 1) { - loadedBeanChanged = true; - } - this.prevLoadedBean = loadedBean; - this.loadedBeanId = id; - } - this.loadedBean = bean; - } - } - - public void setLoadedManyBean(Object manyValue) { - this.loadedManyBean = manyValue; - } - - /** - * Return the last read bean. - */ - @SuppressWarnings("unchecked") - public T getLoadedBean() { - if (manyIncluded) { - if (prevDetailCollection instanceof BeanCollection) { - ((BeanCollection)prevDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); - - } else if (currentDetailCollection instanceof BeanCollection) { - ((BeanCollection)currentDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); - } - } - - if (prevLoadedBean != null) { - return (T)prevLoadedBean; - } else { - return (T)loadedBean; - } - } - - private boolean hasMoreRows() throws SQLException { - synchronized (this) { - if (cancelled){ - return false; - } - return dataReader.next(); - } + public void propagateState(Object e) { + if (Boolean.TRUE.equals(readOnly)) { + if (e instanceof EntityBean) { + ((EntityBean) e)._ebean_getIntercept().setReadOnly(true); + } } - - /** - * Read a row from the result set returning a bean. - *

- * If the query includes a many then the first object in the returned array - * is the one/master and the second the many/detail. - *

- */ - private boolean readRow() throws SQLException { + } - synchronized (this) { - if (cancelled){ - return false; - } - - if (!dataReader.next()){ - return false; - } + public DataReader getDataReader() { + return dataReader; + } - rowCount++; - dataReader.resetColumnPosition(); - - if (rowNumberIncluded) { - // row_number() column used for limit features - dataReader.incrementPos(1); - } - - rootNode.load(this, null); - - return true; - } - } - - public int getQueryExecutionTimeMicros(){ - return executionTimeMicros; - } - - public boolean readBean() throws SQLException { - - boolean result = readBeanInternal(true); + public Mode getQueryMode() { + return queryMode; + } - updateExecutionStatistics(); - - return result; - } - - private boolean readBeanInternal(boolean inForeground) throws SQLException { - - if (loadedBeanCount >= maxRowsLimit) { - collection.setHasMoreRows(hasMoreRows()); - return false; - } - - if (inForeground && loadedBeanCount >= backgroundFetchAfter) { - hasHitBackgroundFetchAfter = true; - collection.setFinishedFetch(false); - return false; - } - - if (!manyIncluded) { - // simple query... no details... - return readRow(); - } + /** + * Return true if we want to return vanilla (not enhanced) objects. + */ + public boolean isVanillaMode() { + return request.isVanillaMode(); + } - if (noMoreRows) { - return false; - } + public CQueryPredicates getPredicates() { + return predicates; + } - if (rowCount == 0) { - if (!readRow()) { - // no rows at all... - return false; - } else { - createNewDetailCollection(); - } - } + public LoadContext getGraphContext() { + return request.getGraphContext(); + } - if (readIntoCurrentDetailCollection()) { - createNewDetailCollection(); - // return prevLoadedBean - return true; + public SpiOrmQueryRequest getQueryRequest() { + return request; + } - } else { - // return loadedBean - prevDetailCollection = null; - prevLoadedBean = null; - noMoreRows = true; - return true; - } - } - - private boolean readIntoCurrentDetailCollection() throws SQLException { - while (readRow()) { - if (loadedBeanChanged) { - loadedBeanChanged = false; - return true; - } else { - addToCurrentDetailCollection(); - } - } - return false; - } - - private BeanCollectionAdd currentDetailAdd; - - private void createNewDetailCollection() { - prevDetailCollection = currentDetailCollection; - if (queryMode.equals(Mode.LAZYLOAD_MANY)){ - // just populate the current collection - currentDetailCollection = manyPropertyEl.elGetValue(loadedBean); - } else { - // create a new collection to populate and assign to the bean - currentDetailCollection = manyProperty.createEmpty(request.isVanillaMode()); - manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false, false); - } - - if (filterMany != null && !request.isVanillaMode()){ - // remember the for use with a refresh - ((BeanCollection)currentDetailCollection).setFilterMany(filterMany); - } - - // the manyKey is always null for this case, just using default mapKey on the property - currentDetailAdd = manyProperty.getBeanCollectionAdd(currentDetailCollection, null); - addToCurrentDetailCollection(); - } - - private void addToCurrentDetailCollection() { - if (loadedManyBean != null) { - currentDetailAdd.addBean(loadedManyBean); - } - } - - public BeanCollection continueFetchingInBackground() throws SQLException { - readTheRows(false); - collection.setFinishedFetch(true); - return collection; - } - - public BeanCollection readCollection() throws SQLException { - - readTheRows(true); - - updateExecutionStatistics(); - - return collection; - } - - protected void updateExecutionStatistics() { + public void cancel() { + synchronized (this) { + this.cancelled = true; + if (pstmt != null) { try { - long exeNano = System.nanoTime() - startNano; - executionTimeMicros = (int)exeNano/1000; - - if (autoFetchProfiling){ - autoFetchManager.collectQueryInfo(autoFetchParentNode, loadedBeanCount, executionTimeMicros); - } - queryPlan.executionTime(loadedBeanCount, executionTimeMicros); - - } catch (Exception e){ - logger.error(null, e); + pstmt.cancel(); + } catch (SQLException e) { + String msg = "Error cancelling query"; + throw new PersistenceException(msg, e); } + } } - - public QueryIterator readIterate(int bufferSize, OrmQueryRequest request) { - - if (bufferSize > 0){ - return new CQueryIteratorWithBuffer(this, request, bufferSize); - - } else { - return new CQueryIteratorSimple(this, request); - } - } + } - private void readTheRows(boolean inForeground) throws SQLException { - while (hasNextBean(inForeground)) { - if (queryListener != null) { - queryListener.process(getLoadedBean()); + public boolean prepareBindExecuteQuery() throws SQLException { - } else { - // add to the list/set/map - help.add(collection, getLoadedBean()); - } + synchronized (this) { + if (cancelled || query.isCancelled()) { + // cancelled before we started + cancelled = true; + return false; + } + + startNano = System.nanoTime(); + + // prepare + SpiTransaction t = request.getTransaction(); + Connection conn = t.getInternalConnection(); + pstmt = conn.prepareStatement(sql); + + if (query.getTimeout() > 0) { + pstmt.setQueryTimeout(query.getTimeout()); + } + if (query.getBufferFetchSizeHint() > 0) { + pstmt.setFetchSize(query.getBufferFetchSizeHint()); + } + + DataBind dataBind = new DataBind(pstmt); + + // bind keys for encrypted properties + queryPlan.bindEncryptedProperties(dataBind); + + bindLog = predicates.bind(dataBind); + + // executeQuery + ResultSet rset = pstmt.executeQuery(); + dataReader = queryPlan.createDataReader(rset); + + return true; + } + } + + /** + * Close the resources. + *

+ * The jdbc resultSet and statement need to be closed. Its important that this + * method is called. + *

+ */ + public void close() { + try { + if (dataReader != null) { + dataReader.close(); + dataReader = null; + } + } catch (SQLException e) { + logger.error(null, e); + } + try { + if (pstmt != null) { + pstmt.close(); + pstmt = null; + } + } catch (SQLException e) { + logger.error(null, e); + } + } + + /** + * Return the persistence context. + */ + public PersistenceContext getPersistenceContext() { + return persistenceContext; + } + + public void setLoadedBean(Object bean, Object id) { + if (id != null && id.equals(loadedBeanId)) { + // master/detail loading with master bean + // unchanged. NB Using id to avoid any issue + // with equals not being implemented + + } else { + if (manyIncluded) { + if (rowCount > 1) { + loadedBeanChanged = true; } - } - - - protected boolean hasNextBean(boolean inForeground) throws SQLException { - - if (!readBeanInternal(inForeground)) { - return false; - - } else { - loadedBeanCount++; - return true; - } - } - - public String getLoadedRowDetail() { - if (!manyIncluded) { - return String.valueOf(rowCount); - } else { - return loadedBeanCount + ":" + rowCount; - } - } - - public void register(String path, EntityBeanIntercept ebi){ - - path = getPath(path); - request.getGraphContext().register(path, ebi); - } - - public void register(String path, BeanCollection bc){ - - path = getPath(path); - request.getGraphContext().register(path, bc); - } - - - public boolean useBackgroundToContinueFetch() { - return hasHitBackgroundFetchAfter; - } - - /** - * Return the query name. - */ - public String getName() { - return query.getName(); - } - - /** - * Return true if this is a raw sql query as opposed to Ebean generated sql. - */ - public boolean isRawSql() { - return rawSql; - } - - /** - * Return the where predicate for display in the transaction log. - */ - public String getLogWhereSql() { - return logWhereSql; - } - - /** - * 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; - } - - /** - * Get the summary of the sql. - */ - public String getSummary() { - return sqlTree.getSummary(); - } - - /** - * Return the SqlSelectChain. This is the flattened structure that - * represents this query. - */ - public SqlTree getSqlTree() { - return sqlTree; - } - - public String getBindLog() { - return bindLog; - } - - public SpiTransaction getTransaction() { - return request.getTransaction(); - } - - public String getBeanType() { - return desc.getFullName(); - } - - /** - * Return the short bean name. - */ - public String getBeanName() { - return desc.getName(); + this.prevLoadedBean = loadedBean; + this.loadedBeanId = id; + } + this.loadedBean = bean; } - - /** - * Return the generated sql. - */ - public String getGeneratedSql() { - return sql; - } - - /** - * Create a PersistenceException including interesting information like the bindLog and sql used. - */ - public PersistenceException createPersistenceException(SQLException e) { - - return createPersistenceException(e, getTransaction(), bindLog, sql); + } + + public void setLoadedManyBean(Object manyValue) { + this.loadedManyBean = manyValue; + } + + /** + * Return the last read bean. + */ + @SuppressWarnings("unchecked") + public T getLoadedBean() { + if (manyIncluded) { + if (prevDetailCollection instanceof BeanCollection) { + ((BeanCollection) prevDetailCollection).setModifyListening(manyProperty + .getModifyListenMode()); + + } else if (currentDetailCollection instanceof BeanCollection) { + ((BeanCollection) currentDetailCollection).setModifyListening(manyProperty + .getModifyListenMode()); + } } - - /** - * Create a PersistenceException including interesting information like the bindLog and sql used. - */ - public static PersistenceException createPersistenceException(SQLException e, SpiTransaction t, String bindLog, String sql) { - if (t.isLogSummary()) { - // log the error to the transaction log - String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[] { "\r", "\n" }, "\\n "); - String msg = "ERROR executing query: bindLog[" + bindLog + "] error[" + errMsg + "]"; - t.logInternal(msg); - } - - // ensure 'rollback' is logged if queryOnly transaction - t.getConnection(); - - // build a decent error message for the exception - String m = Message.msg("fetch.sqlerror", e.getMessage(), bindLog, sql); - return new PersistenceException(m, e); + if (prevLoadedBean != null) { + return (T) prevLoadedBean; + } else { + return (T) loadedBean; } - - /** - * Should we create profileNodes for beans created in this query. - *

- * This is true for all queries except lazy load bean queries. - *

- */ - public boolean isAutoFetchProfiling() { - // need query.isProfiling() because we just take the data - // from the lazy loaded or refreshed beans and put it into the already - // existing beans which are already collecting usage information - return autoFetchProfiling && query.isUsageProfiling(); - } + } - private String getPath(String propertyName) { - - if (currentPrefix == null){ - return propertyName; - } else if (propertyName == null) { - return currentPrefix; - } - - String path = currentPathMap.get(propertyName); - if (path != null){ - return path; - } else { - return currentPrefix+"."+propertyName; - } - } - - - public void profileBean(EntityBeanIntercept ebi, String prefix) { - - ObjectGraphNode node = request.getGraphContext().getObjectGraphNode(prefix); - - ebi.setNodeUsageCollector(new NodeUsageCollector(node, autoFetchManagerRef)); - } + private boolean hasMoreRows() throws SQLException { + synchronized (this) { + if (cancelled) { + return false; + } + return dataReader.next(); + } + } + + /** + * Read a row from the result set returning a bean. + *

+ * If the query includes a many then the first object in the returned array is + * the one/master and the second the many/detail. + *

+ */ + private boolean readRow() throws SQLException { + + synchronized (this) { + if (cancelled) { + return false; + } + + if (!dataReader.next()) { + return false; + } + + rowCount++; + dataReader.resetColumnPosition(); + + if (rowNumberIncluded) { + // row_number() column used for limit features + dataReader.incrementPos(1); + } + + rootNode.load(this, null); + + return true; + } + } + + public int getQueryExecutionTimeMicros() { + return executionTimeMicros; + } + + public boolean readBean() throws SQLException { + + boolean result = readBeanInternal(true); + + updateExecutionStatistics(); + + return result; + } + + private boolean readBeanInternal(boolean inForeground) throws SQLException { + + if (loadedBeanCount >= maxRowsLimit) { + collection.setHasMoreRows(hasMoreRows()); + return false; + } + + if (inForeground && loadedBeanCount >= backgroundFetchAfter) { + hasHitBackgroundFetchAfter = true; + collection.setFinishedFetch(false); + return false; + } + + if (!manyIncluded) { + // simple query... no details... + return readRow(); + } + + if (noMoreRows) { + return false; + } + + if (rowCount == 0) { + if (!readRow()) { + // no rows at all... + return false; + } else { + createNewDetailCollection(); + } + } + + if (readIntoCurrentDetailCollection()) { + createNewDetailCollection(); + // return prevLoadedBean + return true; + + } else { + // return loadedBean + prevDetailCollection = null; + prevLoadedBean = null; + noMoreRows = true; + return true; + } + } + + private boolean readIntoCurrentDetailCollection() throws SQLException { + while (readRow()) { + if (loadedBeanChanged) { + loadedBeanChanged = false; + return true; + } else { + addToCurrentDetailCollection(); + } + } + return false; + } + + private BeanCollectionAdd currentDetailAdd; + + private void createNewDetailCollection() { + prevDetailCollection = currentDetailCollection; + if (queryMode.equals(Mode.LAZYLOAD_MANY)) { + // just populate the current collection + currentDetailCollection = manyPropertyEl.elGetValue(loadedBean); + } else { + // create a new collection to populate and assign to the bean + currentDetailCollection = manyProperty.createEmpty(request.isVanillaMode()); + manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false, false); + } + + if (filterMany != null && !request.isVanillaMode()) { + // remember the for use with a refresh + ((BeanCollection) currentDetailCollection).setFilterMany(filterMany); + } + + // the manyKey is always null for this case, just using default mapKey on + // the property + currentDetailAdd = manyProperty.getBeanCollectionAdd(currentDetailCollection, null); + addToCurrentDetailCollection(); + } + + private void addToCurrentDetailCollection() { + if (loadedManyBean != null) { + currentDetailAdd.addBean(loadedManyBean); + } + } + + public BeanCollection continueFetchingInBackground() throws SQLException { + readTheRows(false); + collection.setFinishedFetch(true); + return collection; + } + + public BeanCollection readCollection() throws SQLException { + + readTheRows(true); + + updateExecutionStatistics(); + + return collection; + } + + protected void updateExecutionStatistics() { + try { + long exeNano = System.nanoTime() - startNano; + executionTimeMicros = (int) exeNano / 1000; + + if (autoFetchProfiling) { + autoFetchManager + .collectQueryInfo(autoFetchParentNode, loadedBeanCount, executionTimeMicros); + } + queryPlan.executionTime(loadedBeanCount, executionTimeMicros); + + } catch (Exception e) { + logger.error(null, e); + } + } + + public QueryIterator readIterate(int bufferSize, OrmQueryRequest request) { + + if (bufferSize > 0) { + return new CQueryIteratorWithBuffer(this, request, bufferSize); + + } else { + return new CQueryIteratorSimple(this, request); + } + } + + private void readTheRows(boolean inForeground) throws SQLException { + while (hasNextBean(inForeground)) { + if (queryListener != null) { + queryListener.process(getLoadedBean()); + + } else { + // add to the list/set/map + help.add(collection, getLoadedBean()); + } + } + } + + protected boolean hasNextBean(boolean inForeground) throws SQLException { + + if (!readBeanInternal(inForeground)) { + return false; + + } else { + loadedBeanCount++; + return true; + } + } + + public String getLoadedRowDetail() { + if (!manyIncluded) { + return String.valueOf(rowCount); + } else { + return loadedBeanCount + ":" + rowCount; + } + } + + public void register(String path, EntityBeanIntercept ebi) { + + path = getPath(path); + request.getGraphContext().register(path, ebi); + } + + public void register(String path, BeanCollection bc) { + + path = getPath(path); + request.getGraphContext().register(path, bc); + } + + public boolean useBackgroundToContinueFetch() { + return hasHitBackgroundFetchAfter; + } + + /** + * Return the query name. + */ + public String getName() { + return query.getName(); + } + + /** + * Return true if this is a raw sql query as opposed to Ebean generated sql. + */ + public boolean isRawSql() { + return rawSql; + } + + /** + * Return the where predicate for display in the transaction log. + */ + public String getLogWhereSql() { + return logWhereSql; + } + + /** + * 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; + } + + /** + * Get the summary of the sql. + */ + public String getSummary() { + return sqlTree.getSummary(); + } + + /** + * Return the SqlSelectChain. This is the flattened structure that represents + * this query. + */ + public SqlTree getSqlTree() { + return sqlTree; + } + + public String getBindLog() { + return bindLog; + } + + public SpiTransaction getTransaction() { + return request.getTransaction(); + } + + public String getBeanType() { + return desc.getFullName(); + } + + /** + * Return the short bean name. + */ + public String getBeanName() { + return desc.getName(); + } + + /** + * Return the generated sql. + */ + public String getGeneratedSql() { + return sql; + } + + /** + * Create a PersistenceException including interesting information like the + * bindLog and sql used. + */ + public PersistenceException createPersistenceException(SQLException e) { + + return createPersistenceException(e, getTransaction(), bindLog, sql); + } + + /** + * Create a PersistenceException including interesting information like the + * bindLog and sql used. + */ + public static PersistenceException createPersistenceException(SQLException e, SpiTransaction t, + String bindLog, String sql) { + + if (t.isLogSummary()) { + // log the error to the transaction log + String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[] { "\r", "\n" }, + "\\n "); + String msg = "ERROR executing query: bindLog[" + bindLog + "] error[" + errMsg + "]"; + t.logSummary(msg); + } + + // ensure 'rollback' is logged if queryOnly transaction + t.getConnection(); + + // build a decent error message for the exception + String m = Message.msg("fetch.sqlerror", e.getMessage(), bindLog, sql); + return new PersistenceException(m, e); + } + + /** + * Should we create profileNodes for beans created in this query. + *

+ * This is true for all queries except lazy load bean queries. + *

+ */ + public boolean isAutoFetchProfiling() { + // need query.isProfiling() because we just take the data + // from the lazy loaded or refreshed beans and put it into the already + // existing beans which are already collecting usage information + return autoFetchProfiling && query.isUsageProfiling(); + } + + private String getPath(String propertyName) { + + if (currentPrefix == null) { + return propertyName; + } else if (propertyName == null) { + return currentPrefix; + } + + String path = currentPathMap.get(propertyName); + if (path != null) { + return path; + } else { + return currentPrefix + "." + propertyName; + } + } + + public void profileBean(EntityBeanIntercept ebi, String prefix) { + + ObjectGraphNode node = request.getGraphContext().getObjectGraphNode(prefix); + + ebi.setNodeUsageCollector(new NodeUsageCollector(node, autoFetchManagerRef)); + } + + public void setCurrentPrefix(String currentPrefix, Map currentPathMap) { + this.currentPrefix = currentPrefix; + this.currentPathMap = currentPathMap; + } - public void setCurrentPrefix(String currentPrefix, Map currentPathMap) { - this.currentPrefix = currentPrefix; - this.currentPathMap = currentPathMap; - } - } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java index 265e40f70..c182cfc56 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java @@ -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) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java index be2ebe6b6..f9b42bd4d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java @@ -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); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java index 39231a780..af3ea921e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java @@ -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 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 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 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(""); - sb.append(Constants.NEW_LINE); - sb.append(sql); - sb.append(Constants.NEW_LINE).append(""); - - 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()); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java index d609ddc51..1f11e6f68 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java @@ -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); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java b/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java index 10e51db4d..6c0837cd1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java @@ -5,19 +5,6 @@ package com.avaje.ebeaninternal.server.query; */ public interface Constants { - /** - * the new line character used. - *

- * Note that this is removed for logging sql to the transaction log. - *

- */ - 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. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultDbSqlContext.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultDbSqlContext.java index 19f38b8bc..39bc1427a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultDbSqlContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultDbSqlContext.java @@ -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 tableAliasStack = new ArrayStack(); - private final ArrayStack tableAliasStack = new ArrayStack(); + private final ArrayStack joinStack = new ArrayStack(); - private final ArrayStack joinStack = new ArrayStack(); - - private final ArrayStack prefixStack = new ArrayStack(); + private final ArrayStack prefixStack = new ArrayStack(); - 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 formulaJoins; + /** + * A Set used to make sure formula joins are only added once to a query. + */ + private HashSet formulaJoins; - private HashSet tableJoins; + private HashSet tableJoins; - private SqlTreeAlias alias; + private SqlTreeAlias alias; - private String currentPrefix; + private String currentPrefix; - private ArrayList 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 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(); + } + 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(); } - public void addEncryptedProp(BeanProperty p) { - if (encryptedProps == null){ - encryptedProps = new ArrayList(); - } - 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(); + + } 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(); - } + 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(); - - } 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(); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java index 029b7474b..d51486527 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java @@ -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()){ diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java index 6eeabdd53..0065904a5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java @@ -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 includes; + private Set 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 buildSelectExpressionChain() { - ArrayList list = new ArrayList(); - rootNode.buildSelectExpressionChain(list); - return list; - } - - /** - * Return the includes. Associated beans lists etc. - */ - public Set getIncludes() { - return includes; - } - - /** - * Set the association includes (Ones and Many's). - */ - public void setIncludes(Set 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 buildSelectExpressionChain() { + ArrayList list = new ArrayList(); + rootNode.buildSelectExpressionChain(list); + return list; + } - - public String getFromSql() { - return fromSql; - } + /** + * Return the includes. Associated beans lists etc. + */ + public Set 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 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; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java index 5dfa4f2d5..1cf9047ef 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java @@ -32,633 +32,647 @@ import org.slf4j.LoggerFactory; */ public class SqlTreeBuilder { - private static final Logger logger = LoggerFactory.getLogger(SqlTreeBuilder.class); + private static final Logger logger = LoggerFactory.getLogger(SqlTreeBuilder.class); - private final SpiQuery query; + private final SpiQuery query; + + private final BeanDescriptor desc; + + private final OrmQueryDetail queryDetail; + + private final StringBuilder summary = new StringBuilder(); + + private final CQueryPredicates predicates; + + private final boolean subQuery; + + /** + * Property if resultSet contains master and detail rows. + */ + private BeanPropertyAssocMany manyProperty; + + private String manyPropertyName; + + private final SqlTreeAlias alias; + + private final DefaultDbSqlContext ctx; + + private final HashSet selectIncludes = new HashSet(); + + private final ManyWhereJoins manyWhereJoins; + + private final TableJoin includeJoin; + + private final boolean rawSql; + + /** + * Construct for RawSql query. + */ + public SqlTreeBuilder(OrmQueryRequest request, CQueryPredicates predicates, + OrmQueryDetail queryDetail) { + + this.rawSql = true; + this.desc = request.getBeanDescriptor(); + this.query = null; + this.subQuery = false; + this.queryDetail = queryDetail; + this.predicates = predicates; + + this.includeJoin = null; + this.manyWhereJoins = null; + this.alias = null; + this.ctx = null; + } + + /** + * The predicates are used to determine if 'extra' joins are required to + * support the where and/or order by clause. If so these extra joins are added + * to the root node. + */ + public SqlTreeBuilder(String tableAliasPlaceHolder, String columnAliasPrefix, + OrmQueryRequest request, CQueryPredicates predicates) { + + this.rawSql = false; + this.desc = request.getBeanDescriptor(); + this.query = request.getQuery(); + + this.subQuery = Type.SUBQUERY.equals(query.getType()); + this.includeJoin = query.getIncludeTableJoin(); + this.manyWhereJoins = query.getManyWhereJoins(); + this.queryDetail = query.getDetail(); + + this.predicates = predicates; + this.alias = new SqlTreeAlias(request.getBeanDescriptor().getBaseTableAlias()); + this.ctx = new DefaultDbSqlContext(alias, tableAliasPlaceHolder, columnAliasPrefix, !subQuery); + } + + /** + * Build based on the includes and using the BeanJoinTree. + */ + public SqlTree build() { + + SqlTree sqlTree = new SqlTree(); + + summary.append(desc.getName()); + + // build the appropriate chain of SelectAdapter's + buildRoot(desc, sqlTree); + + // build the actual String + SqlTreeNode rootNode = sqlTree.getRootNode(); + + if (!rawSql) { + sqlTree.setSelectSql(buildSelectClause(rootNode)); + sqlTree.setFromSql(buildFromClause(rootNode)); + sqlTree.setInheritanceWhereSql(buildWhereClause(rootNode)); + sqlTree.setEncryptedProps(ctx.getEncryptedProps()); + } + sqlTree.setIncludes(queryDetail.getIncludes()); + sqlTree.setSummary(summary.toString()); + + if (manyPropertyName != null) { + ElPropertyValue manyPropEl = desc.getElGetValue(manyPropertyName); + sqlTree.setManyProperty(manyProperty, manyPropertyName, manyPropEl); + } + + return sqlTree; + } + + private String buildSelectClause(SqlTreeNode rootNode) { + + if (rawSql) { + return "Not Used"; + } + rootNode.appendSelect(ctx, subQuery); + + String selectSql = ctx.getContent(); + + // trim off the first comma + if (selectSql.length() >= SqlTreeNode.COMMA.length()) { + selectSql = selectSql.substring(SqlTreeNode.COMMA.length()); + } + + return selectSql; + } + + private String buildWhereClause(SqlTreeNode rootNode) { + + if (rawSql) { + return "Not Used"; + } + rootNode.appendWhere(ctx); + return ctx.getContent(); + } + + private String buildFromClause(SqlTreeNode rootNode) { + + if (rawSql) { + return "Not Used"; + } + rootNode.appendFrom(ctx, false); + return ctx.getContent(); + } + + private void buildRoot(BeanDescriptor desc, SqlTree sqlTree) { + + SqlTreeNode selectRoot = buildSelectChain(null, null, desc, null); + sqlTree.setRootNode(selectRoot); + + if (!rawSql) { + alias.addJoin(queryDetail.getIncludes(), desc); + alias.addJoin(predicates.getPredicateIncludes(), desc); + alias.addManyWhereJoins(manyWhereJoins.getJoins()); + + // build set of table alias + alias.buildAlias(); + + predicates.parseTableAlias(alias); + } + } + + /** + * Recursively build the query tree depending on what leaves in the tree + * should be included. + */ + private SqlTreeNode buildSelectChain(String prefix, BeanPropertyAssoc prop, + BeanDescriptor desc, List joinList) { + + List myJoinList = new ArrayList(); + + BeanPropertyAssocOne[] ones = desc.propertiesOne(); + for (int i = 0; i < ones.length; i++) { + String propPrefix = SplitName.add(prefix, ones[i].getName()); + if (isIncludeBean(propPrefix, ones[i])) { + selectIncludes.add(propPrefix); + buildSelectChain(propPrefix, ones[i], ones[i].getTargetDescriptor(), myJoinList); + } + } + + BeanPropertyAssocMany[] manys = desc.propertiesMany(); + for (int i = 0; i < manys.length; i++) { + String propPrefix = SplitName.add(prefix, manys[i].getName()); + if (isIncludeMany(prefix, propPrefix, manys[i])) { + selectIncludes.add(propPrefix); + buildSelectChain(propPrefix, manys[i], manys[i].getTargetDescriptor(), myJoinList); + } + } + + if (prefix == null && !rawSql) { + addManyWhereJoins(myJoinList); + } + + SqlTreeNode selectNode = buildNode(prefix, prop, desc, myJoinList); + if (joinList != null) { + joinList.add(selectNode); + } + return selectNode; + } + + /** + * Add joins used to support where clause predicates on 'many' properties. + *

+ * These joins are effectively independent of any fetch joins on 'many' + * properties. + *

+ */ + private void addManyWhereJoins(List myJoinList) { + + Set includes = manyWhereJoins.getJoins(); + for (String joinProp : includes) { + + BeanPropertyAssoc beanProperty = (BeanPropertyAssoc) desc + .getBeanPropertyFromPath(joinProp); + SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp, beanProperty); + myJoinList.add(nodeJoin); + } + } + + private SqlTreeNode buildNode(String prefix, BeanPropertyAssoc prop, BeanDescriptor desc, + List myList) { + + OrmQueryProperties queryProps = queryDetail.getChunk(prefix, false); + + SqlTreeProperties props = getBaseSelect(desc, queryProps); + + if (prefix == null) { + buildExtraJoins(desc, myList); + return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin); + + } else if (prop instanceof BeanPropertyAssocMany) { + return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany) prop, props, myList); + + } else { + return new SqlTreeNodeBean(prefix, prop, props, myList, true); + } + } + + /** + * Build extra joins to support properties used in where clause but not + * already in select clause. + */ + private void buildExtraJoins(BeanDescriptor desc, List myList) { + + if (rawSql) { + return; + } + + Set predicateIncludes = predicates.getPredicateIncludes(); + + if (predicateIncludes == null) { + return; + } + + // Note includes - basically means joins. + // The selectIncludes is the set of joins that are required to support + // the 'select' part of the query. We may need to add other joins to + // support the predicates or order by clauses. + + // remove ManyWhereJoins from the predicateIncludes + predicateIncludes.removeAll(manyWhereJoins.getJoins()); + + // look for predicateIncludes that are not in selectIncludes and add + // them as extra joins to the query + IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, + predicateIncludes); + + Collection extraJoins = extraJoinDistill.getExtraJoinRootNodes(); + if (extraJoins.isEmpty()) { + return; + + } else { + // add extra joins required to support predicates + // and/or order by clause + Iterator it = extraJoins.iterator(); + while (it.hasNext()) { + SqlTreeNodeExtraJoin extraJoin = it.next(); + myList.add(extraJoin); + + if (extraJoin.isManyJoin()) { + // as we are now going to join to the many then we need + // to add the distinct to the sql query to stop duplicate + // rows... + query.setDistinct(true); + } + } + } + } + + /** + * A subQuery has slightly different rules in that it just generates SQL (into + * the where clause) and its properties are not required to read the resultSet + * etc. + *

+ * This means it can included individual properties of an embedded bean. + *

+ */ + private void addPropertyToSubQuery(SqlTreeProperties selectProps, BeanDescriptor desc, + OrmQueryProperties queryProps, String propName) { + + BeanProperty p = desc.findBeanProperty(propName); + if (p == null) { + logger + .error("property [" + propName + "]not found on " + desc + " for query - excluding it."); + + } else if (p instanceof BeanPropertyAssoc && p.isEmbedded()) { + // if the property is embedded we need to lookup the real column name + int pos = propName.indexOf("."); + if (pos > -1) { + String name = propName.substring(pos + 1); + p = ((BeanPropertyAssoc) p).getTargetDescriptor().findBeanProperty(name); + } + } + + selectProps.add(p); + } + + private void addProperty(SqlTreeProperties selectProps, BeanDescriptor desc, + OrmQueryProperties queryProps, String propName) { + + if (subQuery) { + addPropertyToSubQuery(selectProps, desc, queryProps, propName); + return; + } + + int basePos = propName.indexOf('.'); + if (basePos > -1) { + // property on an embedded bean. Embedded beans do not yet + // support being partially populated so we include the + // 'base' property and make sure we only do that once + String baseName = propName.substring(0, basePos); + + // make sure we only included the base/embedded bean once + if (!selectProps.containsProperty(baseName)) { + BeanProperty p = desc.findBeanProperty(baseName); + if (p == null) { + String m = "property [" + propName + "] not found on " + desc + + " for query - excluding it."; + logger.error(m); + + } else if (p.isEmbedded()) { + // add the embedded bean (and effectively + // all its properties) + selectProps.add(p); + // also make sure it is added to included properties + // to avoid unnecessary lazy loading + selectProps.getIncludedProperties().add(baseName); + + } else { + String m = "property [" + p.getFullBeanName() + + "] expected to be an embedded bean for query - excluding it."; + logger.error(m); + } + } + + } else { + // find the property including searching the + // sub class hierarchy if required + BeanProperty p = desc.findBeanProperty(propName); + if (p == null) { + logger.error("property [" + propName + "] not found on " + desc + + " for query - excluding it."); + + } else if (p.isId()) { + // do not bother to include id for normal queries as the + // id is always added (except for subQueries) + + } else if (p instanceof BeanPropertyAssoc) { + // need to check if this property should be + // excluded. This occurs when this property is + // included as a bean join. With a bean join + // the property should be excluded as the bean + // join has its own node in the SqlTree. + if (!queryProps.isIncludedBeanJoin(p.getName())) { + // include the property... which basically + // means include the foreign key column(s) + selectProps.add(p); + } + } else { + selectProps.add(p); + } + } + } + + private SqlTreeProperties getBaseSelectPartial(BeanDescriptor desc, + OrmQueryProperties queryProps) { + + SqlTreeProperties selectProps = new SqlTreeProperties(); + selectProps.setReadOnly(queryProps.isReadOnly()); + selectProps.setIncludedProperties(queryProps.getAllIncludedProperties()); + + // add properties in the order in which they appear + // in the query. Gives predictable sql/properties for + // use with SqlSelect type queries. + + // Also note that this can include transient properties. + // This makes sense for transient properties used to + // hold sum() count() type values (with SqlSelect) + Iterator it = queryProps.getSelectProperties(); + while (it.hasNext()) { + String propName = it.next(); + if (propName.length() > 0) { + addProperty(selectProps, desc, queryProps, propName); + } + } + + return selectProps; + } + + private SqlTreeProperties getBaseSelect(BeanDescriptor desc, OrmQueryProperties queryProps) { + + boolean partial = queryProps != null && !queryProps.allProperties(); + if (partial) { + return getBaseSelectPartial(desc, queryProps); + } + + SqlTreeProperties selectProps = new SqlTreeProperties(); + + // normal simple properties of the bean + selectProps.add(desc.propertiesBaseScalar()); + selectProps.add(desc.propertiesBaseCompound()); + selectProps.add(desc.propertiesEmbedded()); + + BeanPropertyAssocOne[] propertiesOne = desc.propertiesOne(); + for (int i = 0; i < propertiesOne.length; i++) { + if (queryProps != null && queryProps.isIncludedBeanJoin(propertiesOne[i].getName())) { + // if it is a joined bean... then don't add the property + // as it will have its own entire Node in the SqlTree + } else { + selectProps.add(propertiesOne[i]); + } + } + + selectProps.setTableJoins(desc.tableJoins()); + + InheritInfo inheritInfo = desc.getInheritInfo(); + if (inheritInfo != null) { + // add sub type properties + inheritInfo.addChildrenProperties(selectProps); + + } + return selectProps; + } + + /** + * Return true if this many node should be included in the query. + */ + private boolean isIncludeMany(String prefix, String propName, BeanPropertyAssocMany manyProp) { + + if (queryDetail.isJoinsEmpty()) { + return false; + } + + if (queryDetail.includes(propName)) { + + if (manyProperty != null) { + // only one many associated allowed to be included in fetch + if (logger.isDebugEnabled()) { + String msg = "Not joining [" + propName + "] as already joined to a Many[" + manyProperty + + "]."; + logger.debug(msg); + } + return false; + } + + manyProperty = manyProp; + manyPropertyName = propName; + summary.append(" +many:").append(propName); + return true; + } + return false; + } + + /** + * Test to see if we are including this node into the query. + *

+ * Return true if this node is FULLY included resulting in table join. If the + * node is not included but its parent has been included then a "bean proxy" + * is added and false is returned. + *

+ */ + private boolean isIncludeBean(String prefix, BeanPropertyAssocOne prop) { + + if (queryDetail.includes(prefix)) { + // explicitly included + summary.append(", ").append(prefix); + String[] splitNames = SplitName.split(prefix); + queryDetail.includeBeanJoin(splitNames[0], splitNames[1]); + return true; + } + + return false; + } + + /** + * Takes the select includes and the predicates includes and determines the + * extra joins required to support the predicates (that are not already + * supported by the select includes). + *

+ * This returns ONLY the leaves. The joins for the leaves + *

+ */ + private static class IncludesDistiller { + + private final Set selectIncludes; + private final Set predicateIncludes; + + /** + * Contains the 'root' extra joins. We only return the roots back. + */ + private final Map joinRegister = new HashMap(); + + /** + * Register of all the extra join nodes. + */ + private final Map rootRegister = new HashMap(); private final BeanDescriptor desc; - private final OrmQueryDetail queryDetail; - - private final StringBuilder summary = new StringBuilder(); - - private final CQueryPredicates predicates; - - private final boolean subQuery; - - /** - * Property if resultSet contains master and detail rows. - */ - private BeanPropertyAssocMany manyProperty; - - private String manyPropertyName; - - private final SqlTreeAlias alias; - - private final DefaultDbSqlContext ctx; - - private final HashSet selectIncludes = new HashSet(); - - private final ManyWhereJoins manyWhereJoins; - - private final TableJoin includeJoin; - - private final boolean rawSql; - - /** - * Construct for RawSql query. - */ - public SqlTreeBuilder(OrmQueryRequest request, CQueryPredicates predicates, OrmQueryDetail queryDetail) { - - this.rawSql = true; - this.desc = request.getBeanDescriptor(); - this.query = null; - this.subQuery = false; - this.queryDetail = queryDetail; - this.predicates = predicates; - - this.includeJoin = null; - this.manyWhereJoins = null; - this.alias = null; - this.ctx = null; - } - - /** - * The predicates are used to determine if 'extra' joins are required to - * support the where and/or order by clause. If so these extra joins are - * added to the root node. - */ - public SqlTreeBuilder(String tableAliasPlaceHolder, String columnAliasPrefix, OrmQueryRequest request, CQueryPredicates predicates) { - - this.rawSql = false; - this.desc = request.getBeanDescriptor(); - this.query = request.getQuery(); - - this.subQuery = Type.SUBQUERY.equals(query.getType()); - this.includeJoin = query.getIncludeTableJoin(); - this.manyWhereJoins = query.getManyWhereJoins(); - this.queryDetail = query.getDetail(); - - this.predicates = predicates; - this.alias = new SqlTreeAlias(request.getBeanDescriptor().getBaseTableAlias()); - this.ctx = new DefaultDbSqlContext(alias, tableAliasPlaceHolder, columnAliasPrefix, !subQuery); + private IncludesDistiller(BeanDescriptor desc, Set selectIncludes, + Set predicateIncludes) { + this.desc = desc; + this.selectIncludes = selectIncludes; + this.predicateIncludes = predicateIncludes; } /** - * Build based on the includes and using the BeanJoinTree. - */ - public SqlTree build() { - - SqlTree sqlTree = new SqlTree(); - - summary.append(desc.getName()); - - // build the appropriate chain of SelectAdapter's - buildRoot(desc, sqlTree); - - // build the actual String - SqlTreeNode rootNode = sqlTree.getRootNode(); - - if (!rawSql){ - sqlTree.setSelectSql(buildSelectClause(rootNode)); - sqlTree.setFromSql(buildFromClause(rootNode)); - sqlTree.setInheritanceWhereSql(buildWhereClause(rootNode)); - sqlTree.setEncryptedProps(ctx.getEncryptedProps()); - } - sqlTree.setIncludes(queryDetail.getIncludes()); - sqlTree.setSummary(summary.toString()); - - if (manyPropertyName != null){ - ElPropertyValue manyPropEl = desc.getElGetValue(manyPropertyName); - sqlTree.setManyProperty(manyProperty, manyPropertyName, manyPropEl); - } - - return sqlTree; - } - - private String buildSelectClause(SqlTreeNode rootNode) { - - if (rawSql){ - return "Not Used"; - } - rootNode.appendSelect(ctx, subQuery); - - String selectSql = ctx.getContent(); - - // trim off the first comma - if (selectSql.length() >= SqlTreeNode.COMMA.length()) { - selectSql = selectSql.substring(SqlTreeNode.COMMA.length()); - } - - return selectSql; - } - - private String buildWhereClause(SqlTreeNode rootNode) { - - if (rawSql){ - return "Not Used"; - } - rootNode.appendWhere(ctx); - return ctx.getContent(); - } - - private String buildFromClause(SqlTreeNode rootNode) { - - if (rawSql){ - return "Not Used"; - } - rootNode.appendFrom(ctx, false); - return ctx.getContent(); - } - - private void buildRoot(BeanDescriptor desc, SqlTree sqlTree) { - - SqlTreeNode selectRoot = buildSelectChain(null, null, desc, null); - sqlTree.setRootNode(selectRoot); - - if (!rawSql){ - alias.addJoin(queryDetail.getIncludes(), desc); - alias.addJoin(predicates.getPredicateIncludes(), desc); - alias.addManyWhereJoins(manyWhereJoins.getJoins()); - - // build set of table alias - alias.buildAlias(); - - predicates.parseTableAlias(alias); - } - } - - /** - * Recursively build the query tree depending on what leaves in the tree - * should be included. - */ - private SqlTreeNode buildSelectChain(String prefix, BeanPropertyAssoc prop, BeanDescriptor desc, List joinList) { - - List myJoinList = new ArrayList(); - - BeanPropertyAssocOne[] ones = desc.propertiesOne(); - for (int i = 0; i < ones.length; i++) { - String propPrefix = SplitName.add(prefix, ones[i].getName()); - if (isIncludeBean(propPrefix, ones[i])) { - selectIncludes.add(propPrefix); - buildSelectChain(propPrefix, ones[i], ones[i].getTargetDescriptor(), myJoinList); - } - } - - BeanPropertyAssocMany[] manys = desc.propertiesMany(); - for (int i = 0; i < manys.length; i++) { - String propPrefix = SplitName.add(prefix, manys[i].getName()); - if (isIncludeMany(prefix, propPrefix, manys[i])) { - selectIncludes.add(propPrefix); - buildSelectChain(propPrefix, manys[i], manys[i].getTargetDescriptor(), myJoinList); - } - } - - if (prefix == null && !rawSql) { - addManyWhereJoins(myJoinList); - } - - SqlTreeNode selectNode = buildNode(prefix, prop, desc, myJoinList); - if (joinList != null) { - joinList.add(selectNode); - } - return selectNode; - } - - /** - * Add joins used to support where clause predicates on 'many' properties. + * Build the collection of extra joins returning just the roots. *

- * These joins are effectively independent of any fetch joins on 'many' properties. + * each root returned here could contain a little tree of joins. This + * follows the more natural pattern and allows for forcing outer joins from + * a join to a 'many' down through the rest of its tree. *

*/ - private void addManyWhereJoins(List myJoinList) { + private Collection getExtraJoinRootNodes() { - Set includes = manyWhereJoins.getJoins(); - for (String joinProp : includes) { - - BeanPropertyAssoc beanProperty = (BeanPropertyAssoc) desc.getBeanPropertyFromPath(joinProp); - SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp, beanProperty); - myJoinList.add(nodeJoin); - } + String[] extras = findExtras(); + if (extras.length == 0) { + return rootRegister.values(); + } + + // sort so we process only getting the leaves + // excluding nodes between root and the leaf + Arrays.sort(extras); + + // reverse order so get the leaves first... + for (int i = 0; i < extras.length; i++) { + createExtraJoin(extras[i]); + } + + return rootRegister.values(); } - private SqlTreeNode buildNode(String prefix, BeanPropertyAssoc prop, BeanDescriptor desc, List myList) { + private void createExtraJoin(String includeProp) { - OrmQueryProperties queryProps = queryDetail.getChunk(prefix, false); + SqlTreeNodeExtraJoin extraJoin = createJoinLeaf(includeProp); + if (extraJoin != null) { + // add the extra join... - SqlTreeProperties props = getBaseSelect(desc, queryProps); + // find root of this extra join... linking back to the + // parents (creating the tree) as it goes. + SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin); - if (prefix == null) { - buildExtraJoins(desc, myList); - return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin); - - } else if (prop instanceof BeanPropertyAssocMany) { - return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany) prop, props, myList); - - } else { - return new SqlTreeNodeBean(prefix, prop, props, myList, true); - } + // register the root because these are the only ones we + // return back. + rootRegister.put(root.getName(), root); + } } /** - * Build extra joins to support properties used in where clause but not - * already in select clause. + * Create a SqlTreeNodeExtraJoin, register and return it. */ - private void buildExtraJoins(BeanDescriptor desc, List myList) { + private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { - if (rawSql){ - return; - } - - Set predicateIncludes = predicates.getPredicateIncludes(); - - if (predicateIncludes == null) { - return; - } - - // Note includes - basically means joins. - // The selectIncludes is the set of joins that are required to support - // the 'select' part of the query. We may need to add other joins to - // support the predicates or order by clauses. - - // remove ManyWhereJoins from the predicateIncludes - predicateIncludes.removeAll(manyWhereJoins.getJoins()); - - // look for predicateIncludes that are not in selectIncludes and add - // them as extra joins to the query - IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes); - - Collection extraJoins = extraJoinDistill.getExtraJoinRootNodes(); - if (extraJoins.isEmpty()) { - return; - - } else { - // add extra joins required to support predicates - // and/or order by clause - Iterator it = extraJoins.iterator(); - while (it.hasNext()) { - SqlTreeNodeExtraJoin extraJoin = it.next(); - myList.add(extraJoin); - - if (extraJoin.isManyJoin()) { - // as we are now going to join to the many then we need - // to add the distinct to the sql query to stop duplicate - // rows... - query.setDistinct(true); - } - } + ElPropertyValue elGetValue = desc.getElGetValue(propertyName); + + if (elGetValue == null) { + // this can occur for master detail queries + // with concatenated keys (so not an error now) + return null; + } + BeanProperty beanProperty = elGetValue.getBeanProperty(); + if (beanProperty instanceof BeanPropertyAssoc) { + BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; + if (assocProp.isEmbedded()) { + // no extra join required for embedded beans + return null; } + SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp); + joinRegister.put(propertyName, extraJoin); + return extraJoin; + } + return null; } /** - * A subQuery has slightly different rules in that it just generates SQL - * (into the where clause) and its properties are not required to read the - * resultSet etc. + * Find the root the this extra join tree. *

- * This means it can included individual properties of an embedded bean. + * This may need to create a parent join implicitly if a predicate join + * 'skips' a level. e.g. where details.user.id = 1 (maybe join to details is + * not specified and is implicitly created. *

*/ - private void addPropertyToSubQuery(SqlTreeProperties selectProps, BeanDescriptor desc, OrmQueryProperties queryProps, String propName) { + private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, + SqlTreeNodeExtraJoin childJoin) { - BeanProperty p = desc.findBeanProperty(propName); - if (p == null) { - logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it."); + int dotPos = includeProp.lastIndexOf('.'); + if (dotPos == -1) { + // no parent possible(parent is root) + return childJoin; - } - else if (p instanceof BeanPropertyAssoc && p.isEmbedded()) { - // if the property is embedded we need to lookup the real column name - int pos = propName.indexOf("."); - if (pos > -1) { - String name = propName.substring(pos + 1); - p = ((BeanPropertyAssoc) p).getTargetDescriptor().findBeanProperty(name); - } + } else { + // look in register ... + String parentPropertyName = includeProp.substring(0, dotPos); + if (selectIncludes.contains(parentPropertyName)) { + // parent already handled by select + return childJoin; } - selectProps.add(p); - } - - private void addProperty(SqlTreeProperties selectProps, BeanDescriptor desc, OrmQueryProperties queryProps, String propName) { - - if (subQuery) { - addPropertyToSubQuery(selectProps, desc, queryProps, propName); - return; + SqlTreeNodeExtraJoin parentJoin = joinRegister.get(parentPropertyName); + if (parentJoin == null) { + // we need to create this the parent implicitly... + parentJoin = createJoinLeaf(parentPropertyName); } - int basePos = propName.indexOf('.'); - if (basePos > -1) { - // property on an embedded bean. Embedded beans do not yet - // support being partially populated so we include the - // 'base' property and make sure we only do that once - String baseName = propName.substring(0, basePos); - - // make sure we only included the base/embedded bean once - if (!selectProps.containsProperty(baseName)) { - BeanProperty p = desc.findBeanProperty(baseName); - if (p == null) { - String m = "property [" + propName + "] not found on " + desc + " for query - excluding it."; - logger.error(m); - - } else if (p.isEmbedded()) { - // add the embedded bean (and effectively - // all its properties) - selectProps.add(p); - // also make sure it is added to included properties - // to avoid unnecessary lazy loading - selectProps.getIncludedProperties().add(baseName); - - } else { - String m = "property [" + p.getFullBeanName() - + "] expected to be an embedded bean for query - excluding it."; - logger.error(m); - } - } - - } else { - // find the property including searching the - // sub class hierarchy if required - BeanProperty p = desc.findBeanProperty(propName); - if (p == null) { - logger.error("property [" + propName + "] not found on " + desc - + " for query - excluding it."); - - } else if (p.isId()) { - // do not bother to include id for normal queries as the - // id is always added (except for subQueries) - - } else if (p instanceof BeanPropertyAssoc) { - // need to check if this property should be - // excluded. This occurs when this property is - // included as a bean join. With a bean join - // the property should be excluded as the bean - // join has its own node in the SqlTree. - if (!queryProps.isIncludedBeanJoin(p.getName())) { - // include the property... which basically - // means include the foreign key column(s) - selectProps.add(p); - } - } else { - selectProps.add(p); - } - } - } - - private SqlTreeProperties getBaseSelectPartial(BeanDescriptor desc, OrmQueryProperties queryProps) { - - SqlTreeProperties selectProps = new SqlTreeProperties(); - selectProps.setReadOnly(queryProps.isReadOnly()); - selectProps.setIncludedProperties(queryProps.getAllIncludedProperties()); - - // add properties in the order in which they appear - // in the query. Gives predictable sql/properties for - // use with SqlSelect type queries. - - // Also note that this can include transient properties. - // This makes sense for transient properties used to - // hold sum() count() type values (with SqlSelect) - Iterator it = queryProps.getSelectProperties(); - while (it.hasNext()) { - String propName = it.next(); - if (propName.length() > 0) { - addProperty(selectProps, desc, queryProps, propName); - } - } - - return selectProps; - } - - private SqlTreeProperties getBaseSelect(BeanDescriptor desc, OrmQueryProperties queryProps) { - - boolean partial = queryProps != null && !queryProps.allProperties(); - if (partial) { - return getBaseSelectPartial(desc, queryProps); - } - - SqlTreeProperties selectProps = new SqlTreeProperties(); - - // normal simple properties of the bean - selectProps.add(desc.propertiesBaseScalar()); - selectProps.add(desc.propertiesBaseCompound()); - selectProps.add(desc.propertiesEmbedded()); - - BeanPropertyAssocOne[] propertiesOne = desc.propertiesOne(); - for (int i = 0; i < propertiesOne.length; i++) { - if (queryProps != null && queryProps.isIncludedBeanJoin(propertiesOne[i].getName())) { - // if it is a joined bean... then don't add the property - // as it will have its own entire Node in the SqlTree - } else { - selectProps.add(propertiesOne[i]); - } - } - - selectProps.setTableJoins(desc.tableJoins()); - - InheritInfo inheritInfo = desc.getInheritInfo(); - if (inheritInfo != null) { - // add sub type properties - inheritInfo.addChildrenProperties(selectProps); - - } - return selectProps; + parentJoin.addChild(childJoin); + return findExtraJoinRoot(parentPropertyName, parentJoin); + } } /** - * Return true if this many node should be included in the query. + * Find the extra joins required by predicates and not already taken care of + * by the select. */ - private boolean isIncludeMany(String prefix, String propName, BeanPropertyAssocMany manyProp) { + private String[] findExtras() { - if (queryDetail.isJoinsEmpty()) { - return false; + List extras = new ArrayList(); + + for (String predProp : predicateIncludes) { + if (!selectIncludes.contains(predProp)) { + extras.add(predProp); } - - if (queryDetail.includes(propName)) { - - if (manyProperty != null) { - // only one many associated allowed to be included in fetch - if (logger.isDebugEnabled()) { - String msg = "Not joining [" + propName + "] as already joined to a Many[" + manyProperty + "]."; - logger.debug(msg); - } - return false; - } - - manyProperty = manyProp; - manyPropertyName = propName; - summary.append(" +many:").append(propName); - return true; - } - return false; + } + return extras.toArray(new String[extras.size()]); } - /** - * Test to see if we are including this node into the query. - *

- * Return true if this node is FULLY included resulting in table join. If - * the node is not included but its parent has been included then a "bean - * proxy" is added and false is returned. - *

- */ - private boolean isIncludeBean(String prefix, BeanPropertyAssocOne prop) { - - if (queryDetail.includes(prefix)) { - // explicitly included - summary.append(", ").append(prefix); - String[] splitNames = SplitName.split(prefix); - queryDetail.includeBeanJoin(splitNames[0], splitNames[1]); - return true; - } - - return false; - } - - /** - * Takes the select includes and the predicates includes and determines the - * extra joins required to support the predicates (that are not already - * supported by the select includes). - *

- * This returns ONLY the leaves. The joins for the leaves - *

- */ - private static class IncludesDistiller { - - private final Set selectIncludes; - private final Set predicateIncludes; - - /** - * Contains the 'root' extra joins. We only return the roots back. - */ - private final Map joinRegister = new HashMap(); - - /** - * Register of all the extra join nodes. - */ - private final Map rootRegister = new HashMap(); - - private final BeanDescriptor desc; - - private IncludesDistiller(BeanDescriptor desc, Set selectIncludes, Set predicateIncludes) { - this.desc = desc; - this.selectIncludes = selectIncludes; - this.predicateIncludes = predicateIncludes; - } - - /** - * Build the collection of extra joins returning just the roots. - *

- * each root returned here could contain a little tree of joins. This - * follows the more natural pattern and allows for forcing outer joins - * from a join to a 'many' down through the rest of its tree. - *

- */ - private Collection getExtraJoinRootNodes() { - - String[] extras = findExtras(); - if (extras.length == 0) { - return rootRegister.values(); - } - - // sort so we process only getting the leaves - // excluding nodes between root and the leaf - Arrays.sort(extras); - - // reverse order so get the leaves first... - for (int i = 0; i < extras.length; i++) { - createExtraJoin(extras[i]); - } - - return rootRegister.values(); - } - - private void createExtraJoin(String includeProp) { - - SqlTreeNodeExtraJoin extraJoin = createJoinLeaf(includeProp); - if (extraJoin != null) { - // add the extra join... - - // find root of this extra join... linking back to the - // parents (creating the tree) as it goes. - SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin); - - // register the root because these are the only ones we - // return back. - rootRegister.put(root.getName(), root); - } - } - - /** - * Create a SqlTreeNodeExtraJoin, register and return it. - */ - private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { - - ElPropertyValue elGetValue = desc.getElGetValue(propertyName); - - if (elGetValue == null) { - // this can occur for master detail queries - // with concatenated keys (so not an error now) - return null; - } - BeanProperty beanProperty = elGetValue.getBeanProperty(); - if (beanProperty instanceof BeanPropertyAssoc) { - BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; - if (assocProp.isEmbedded()) { - // no extra join required for embedded beans - return null; - } - SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp); - joinRegister.put(propertyName, extraJoin); - return extraJoin; - } - return null; - } - - /** - * Find the root the this extra join tree. - *

- * This may need to create a parent join implicitly if a predicate join - * 'skips' a level. e.g. where details.user.id = 1 (maybe join to - * details is not specified and is implicitly created. - *

- */ - private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeExtraJoin childJoin) { - - int dotPos = includeProp.lastIndexOf('.'); - if (dotPos == -1) { - // no parent possible(parent is root) - return childJoin; - - } else { - // look in register ... - String parentPropertyName = includeProp.substring(0, dotPos); - if (selectIncludes.contains(parentPropertyName)) { - // parent already handled by select - return childJoin; - } - - SqlTreeNodeExtraJoin parentJoin = joinRegister.get(parentPropertyName); - if (parentJoin == null) { - // we need to create this the parent implicitly... - parentJoin = createJoinLeaf(parentPropertyName); - } - - parentJoin.addChild(childJoin); - return findExtraJoinRoot(parentPropertyName, parentJoin); - } - } - - /** - * Find the extra joins required by predicates and not already taken - * care of by the select. - */ - private String[] findExtras() { - - List extras = new ArrayList(); - - for (String predProp : predicateIncludes) { - if (!selectIncludes.contains(predProp)) { - extras.add(predProp); - } - } - return extras.toArray(new String[extras.size()]); - } - - } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java index a9abb27ae..838176cc7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java @@ -8,48 +8,40 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext; public interface SqlTreeNode { - /** - * the new line character used. - *

- * Note that this is removed for logging sql to the transaction log. - *

- */ - 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 selectChain); + public void buildSelectExpressionChain(List 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. - *

- * At a high level this actually controls the reading of the data from the - * jdbc resultSet and putting it into the bean etc. - *

- * - */ - public void load(DbReadContext ctx, Object parentBean) throws SQLException; + /** + * Load the appropriate information from the SqlSelectReader. + *

+ * At a high level this actually controls the reading of the data from the + * jdbc resultSet and putting it into the bean etc. + *

+ * + */ + public void load(DbReadContext ctx, Object parentBean) throws SQLException; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java index 234a2bd3e..b356b676c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -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 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 includedProps; + /** + * Set to true if this is a partial object fetch. + */ + final boolean partialObject; - final Map pathMap; - - public SqlTreeNodeBean(String prefix, BeanPropertyAssoc beanProp, - SqlTreeProperties props, List 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 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 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 createPathMap(String prefix, BeanDescriptor desc) { - - BeanPropertyAssocMany[] manys = desc.propertiesMany(); - - HashMap m = new HashMap(); - 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 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 includedProps; + + final Map pathMap; + + public SqlTreeNodeBean(String prefix, BeanPropertyAssoc beanProp, SqlTreeProperties props, + List 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 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 createPathMap(String prefix, BeanDescriptor desc) { + + BeanPropertyAssocMany[] manys = desc.propertiesMany(); + + HashMap m = new HashMap(); + 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 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; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java index 68e7fe832..0f00639cc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java @@ -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 myList) { - super(prefix, prop, prop.getTargetDescriptor(), props, myList, true); - } + public SqlTreeNodeManyRoot(String prefix, BeanPropertyAssocMany prop, SqlTreeProperties props, List 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); - } - - } diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java index fd16ed742..e8a0a76d5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java @@ -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 persistingBeans = new HashSet(); + IdentityHashMap persistingBeans; HashSet deletingBeansHash; HashMap m2mIntersectionSave; - - TransactionLogBuffer logBuffer; - HashMap> derivedRelMap; - - private final Map userObjects = new ConcurrentHashMap(); + Map 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 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(); + } + 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(); + } userObjects.put(name, value); } public Object getUserObject(String name) { + if (userObjects == null) { + return null; + } return userObjects.get(name); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogBuffer.java deleted file mode 100644 index 6a0abc570..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogBuffer.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.avaje.ebeaninternal.server.transaction; - -import java.util.ArrayList; -import java.util.List; - -/** - * Buffer of transaction messages. - *

- * 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. - *

- * - * @author rbygrave - * - */ -public class TransactionLogBuffer { - - private final String transactionId; - - private final ArrayList 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(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 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; - } - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogManager.java deleted file mode 100644 index 192c41fb0..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogManager.java +++ /dev/null @@ -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. - *

- * 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. - *

- */ - 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); - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogWriter.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogWriter.java deleted file mode 100644 index 9b1ec91e9..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogWriter.java +++ /dev/null @@ -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(); - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java index 703459974..2d9eba81e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java @@ -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); diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLogger.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLogger.java deleted file mode 100644 index ff6aa742c..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLogger.java +++ /dev/null @@ -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. - *

- * File based logger that can switch daily. It will include the date in the - * files name. - *

- *

- * 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. - *

- */ -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 logBufferQueue = new ConcurrentLinkedQueue(); - - 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 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; - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLoggerWrapper.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLoggerWrapper.java deleted file mode 100644 index 4c812946d..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLoggerWrapper.java +++ /dev/null @@ -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. - *

- * 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. - *

- * - * @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(); - } - - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/JuliTransactionLogger.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/JuliTransactionLogger.java deleted file mode 100644 index 2ee02b45b..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/JuliTransactionLogger.java +++ /dev/null @@ -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. - *

- * See {@link ServerConfig#setUseJuliTransactionLogger(boolean)} - *

- * @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 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); - } - - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/LogTime.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/LogTime.java deleted file mode 100644 index 7c4287ba0..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/LogTime.java +++ /dev/null @@ -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. - *

- * Used in place of SimpleDateFormat which requires synchronization. Determines - * if the day has changed and returns a format of the current time and day. - *

- */ -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. - *

- * The separators is a String[2] with the first string separating hours - * minutes and seconds and the second separating seconds from millis. - *

- *

- * The default is {":","."} - *

- */ - 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. - *

- * Format used is hours:minutes:seconds.millis - *

- */ - 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); - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/SimpleLogger.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/SimpleLogger.java deleted file mode 100644 index a2422654b..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/SimpleLogger.java +++ /dev/null @@ -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. - *

- * 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. - *

- */ -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; - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/package.html b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/package.html deleted file mode 100644 index 2ccf02dd6..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/package.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Transaction Logging - - -Transaction logging - - - \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/basic/MainFutureList.java b/src/test/java/com/avaje/tests/basic/MainFutureList.java index 922ed8c5f..3b9781fcf 100644 --- a/src/test/java/com/avaje/tests/basic/MainFutureList.java +++ b/src/test/java/com/avaje/tests/basic/MainFutureList.java @@ -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 { diff --git a/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java b/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java index e87e8d0b2..3877fedef 100644 --- a/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java +++ b/src/test/java/com/avaje/tests/basic/TestLogTransLogOnError.java @@ -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 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 diff --git a/src/test/java/com/avaje/tests/inheritance/TestDuplcateKeyException.java b/src/test/java/com/avaje/tests/inheritance/TestDuplcateKeyException.java index 6d22c6a3b..e685c9675 100644 --- a/src/test/java/com/avaje/tests/inheritance/TestDuplcateKeyException.java +++ b/src/test/java/com/avaje/tests/inheritance/TestDuplcateKeyException.java @@ -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); } diff --git a/src/test/java/com/avaje/tests/query/TestSubQuery.java b/src/test/java/com/avaje/tests/query/TestSubQuery.java index f91ab024c..2299bf5bf 100644 --- a/src/test/java/com/avaje/tests/query/TestSubQuery.java +++ b/src/test/java/com/avaje/tests/query/TestSubQuery.java @@ -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) diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 9ef5d68e9..cc3d9ef66 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -8,7 +8,6 @@ - - + - + + + + + + \ No newline at end of file