package org.jboss.ejb.plugins.cmp.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collections;
import javax.ejb.EJBException;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
import org.jboss.logging.Logger;
import org.jboss.deployment.DeploymentException;
public final class JDBCLoadRelationCommand
{
private final JDBCStoreManager manager;
private final JDBCEntityBridge entity;
private final Logger log;
public JDBCLoadRelationCommand(JDBCStoreManager manager)
{
this.manager = manager;
this.entity = (JDBCEntityBridge) manager.getEntityBridge();
log = Logger.getLogger(
this.getClass().getName() +
"." +
manager.getMetaData().getName());
}
public Collection execute(JDBCCMRFieldBridge cmrField, Object pk)
{
JDBCCMRFieldBridge relatedCMRField = (JDBCCMRFieldBridge) cmrField.getRelatedCMRField();
ReadAheadCache readAheadCache = manager.getReadAheadCache();
ReadAheadCache relatedReadAheadCache = cmrField.getRelatedManager().getReadAheadCache();
ReadAheadCache.EntityReadAheadInfo info = readAheadCache.getEntityReadAheadInfo(pk);
List loadKeys = info.getLoadKeys();
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try
{
boolean[] preloadMask = getPreloadMask(cmrField);
String sql = getSQL(cmrField, preloadMask, loadKeys.size());
if(log.isDebugEnabled())
log.debug("load relation SQL: " + sql);
con = cmrField.getDataSource().getConnection();
ps = con.prepareStatement(sql.toString());
if(entity.getFetchSize() > 0)
{
ps.setFetchSize(entity.getFetchSize());
}
JDBCCMPFieldBridge[] myKeyFields = getMyKeyFields(cmrField);
JDBCCMPFieldBridge[] relatedKeyFields = getRelatedKeyFields(cmrField);
int paramIndex = 1;
for(int i = 0; i < loadKeys.size(); i++)
{
Object key = loadKeys.get(i);
for(int j = 0; j < myKeyFields.length; ++j)
paramIndex = myKeyFields[j].setPrimaryKeyParameters(ps, paramIndex, key);
}
rs = ps.executeQuery();
Map resultsMap = new HashMap(loadKeys.size());
for(int i = 0; i < loadKeys.size(); ++i)
{
resultsMap.put(loadKeys.get(i), new ArrayList());
}
Object[] ref = new Object[1];
while(rs.next())
{
int index = 1;
ref[0] = null;
Object loadedPk = pk;
if(loadKeys.size() > 1)
{
for(int i = 0; i < myKeyFields.length; ++i)
{
index = myKeyFields[i].loadPrimaryKeyResults(rs, index, ref);
if(ref[0] == null)
{
break;
}
}
loadedPk = ref[0];
}
ref[0] = null;
for(int i = 0; i < relatedKeyFields.length; ++i)
{
index = relatedKeyFields[i].loadPrimaryKeyResults(rs, index, ref);
if(ref[0] == null)
{
break;
}
}
Object loadedFk = ref[0];
if(loadedFk != null)
{
List results = (List)resultsMap.get(loadedPk);
results.add(loadedFk);
if(relatedCMRField.isSingleValued())
{
relatedReadAheadCache.addPreloadData(
loadedFk,
relatedCMRField,
Collections.singletonList(loadedPk));
}
if(preloadMask != null)
{
JDBCFieldBridge[] relatedFields = cmrField.getRelatedJDBCEntity().getTableFields();
for(int i = 0; i < relatedFields.length; ++i)
{
if(preloadMask[i])
{
JDBCFieldBridge field = relatedFields[i];
ref[0] = null;
index = field.loadArgumentResults(rs, index, ref);
relatedReadAheadCache.addPreloadData(loadedFk, field, ref[0]);
}
}
}
}
}
JDBCReadAheadMetaData readAhead = relatedCMRField.getReadAhead();
for(Iterator iter = resultsMap.keySet().iterator(); iter.hasNext();)
{
Object key = iter.next();
List results = (List)resultsMap.get(key);
relatedReadAheadCache.addFinderResults(results, readAhead);
if(!key.equals(pk))
{
readAheadCache.addPreloadData(key, cmrField, results);
}
}
return (List)resultsMap.get(pk);
}
catch(EJBException e)
{
throw e;
}
catch(Exception e)
{
throw new EJBException("Load relation failed", e);
}
finally
{
JDBCUtil.safeClose(rs);
JDBCUtil.safeClose(ps);
JDBCUtil.safeClose(con);
}
}
private String getSQL(JDBCCMRFieldBridge cmrField, boolean[] preloadMask, int keyCount) throws DeploymentException
{
JDBCCMPFieldBridge[] myKeyFields = getMyKeyFields(cmrField);
JDBCCMPFieldBridge[] relatedKeyFields = getRelatedKeyFields(cmrField);
String relationTable = getQualifiedRelationTable(cmrField);
JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
String relatedTable = relatedEntity.getQualifiedTableName();
boolean join = ((preloadMask != null) || cmrField.allFkFieldsMappedToPkFields())
&& (relatedKeyFields != relatedEntity.getPrimaryKeyFields());
String relationTableAlias;
String relatedTableAlias;
if(join)
{
relationTableAlias = getRelationTable(cmrField);
relatedTableAlias = (
relatedTable.equals(relationTable) ? getRelationTable(cmrField) + '_' + cmrField.getFieldName() : relatedEntity.getTableName()
);
}
else
{
relationTableAlias = "";
relatedTableAlias = "";
}
JDBCFunctionMappingMetaData selectTemplate = getSelectTemplate(cmrField);
return selectTemplate == null ?
getPlainSQL(
keyCount,
myKeyFields,
relationTableAlias,
relatedKeyFields,
preloadMask,
cmrField,
relatedTableAlias,
relationTable,
join,
relatedTable)
:
getSQLByTemplate(
keyCount,
myKeyFields,
relationTableAlias,
relatedKeyFields,
preloadMask,
cmrField,
relatedTableAlias,
relationTable,
join,
relatedTable,
selectTemplate);
}
private JDBCCMPFieldBridge[] getMyKeyFields(JDBCCMRFieldBridge cmrField)
{
if(cmrField.getRelationMetaData().isTableMappingStyle())
{
return (JDBCCMPFieldBridge[]) cmrField.getTableKeyFields();
}
else if(cmrField.getRelatedCMRField().hasForeignKey())
{
return (JDBCCMPFieldBridge[]) cmrField.getRelatedCMRField().getForeignKeyFields();
}
else
{
return (JDBCCMPFieldBridge[])entity.getPrimaryKeyFields();
}
}
private static JDBCCMPFieldBridge[] getRelatedKeyFields(JDBCCMRFieldBridge cmrField)
{
if(cmrField.getRelationMetaData().isTableMappingStyle())
{
return (JDBCCMPFieldBridge[]) cmrField.getRelatedCMRField().getTableKeyFields();
}
else if(cmrField.getRelatedCMRField().hasForeignKey())
{
return (JDBCCMPFieldBridge[])cmrField.getRelatedJDBCEntity().getPrimaryKeyFields();
}
else
{
return (JDBCCMPFieldBridge[]) cmrField.getForeignKeyFields();
}
}
private static boolean[] getPreloadMask(JDBCCMRFieldBridge cmrField)
{
boolean[] preloadMask = null;
if(cmrField.getReadAhead().isOnFind())
{
JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
String eagerLoadGroup = cmrField.getReadAhead().getEagerLoadGroup();
preloadMask = relatedEntity.getLoadGroupMask(eagerLoadGroup);
}
return preloadMask;
}
private String getQualifiedRelationTable(JDBCCMRFieldBridge cmrField)
{
if(cmrField.getRelationMetaData().isTableMappingStyle())
{
return cmrField.getQualifiedTableName();
}
else if(cmrField.getRelatedCMRField().hasForeignKey())
{
return cmrField.getRelatedJDBCEntity().getQualifiedTableName();
}
else
{
return entity.getQualifiedTableName();
}
}
private String getRelationTable(JDBCCMRFieldBridge cmrField)
{
if(cmrField.getRelationMetaData().isTableMappingStyle())
{
return cmrField.getTableName();
}
else if(cmrField.getRelatedCMRField().hasForeignKey())
{
return cmrField.getRelatedJDBCEntity().getTableName();
}
else
{
return entity.getTableName();
}
}
private JDBCFunctionMappingMetaData getSelectTemplate(JDBCCMRFieldBridge cmrField) throws DeploymentException
{
JDBCFunctionMappingMetaData selectTemplate = null;
if(cmrField.getRelationMetaData().isTableMappingStyle())
{
if(cmrField.getRelationMetaData().hasRowLocking())
{
selectTemplate =
cmrField.getRelationMetaData().getTypeMapping().getRowLockingTemplate();
if(selectTemplate == null)
{
throw new IllegalStateException(
"row-locking is not allowed for this type of datastore");
}
}
}
else if(cmrField.getRelatedCMRField().hasForeignKey())
{
if(cmrField.getRelatedJDBCEntity().getMetaData().hasRowLocking())
{
selectTemplate =
cmrField.getRelatedJDBCEntity().getMetaData().getTypeMapping().getRowLockingTemplate();
if(selectTemplate == null)
{
throw new IllegalStateException(
"row-locking is not allowed for this type of datastore");
}
}
}
else
{
if(entity.getMetaData().hasRowLocking())
{
selectTemplate = entity.getMetaData().getTypeMapping().getRowLockingTemplate();
if(selectTemplate == null)
{
throw new IllegalStateException(
"row-locking is not allowed for this type of datastore");
}
}
}
return selectTemplate;
}
private static String getPlainSQL(int keyCount,
JDBCCMPFieldBridge[] myKeyFields,
String relationTableAlias,
JDBCCMPFieldBridge[] relatedKeyFields,
boolean[] preloadMask,
JDBCCMRFieldBridge cmrField,
String relatedTableAlias,
String relationTable,
boolean join,
String relatedTable)
{
StringBuffer sql = new StringBuffer(400);
sql.append(SQLUtil.SELECT);
if(keyCount > 1)
{
SQLUtil.getColumnNamesClause(myKeyFields, relationTableAlias, sql)
.append(SQLUtil.COMMA);
}
SQLUtil.getColumnNamesClause(relatedKeyFields, relationTableAlias, sql);
if(preloadMask != null)
{
SQLUtil.appendColumnNamesClause(
cmrField.getRelatedJDBCEntity().getTableFields(),
preloadMask,
relatedTableAlias,
sql);
}
sql.append(SQLUtil.FROM).append(relationTable);
if(join)
{
sql.append(' ')
.append(relationTableAlias)
.append(SQLUtil.COMMA)
.append(relatedTable)
.append(' ')
.append(relatedTableAlias);
}
sql.append(SQLUtil.WHERE);
if(join)
{
sql.append('(');
SQLUtil.getJoinClause(
relatedKeyFields,
relationTableAlias,
cmrField.getRelatedJDBCEntity().getPrimaryKeyFields(),
relatedTableAlias,
sql)
.append(')')
.append(SQLUtil.AND)
.append('(');
}
String pkWhere = SQLUtil.getWhereClause(myKeyFields, relationTableAlias, new StringBuffer(50)).toString();
for(int i = 0; i < keyCount; i++)
{
if(i > 0)
sql.append(SQLUtil.OR);
sql.append('(').append(pkWhere).append(')');
}
if(join)
sql.append(')');
return sql.toString();
}
private static String getSQLByTemplate(int keyCount,
JDBCCMPFieldBridge[] myKeyFields,
String relationTableAlias,
JDBCCMPFieldBridge[] relatedKeyFields,
boolean[] preloadMask,
JDBCCMRFieldBridge cmrField,
String relatedTableAlias,
String relationTable,
boolean join,
String relatedTable,
JDBCFunctionMappingMetaData selectTemplate)
{
StringBuffer columnNamesClause = new StringBuffer(100);
if(keyCount > 1)
{
SQLUtil.getColumnNamesClause(myKeyFields, relationTableAlias, columnNamesClause)
.append(SQLUtil.COMMA);
}
SQLUtil.getColumnNamesClause(relatedKeyFields, relationTableAlias, columnNamesClause);
if(preloadMask != null)
{
SQLUtil.appendColumnNamesClause(
cmrField.getRelatedJDBCEntity().getTableFields(),
preloadMask,
relatedTableAlias,
columnNamesClause);
}
StringBuffer fromClause = new StringBuffer(100);
fromClause.append(relationTable);
if(join)
{
fromClause.append(' ')
.append(relationTableAlias)
.append(SQLUtil.COMMA)
.append(relatedTable)
.append(' ')
.append(relatedTableAlias);
}
StringBuffer whereClause = new StringBuffer(150);
if(join)
{
whereClause.append('(');
SQLUtil.getJoinClause(
relatedKeyFields,
relationTableAlias,
cmrField.getRelatedJDBCEntity().getPrimaryKeyFields(),
relatedTableAlias,
whereClause)
.append(')')
.append(SQLUtil.AND)
.append('(');
}
String pkWhere = SQLUtil.getWhereClause(myKeyFields, relationTableAlias, new StringBuffer(50)).toString();
for(int i = 0; i < keyCount; i++)
{
if(i > 0)
{
whereClause.append(SQLUtil.OR);
}
whereClause.append('(').append(pkWhere).append(')');
}
if(join)
{
whereClause.append(')');
}
String[] args = new String[]{
columnNamesClause.toString(),
fromClause.toString(),
whereClause.toString(),
null };
return selectTemplate.getFunctionSql(args, new StringBuffer(500)).toString();
}
}