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