package org.apache.ojb.broker.accesslayer.sql;
/* Copyright 2002-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.Collection;
import java.util.Enumeration;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.ProcedureDescriptor;
import org.apache.ojb.broker.platforms.Platform;
import org.apache.ojb.broker.query.BetweenCriteria;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.ExistsCriteria;
import org.apache.ojb.broker.query.FieldCriteria;
import org.apache.ojb.broker.query.InCriteria;
import org.apache.ojb.broker.query.NullCriteria;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.SelectionCriteria;
import org.apache.ojb.broker.query.SqlCriteria;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
/**
* This Class is responsible for building sql statements
* Objects fields and their repective values are accessed by Java reflection
*
* @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
* @author <a href="mailto:rgallagh@bellsouth.net">Ron Gallagher</a>
* @author <a href="mailto:rburt3@mchsi.com">Randall Burt</a>
* @version $Id: SqlGeneratorDefaultImpl.java,v 1.23.2.1 2005/01/13 19:20:03 arminw Exp $
*/
public class SqlGeneratorDefaultImpl implements SqlGenerator
{
private Logger logger = LoggerFactory.getLogger(SqlGeneratorDefaultImpl.class);
private Map m_sqlCacheMap = new WeakHashMap();
private Platform m_platform;
/**
* Constructor is protected, use getInstance() to retrieve the singleton
* instance of this class.
*/
public SqlGeneratorDefaultImpl(Platform pf)
{
this.m_platform = pf;
}
/**
* generate a prepared DELETE-Statement for the Class
* described by cld.
* @param cld the ClassDescriptor
*/
public String getPreparedDeleteStatement(ClassDescriptor cld)
{
SqlStatement sql;
String result;
ProcedureDescriptor pd = cld.getDeleteProcedure();
if (pd == null)
{
sql = new SqlDeleteByPkStatement(cld, logger);
}
else
{
sql = new SqlProcedureStatement(pd, logger);
}
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
return result;
}
/**
* generate a prepared INSERT-Statement for the Class
* described by cld.
* @param cld the ClassDescriptor
*/
public String getPreparedInsertStatement(ClassDescriptor cld)
{
SqlStatement sql;
String result;
ProcedureDescriptor pd = cld.getInsertProcedure();
if (pd == null)
{
sql = new SqlInsertStatement(cld, logger);
}
else
{
sql = new SqlProcedureStatement(pd, logger);
}
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
return result;
}
/**
* generate a prepared SELECT-Statement for the Class
* described by cld
* @param cld the ClassDescriptor
*/
public String getPreparedSelectByPkStatement(ClassDescriptor cld)
{
SqlStatement sql;
String result;
sql = new SqlSelectByPkStatement(cld, logger);
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
return result;
}
/**
* generate a select-Statement according to query
* @param query the Query
* @param cld the ClassDescriptor
*/
public String getPreparedSelectStatement(Query query, ClassDescriptor cld)
{
SqlCacheKey key = new SqlCacheKey(query, cld, SqlCacheKey.TYPE_SELECT);
String result = (String) m_sqlCacheMap.get(key);
if (result == null)
{
SqlStatement sql = new SqlSelectStatement(m_platform, cld, query, logger);
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
m_sqlCacheMap.put(key, result);
}
return result;
}
/**
* generate a prepared UPDATE-Statement for the Class
* described by cld
* @param cld the ClassDescriptor
*/
public String getPreparedUpdateStatement(ClassDescriptor cld)
{
SqlStatement sql;
String result;
ProcedureDescriptor pd = cld.getUpdateProcedure();
if (pd == null)
{
sql = new SqlUpdateStatement(cld, logger);
}
else
{
sql = new SqlProcedureStatement(pd, logger);
}
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
return result;
}
/**
* generate an INSERT-Statement for M:N indirection table
*
* @param table
* @param pkColumns1
* @param pkColumns2
*/
public String getInsertMNStatement(String table, String[] pkColumns1, String[] pkColumns2)
{
SqlStatement sql;
String result;
String[] cols = new String[pkColumns1.length + pkColumns2.length];
System.arraycopy(pkColumns1, 0, cols, 0, pkColumns1.length);
System.arraycopy(pkColumns2, 0, cols, pkColumns1.length, pkColumns2.length);
sql = new SqlInsertMNStatement(table, cols, logger);
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
return result;
}
/**
* generate a SELECT-Statement for M:N indirection table
*
* @param table the indirection table
* @param selectColumns selected columns
* @param columns for where
*/
public String getSelectMNStatement(String table, String[] selectColumns, String[] columns)
{
SqlStatement sql;
String result;
sql = new SqlSelectMNStatement(table, selectColumns, columns, logger);
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
return result;
}
/**
* generate a DELETE-Statement for M:N indirection table
*
* @param table
* @param pkColumns1
* @param pkColumns2
*/
public String getDeleteMNStatement(String table, String[] pkColumns1, String[] pkColumns2)
{
SqlStatement sql;
String result;
String[] cols;
if (pkColumns2 == null)
{
cols = pkColumns1;
}
else
{
cols = new String[pkColumns1.length + pkColumns2.length];
System.arraycopy(pkColumns1, 0, cols, 0, pkColumns1.length);
System.arraycopy(pkColumns2, 0, cols, pkColumns1.length, pkColumns2.length);
}
sql = new SqlDeleteMNStatement(table, cols, logger);
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
return result;
}
/**
* generate a select-Statement according to query
* @param query the Query
* @param cld the ClassDescriptor
*/
public String getSelectStatementDep(Query query, ClassDescriptor cld)
{
SqlCacheKey key = new SqlCacheKey(query, cld, SqlCacheKey.TYPE_SELECT);
String result = (String) m_sqlCacheMap.get(key);
if (result == null)
{
SqlStatement sql = new SqlSelectStatement(m_platform, cld, query, logger);
result = sql.getStatement();
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
m_sqlCacheMap.put(key, result);
}
return result;
}
/**
* @param crit Selection criteria
*
* 26/06/99 Change statement to a StringBuffer for efficiency
*/
public String asSQLStatement(Criteria crit, ClassDescriptor cld)
{
Enumeration e = crit.getElements();
StringBuffer statement = new StringBuffer();
while (e.hasMoreElements())
{
Object o = e.nextElement();
if (o instanceof Criteria)
{
String addAtStart;
String addAtEnd;
Criteria pc = (Criteria) o;
// need to add parenthesises?
if (pc.isEmbraced())
{
addAtStart = " (";
addAtEnd = ") ";
}
else
{
addAtStart = "";
addAtEnd = "";
}
switch (pc.getType())
{
case (Criteria.OR) :
{
statement.append(" OR " + addAtStart);
statement.append(asSQLStatement(pc, cld));
statement.append(addAtEnd);
break;
}
case (Criteria.AND) :
{
statement.insert(0, "( ");
statement.append(") ");
statement.append(" AND " + addAtStart);
statement.append(asSQLStatement(pc, cld));
statement.append(addAtEnd);
break;
}
}
}
else
{
SelectionCriteria c = (SelectionCriteria) o;
if (statement.length() == 0)
{
statement.append(asSQLClause(c, cld));
}
else
{
statement.insert(0, "(");
statement.append(") ");
statement.append(" AND ");
statement.append(asSQLClause(c, cld));
}
}
} // while
if (statement.length() == 0)
{
return null;
}
return statement.toString();
}
/**
* Answer the SQL-Clause for a SelectionCriteria
* @param c SelectionCriteria
* @param cld ClassDescriptor
*/
protected String asSQLClause(SelectionCriteria c, ClassDescriptor cld)
{
if (c instanceof FieldCriteria)
return toSQLClause((FieldCriteria) c, cld);
if (c instanceof NullCriteria)
return toSQLClause((NullCriteria) c, cld);
if (c instanceof BetweenCriteria)
return toSQLClause((BetweenCriteria) c, cld);
if (c instanceof InCriteria)
return toSQLClause((InCriteria) c, cld);
if (c instanceof SqlCriteria)
return toSQLClause((SqlCriteria) c, cld);
if (c instanceof ExistsCriteria)
return toSQLClause((ExistsCriteria) c, cld);
return toSQLClause(c, cld);
}
private String toSqlClause(Object attributeOrQuery, ClassDescriptor cld)
{
String result;
if (attributeOrQuery instanceof Query)
{
Query q = (Query)attributeOrQuery;
result = getPreparedSelectStatement(q,cld.getRepository().getDescriptorFor(q.getSearchClass()));
}
else
{
result = (String)attributeOrQuery;
}
return result;
}
/**
* Answer the SQL-Clause for a NullCriteria
*
* @param c NullCriteria
* @param cld ClassDescriptor
*/
private String toSQLClause(NullCriteria c, ClassDescriptor cld)
{
String colName = (String)c.getAttribute();
return colName + c.getClause();
}
/**
* Answer the SQL-Clause for a FieldCriteria
*
* @param c FieldCriteria
* @param cld ClassDescriptor
*/
private String toSQLClause(FieldCriteria c, ClassDescriptor cld)
{
String colName = toSqlClause(c.getAttribute(), cld);
return colName + c.getClause() + c.getValue();
}
/**
* Answer the SQL-Clause for a BetweenCriteria
*
* @param c BetweenCriteria
* @param cld ClassDescriptor
*/
private String toSQLClause(BetweenCriteria c, ClassDescriptor cld)
{
String colName = toSqlClause(c.getAttribute(), cld);
return colName + c.getClause() + " ? AND ? ";
}
/**
* Answer the SQL-Clause for an InCriteria
*
* @param c SelectionCriteria
* @param cld ClassDescriptor
*/
private String toSQLClause(InCriteria c, ClassDescriptor cld)
{
StringBuffer buf = new StringBuffer();
Collection values = (Collection) c.getValue();
int size = values.size();
buf.append(c.getAttribute());
buf.append(c.getClause());
buf.append("(");
for (int i = 0; i < size - 1; i++)
{
buf.append("?,");
}
buf.append("?)");
return buf.toString();
}
/**
* Answer the SQL-Clause for a SelectionCriteria
*
* @param c SelectionCriteria
* @param cld ClassDescriptor
*/
private String toSQLClause(SelectionCriteria c, ClassDescriptor cld)
{
String colName = toSqlClause(c.getAttribute(), cld);
return colName + c.getClause() + " ? ";
}
/**
* Answer the SQL-Clause for a SqlCriteria
*
* @param c SqlCriteria
* @param cld ClassDescriptor
*/
private String toSQLClause(SqlCriteria c, ClassDescriptor cld)
{
return c.getClause();
}
/**
* Answer the SQL-Clause for an ExistsCriteria
*
* @param c ExistsCriteria
* @param cld ClassDescriptor
*/
private String toSQLClause(ExistsCriteria c, ClassDescriptor cld)
{
StringBuffer buf = new StringBuffer();
Query subQuery = (Query) c.getValue();
buf.append(c.getClause());
buf.append(" (");
// If it's a proper call
if (cld != null)
{
buf.append(
getPreparedSelectStatement(
subQuery,
cld.getRepository().getDescriptorFor(subQuery.getSearchClass())));
// Otherwise it's most likely a call to toString()
}
else
{
buf.append(subQuery);
}
buf.append(")");
return buf.toString();
}
/**
* generate a prepared DELETE-Statement according to query
* @param query the Query
* @param cld the ClassDescriptor
*/
public String getPreparedDeleteStatement(Query query, ClassDescriptor cld)
{
SqlCacheKey key = new SqlCacheKey(query, cld, SqlCacheKey.TYPE_DELETE);
String result = (String) m_sqlCacheMap.get(key);
if (result == null)
{
SqlStatement sql;
sql = new SqlDeleteByQuery(m_platform, cld, query, logger);
result = sql.getStatement();
m_sqlCacheMap.put(key, result);
if (logger.isDebugEnabled())
{
logger.debug("SQL:" + result);
}
}
return result;
}
/* (non-Javadoc)
* @see org.apache.ojb.broker.accesslayer.sql.SqlGenerator#getPlatform()
*/
public Platform getPlatform()
{
return m_platform;
}
}