Coverage Summary for Class: SessionHelper (net.sf.persism)
| Class |
Method, %
|
Branch, %
|
Line, %
|
| SessionHelper |
100%
(27/27)
|
86.8%
(236/272)
|
97.1%
(367/378)
|
| SessionHelper$1 |
100%
(1/1)
|
100%
(2/2)
|
| Total |
100%
(28/28)
|
86.8%
(236/272)
|
97.1%
(369/380)
|
package net.sf.persism;
import net.sf.persism.annotations.Join;
import net.sf.persism.annotations.NotTable;
import net.sf.persism.annotations.View;
import java.math.BigDecimal;
import java.sql.*;
import java.util.*;
import java.util.stream.Collectors;
import static net.sf.persism.SQL.where;
// Non-public code Session uses.
final class SessionHelper {
// leave this using the Session.class for logging
private static final Log log = Log.getLogger(Session.class);
private static final Log blog = Log.getLogger("net.sf.persism.Benchmarks");
private static final Log sqllog = Log.getLogger("net.sf.persism.SQL");
private final Session session;
public SessionHelper(Session session) {
this.session = session;
}
JDBCResult executeQuery(Class<?> objectClass, SQL sql, Parameters parameters) throws SQLException {
JDBCResult result = new JDBCResult();
String sqlQuery = sql.sql;
if (parameters.areNamed) {
if (sql.type == SQL.SQLType.StoredProc) {
//log.warnNoDuplicates(Message.NamedParametersUsedWithStoredProc.message(sql.sql));
throw new PersismException(Message.NamedParametersUsedWithStoredProc.message(sql.sql));
}
char delim = '@';
Map<String, List<Integer>> paramMap = new HashMap<>();
sqlQuery = parseParameters(delim, sqlQuery, paramMap);
parameters.setParameterMap(paramMap);
} else if (parameters.areKeys) {
// convert parameters - usually it's the UUID type that may need a conversion to byte[16]
// Probably we don't want to auto-convert here since it's inconsistent. DO WE? YES.
List<String> keys = session.metaData.getPrimaryKeys(objectClass, session.connection);
if (keys.size() == 1) {
Map<String, ColumnInfo> columns = session.metaData.getColumns(objectClass, session.connection);
String key = keys.get(0);
ColumnInfo columnInfo = columns.get(key);
for (int j = 0; j < parameters.size(); j++) {
if (parameters.get(j) != null) {
parameters.set(j, session.converter.convert(parameters.get(j), columnInfo.columnType.getJavaType(), columnInfo.columnName));
}
}
}
}
if (sql.type == SQL.SQLType.Where) {
if (objectClass.getAnnotation(NotTable.class) != null) {
throw new PersismException(Message.WhereNotSupportedForNotTableQueries.message());
}
sqlQuery = session.metaData.getSelectStatement(objectClass, session.connection) + parsePropertyNames(sqlQuery, objectClass, session.connection);
sql.processedSQL = sqlQuery;
}
if (sql.limit > 0) {
sqlQuery = addLimitToSQL(sqlQuery, sql.limit, session.metaData.getConnectionType());
sql.processedSQL = sqlQuery;
}
executeSelect(result, sqlQuery, parameters.toArray());
return result;
}
private static String addLimitToSQL(String sqlQuery, int limit, ConnectionType connectionType) {
switch (connectionType) {
// for other (unknown type we'll just guess at 2008 standard)
case Oracle, Derby, Other -> sqlQuery += " FETCH FIRST " + limit + " ROWS ONLY";
case MSSQL, JTDS, UCanAccess -> sqlQuery = "SELECT TOP " + limit + " " + sqlQuery.substring(7);
case MySQL, PostgreSQL, H2, SQLite, HSQLDB, Informix -> sqlQuery += " LIMIT " + limit;
case Firebird -> sqlQuery = "SELECT FIRST " + limit + " " + sqlQuery.substring(7);
}
return sqlQuery;
}
// this method should only be used by query or fetch
void executeSelect(JDBCResult result, String sql, Object... parameters) throws SQLException {
long now = System.currentTimeMillis();
if (sqllog.isDebugEnabled()) {
sqllog.debug("%s params: %s", sql, Arrays.asList(parameters));
}
try {
if (isSelect(sql)) {
if (session.metaData.getConnectionType() == ConnectionType.Firebird) {
// https://stackoverflow.com/questions/935511/how-can-i-avoid-resultset-is-closed-exception-in-java
result.st = session.connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
} else {
result.st = session.connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
PreparedStatement pst = (PreparedStatement) result.st;
setParameters(pst, parameters);
result.rs = pst.executeQuery();
} else {
if (!sql.trim().toLowerCase().startsWith("{call")) {
sql = "{call " + sql + "} ";
}
// Don't need If Firebird here. Firebird would call a selectable stored proc with SELECT anyway
result.st = session.connection.prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
CallableStatement cst = (CallableStatement) result.st;
setParameters(cst, parameters);
result.rs = cst.executeQuery();
}
} catch (SQLException e) {
throw new SQLException(e.getMessage() + " SQL: " + sql + " params: " + Arrays.asList(parameters), e);
} finally {
if (blog.isDebugEnabled()) {
blog.debug("exec time: " + (System.currentTimeMillis() - now) + "ms " + sql + " params: " + Arrays.asList(parameters));
}
}
}
// For unit tests only for now.
void execute(String sql, Object... parameters) {
log.debug("execute: %s params: %s", sql, Arrays.asList(parameters));
Statement st = null;
try {
if (parameters.length == 0) {
st = session.connection.createStatement();
st.execute(sql);
} else {
st = session.connection.prepareStatement(sql);
PreparedStatement pst = (PreparedStatement) st;
setParameters(pst, parameters);
pst.execute();
}
} catch (Exception e) {
Util.rollback(session.connection);
throw new PersismException(e.getMessage(), e);
} finally {
Util.cleanup(st, null);
}
}
/**
* Original from Adam Crume, JavaWorld.com, 04/03/07
* https://www.infoworld.com/article/2077706/named-parameters-for-preparedstatement.html
* https://archive.ph/au5XM and https://archive.ph/4OOze
*
* @param sql query to parse
* @param paramMap map to hold parameter-index mappings
* @return the parsed query
*/
String parseParameters(char delim, String sql, Map<String, List<Integer>> paramMap) {
log.debug("parseParameters using " + delim);
int length = sql.length();
StringBuilder parsedQuery = new StringBuilder(length);
Set<Character> startDelims = new HashSet<>(4);
startDelims.add('"');
String sd = session.metaData.getConnectionType().getKeywordStartDelimiter();
String ed = session.metaData.getConnectionType().getKeywordEndDelimiter();
if (Util.isNotEmpty(sd)) {
startDelims.add(sd.charAt(0));
}
Set<Character> endDelims = new HashSet<>(4);
endDelims.add('"');
if (Util.isNotEmpty(ed)) {
endDelims.add(ed.charAt(0));
}
boolean inDelimiter = false;
int index = 1;
for (int i = 0; i < length; i++) {
char c = sql.charAt(i);
if (inDelimiter) {
if (endDelims.contains(c)) {
inDelimiter = false;
}
} else if (startDelims.contains(c)) {
inDelimiter = true;
} else if (c == delim && i + 1 < length && Character.isJavaIdentifierStart(sql.charAt(i + 1))) {
int j = i + 2;
while (j < length && Character.isJavaIdentifierPart(sql.charAt(j))) {
j++;
}
String name = sql.substring(i + 1, j);
c = '?'; // replace the parameter with a question mark
i += name.length(); // skip past the end of the parameter
List<Integer> indexList = paramMap.get(name);
if (indexList == null) {
indexList = new LinkedList<>();
paramMap.put(name, indexList);
}
indexList.add(index);
index++;
}
parsedQuery.append(c);
}
return parsedQuery.toString();
}
String parsePropertyNames(String sql, Class<?> objectClass, Connection connection) {
log.debug("parsePropertyNames using : with SQL: %s", sql);
if (session.metaData.whereClauses.containsKey(objectClass) && session.metaData.whereClauses.get(objectClass).containsKey(sql)) {
return session.metaData.whereClauses.get(objectClass).get(sql);
}
return determineWhereClause(sql, objectClass, connection);
}
private synchronized String determineWhereClause(String sql, Class<?> objectClass, Connection connection) {
if (session.metaData.whereClauses.containsKey(objectClass) && session.metaData.whereClauses.get(objectClass).containsKey(sql)) {
return session.metaData.whereClauses.get(objectClass).get(sql);
}
int length = sql.length();
StringBuilder parsedQuery = new StringBuilder(length);
String sd = session.metaData.getConnectionType().getKeywordStartDelimiter();
String ed = session.metaData.getConnectionType().getKeywordEndDelimiter();
Set<Character> startDelims = new HashSet<>(4);
startDelims.add('"');
startDelims.add('\'');
if (Util.isNotEmpty(sd)) {
startDelims.add(sd.charAt(0));
}
Set<Character> endDelims = new HashSet<>(4);
endDelims.add('"');
endDelims.add('\'');
if (Util.isNotEmpty(session.metaData.getConnectionType().getKeywordEndDelimiter())) {
endDelims.add(ed.charAt(0));
}
Map<String, PropertyInfo> properties = session.metaData.getTableColumnsPropertyInfo(objectClass, connection);
Set<String> propertiesNotFound = new LinkedHashSet<>();
boolean inDelimiter = false;
boolean appendChar;
for (int i = 0; i < length; i++) {
appendChar = true;
char c = sql.charAt(i);
if (inDelimiter) {
if (endDelims.contains(c)) {
inDelimiter = false;
}
} else if (startDelims.contains(c)) {
inDelimiter = true;
} else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(sql.charAt(i + 1))) {
int j = i + 2;
while (j < length && Character.isJavaIdentifierPart(sql.charAt(j))) {
j++;
}
String name = sql.substring(i + 1, j);
log.debug("parsePropertyNames property name: %s", name);
i += name.length(); // skip past the end if the property name
boolean found = false;
for (String col : properties.keySet()) {
PropertyInfo propertyInfo = properties.get(col);
// ignore case?
if (propertyInfo.propertyName.equals(name)) {
parsedQuery.append(sd).append(col).append(ed);
appendChar = false;
found = true;
break;
}
}
if (!found) {
propertiesNotFound.add(name);
}
}
if (appendChar) {
parsedQuery.append(c);
}
}
if (!propertiesNotFound.isEmpty()) {
throw new PersismException(Message.QueryPropertyNamesMissingOrNotFound.message(propertiesNotFound, sql));
}
String parsedSql = " " + parsedQuery;
log.debug("parsePropertyNames SQL: %s", parsedSql);
session.metaData.whereClauses.putIfAbsent(objectClass, new HashMap<>());
session.metaData.whereClauses.get(objectClass).put(sql, parsedSql);
return parsedSql;
}
void checkIfOkForWriteOperation(Class<?> objectClass, String operation) {
if (objectClass.getAnnotation(View.class) != null) {
throw new PersismException(Message.OperationNotSupportedForView.message(objectClass, operation));
}
if (objectClass.getAnnotation(NotTable.class) != null) {
throw new PersismException(Message.OperationNotSupportedForNotTableQuery.message(objectClass, operation));
}
if (JavaType.getType(objectClass) != null) {
throw new PersismException(Message.OperationNotSupportedForJavaType.message(objectClass, operation));
}
}
Object getTypedValueReturnedFromGeneratedKeys(Class<?> objectClass, ResultSet rs) throws SQLException {
Object value;
JavaType type = JavaType.getType(objectClass);
if (type == null) {
log.warn(Message.UnknownTypeForPrimaryGeneratedKey.message(objectClass));
return rs.getObject(1);
}
value = switch (type) {
case integerType, IntegerType -> rs.getInt(1);
case longType, LongType -> rs.getLong(1);
default -> rs.getObject(1);
};
return value;
}
void setParameters(PreparedStatement st, Object[] parameters) throws SQLException {
if (log.isDebugEnabled()) {
log.debug("setParameters PARAMS: %s", Arrays.asList(parameters));
}
Object value; // used for conversions
int n = 1;
for (Object param : parameters) {
if (param != null) {
JavaType paramType = JavaType.getType(param.getClass());
if (paramType == null) {
log.warn(Message.UnknownTypeInSetParameters.message(param.getClass()));
paramType = JavaType.ObjectType;
}
switch (paramType) {
case booleanType:
case BooleanType:
st.setBoolean(n, (Boolean) param);
break;
case byteType:
case ByteType:
st.setByte(n, (Byte) param);
break;
case shortType:
case ShortType:
st.setShort(n, (Short) param);
break;
case integerType:
case IntegerType:
st.setInt(n, (Integer) param);
break;
case longType:
case LongType:
st.setLong(n, (Long) param);
break;
case floatType:
case FloatType:
st.setFloat(n, (Float) param);
break;
case doubleType:
case DoubleType:
st.setDouble(n, (Double) param);
break;
case BigDecimalType:
st.setBigDecimal(n, (BigDecimal) param);
break;
case BigIntegerType:
st.setString(n, "" + param);
break;
case StringType:
st.setString(n, (String) param);
break;
case characterType:
case CharacterType:
st.setObject(n, "" + param);
break;
case SQLDateType:
st.setDate(n, (java.sql.Date) param);
break;
case TimeType:
st.setTime(n, (Time) param);
break;
case TimestampType:
st.setTimestamp(n, (Timestamp) param);
break;
case LocalTimeType:
value = session.converter.convert(param, Time.class, "Parameter " + n);
st.setObject(n, value);
break;
case UtilDateType:
case LocalDateType:
case LocalDateTimeType:
value = session.converter.convert(param, Timestamp.class, "Parameter " + n);
st.setObject(n, value);
break;
case OffsetDateTimeType:
case ZonedDateTimeType:
case InstantType:
log.warn(Message.UnSupportedTypeInSetParameters.message(paramType));
st.setObject(n, "" + param);
break;
case byteArrayType:
case ByteArrayType:
// Blob maps to byte array
st.setBytes(n, (byte[]) param);
break;
case ClobType:
case BlobType:
// Clob is converted to String Blob is converted to byte array
// so this should not occur unless they were passed in by the user.
// We are most probably about to fail here.
log.warn(Message.ParametersDoNotUseClobOrBlob.message(), new Throwable());
st.setObject(n, param);
break;
case EnumType:
if (session.metaData.getConnectionType() == ConnectionType.PostgreSQL) {
st.setObject(n, param.toString(), Types.OTHER);
} else {
st.setString(n, param.toString());
}
break;
case UUIDType:
if (session.metaData.getConnectionType() == ConnectionType.PostgreSQL) {
st.setObject(n, param);
} else {
st.setString(n, param.toString());
}
break;
default:
// Usually SQLite with util.date - setObject works
// Also if it's a custom non-standard type.
log.info("setParameters using setObject on parameter: " + n + " for " + param.getClass());
st.setObject(n, param);
}
} else {
// param is null
if (session.metaData.getConnectionType() == ConnectionType.UCanAccess) {
st.setNull(n, Types.OTHER);
} else {
st.setObject(n, null);
}
}
n++;
}
}
// todo we really need a way to detect infinite loops and FAIL FAST
// parent is a POJO or a List of POJOs
void handleJoins(Object parent, Class<?> parentClass, SQL parentSql, Parameters parentParams, boolean isRoot) {
// maybe we could add a check for fetch after insert. In those cases there's no need to query for child lists
// BUT we do need query for child SINGLES AND IT'S POSSIBLE THAT CHILD RECORDS GET INSERTED 1st!
// NOT really important. At most 1 extra query per type will be run (and no child queries after that)
log.debug("handleJoins: Root?" + isRoot + " " + parent.getClass() + " parentSql: " + parentSql);
List<PropertyInfo> joinProperties = MetaData.getPropertyInfo(parentClass).stream().filter(PropertyInfo::isJoin).toList();
for (PropertyInfo joinProperty : joinProperties) {
Join joinAnnotation = (Join) joinProperty.getAnnotation(Join.class);
JoinInfo joinInfo = JoinInfo.getInstance(joinAnnotation, joinProperty, parent, parentClass);
if (joinInfo.parentIsAQuery()) {
// We expect this method not to be called if the result query has 0 rows.
assert !((Collection<?>) parent).isEmpty();
}
String sql = parentSql.toString();
String parentWhere;
if (sql.toUpperCase().contains(" WHERE ")) {
parentWhere = sql.substring(sql.toUpperCase().indexOf(" WHERE ") + 7);
} else {
parentWhere = "";
}
// TODO what if root is a general query - not a table. Blowing up.... fail with no primary key
// todo limit > 0 with fetch? makes no sense
if (isRoot && (parentSql.limit > 0 || !joinInfo.parentIsAQuery())) {
// log.warn("original where: " + parentWhere + " " + parentParams);
// replace parent where with IN (ID, ID, ID) - replace parameters with keys
if (joinInfo.parentIsAQuery()) {
Collection<?> list = (Collection<?>) parent;
parentWhere = session.metaData.getPrimaryInClause(parentClass, list.size(), session.connection);
List<String> keys = session.metaData.getPrimaryKeys(parentClass, session.connection);
parentParams = new Parameters();
for (String key : keys) {
for (Object pojo : list) {
PropertyInfo prop = session.getMetaData().getTableColumnsPropertyInfo(parentClass, session.connection).get(key);
Object value = prop.getValue(pojo);
parentParams.add(value);
}
}
} else {
parentWhere = session.metaData.getWhereClause(parentClass, session.connection);
List<String> keys = session.metaData.getPrimaryKeys(parentClass, session.connection);
parentParams = new Parameters();
for (String key : keys) {
PropertyInfo prop = session.getMetaData().getTableColumnsPropertyInfo(parentClass, session.connection).get(key);
Object value = prop.getValue(parent);
parentParams.add(value);
}
}
}
String whereClause;
whereClause = getChildWhereClause(joinInfo, parentWhere);
log.debug("whereClause: " + whereClause);
// join to a collection
if (Collection.class.isAssignableFrom(joinProperty.field.getType())) {
// query
List<?> childList = childQuery(joinInfo.childClass(), where(whereClause), parentParams);
if (joinInfo.parentIsAQuery()) {
// many to many
List<?> parentList = (List<?>) parent;
stitch(joinInfo, parentList, childList);
} else {
// one to many
assignJoinedList(joinProperty, parent, childList);
}
} else { // join to single object
if (joinInfo.parentIsAQuery()) {
// many to one
List<?> childList = childQuery(joinInfo.childClass(), where(whereClause), parentParams);
stitch(joinInfo.swapParentAndChild(), childList, (List<?>) parent);
} else {
// one to one
Object child = childFetch(joinInfo.childClass(), where(whereClause), parentParams);
if (child != null) {
joinProperty.setValue(parent, child);
}
}
}
}
}
// to distinguish our internal call vs public call
private Object childFetch(Class<?> aClass, SQL where, Parameters params) {
return session.fetch(aClass, where, params, false);
}
// to distinguish our internal call vs public call
private List<?> childQuery(Class<?> aClass, SQL where, Parameters params) {
return session.query(aClass, where, params, false);
}
// called with many to many or many to one
private void stitch(JoinInfo joinInfo, List<?> parentList, List<?> childList) {
blog.debug("STITCH " + joinInfo);
if (childList.isEmpty()) {
return;
}
long now = System.currentTimeMillis();
Map<Object, Object> parentMap;
if (joinInfo.parentProperties().size() == 1 && joinInfo.childProperties().size() == 1) {
PropertyInfo parentPropertyInfo = joinInfo.parentProperties().get(0);
PropertyInfo childPropertyInfo = joinInfo.childProperties().get(0);
// https://stackoverflow.com/questions/32312876/ignore-duplicates-when-producing-map-using-streams
parentMap = parentList.stream().collect(Collectors.
toMap(key -> {
Object value = parentPropertyInfo.getValue(key);
if (!joinInfo.caseSensitive() && value instanceof String s) {
return s.toUpperCase();
}
return value;
}, o -> o, (o1, o2) -> o1));
for (Object child : childList) {
Object childValue = childPropertyInfo.getValue(child);
if (childValue != null) {
Object parent = parentMap.get(childValue);
if (parent == null) {
log.warnNoDuplicates("parent not found: " + childValue + " : " + joinInfo + "DAO: " + child); // Should not usually occur. Why would we not find a parent?
} else {
setPropertyFromJoinInfo(joinInfo, parent, child);
}
}
}
} else {
parentMap = parentList.stream().collect(Collectors.
toMap(key -> {
List<Object> values = new ArrayList<>();
for (int j = 0; j < joinInfo.parentProperties().size(); j++) {
values.add(joinInfo.parentProperties().get(j).getValue(key));
}
return new KeyBox(joinInfo.caseSensitive(), values.toArray());
}, o -> o, (o1, o2) -> o1));
for (Object child : childList) {
List<Object> values = new ArrayList<>();
for (int j = 0; j < joinInfo.childProperties().size(); j++) {
values.add(joinInfo.childProperties().get(j).getValue(child));
}
KeyBox keyBox = new KeyBox(joinInfo.caseSensitive(), values.toArray());
if (!keyBox.isAllNull()) {
Object parent = parentMap.get(keyBox);
if (parent == null) {
log.warnNoDuplicates("parent not found: " + keyBox + " : " + joinInfo + "DAO: " + child); // Should not usually occur. Why would we not find a parent?
} else {
setPropertyFromJoinInfo(joinInfo, parent, child);
}
}
}
}
blog.debug("stitch Time: " + (System.currentTimeMillis() - now));
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void setPropertyFromJoinInfo(JoinInfo joinInfo, Object parent, Object child) {
if (Collection.class.isAssignableFrom(joinInfo.joinProperty().field.getType())) {
var list = (Collection) joinInfo.joinProperty().getValue(parent);
if (list == null) {
throw new PersismException(Message.CannotNotJoinToNullProperty.message(joinInfo.joinProperty().propertyName));
}
list.add(child);
} else {
assert joinInfo.reversed(); // many to 1 which is reversed.
joinInfo.joinProperty().setValue(child, parent);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void assignJoinedList(PropertyInfo joinProperty, Object parentObject, List list) {
// no null test - the object should have some List initialized.
Collection joinTo = (Collection) joinProperty.getValue(parentObject);
if (joinTo == null) {
throw new PersismException(Message.CannotNotJoinToNullProperty.message(joinProperty.propertyName));
}
joinTo.addAll(list);
}
private String getChildWhereClause(JoinInfo joinInfo, String parentWhere) {
if (session.metaData.childWhereClauses.containsKey(joinInfo) && session.metaData.childWhereClauses.get(joinInfo).containsKey(parentWhere)) {
return session.metaData.childWhereClauses.get(joinInfo).get(parentWhere);
}
return determineChildWhereClause(joinInfo, parentWhere);
}
private synchronized String determineChildWhereClause(JoinInfo joinInfo, String parentWhere) {
if (session.metaData.childWhereClauses.containsKey(joinInfo) && session.metaData.childWhereClauses.get(joinInfo).containsKey(parentWhere)) {
return session.metaData.childWhereClauses.get(joinInfo).get(parentWhere);
}
StringBuilder where = new StringBuilder();
String sd = session.metaData.getConnectionType().getKeywordStartDelimiter();
String ed = session.metaData.getConnectionType().getKeywordEndDelimiter();
TableInfo parentTable = session.metaData.getTableInfo(joinInfo.parentClass());
TableInfo childTable = session.metaData.getTableInfo(joinInfo.childClass());
Map<String, PropertyInfo> parentProperties = session.metaData.getTableColumnsPropertyInfo(joinInfo.parentClass(), session.connection);
Map<String, PropertyInfo> childProperties = session.metaData.getTableColumnsPropertyInfo(joinInfo.childClass(), session.connection);
String sep = "";
int n = parentWhere.toUpperCase().indexOf("ORDER BY");
if (n > -1) {
parentWhere = parentWhere.substring(0, n);
}
// Issue #45
// ensure parentWhere has () to prevent bad results if it has OR or similar because this gets appended with AND parentId = childId in the sub selects
if (!parentWhere.trim().isEmpty()) {
parentWhere = "(" + parentWhere + ")";
}
String parentAlias = "";
if (parentTable.equals(childTable)) {
// for self join we need an alias todo verify if we can get duplicate aliases!
parentAlias = parentTable.name().substring(0, 1).toUpperCase();
}
where.append("EXISTS (SELECT ");
for (int j = 0; j < joinInfo.parentPropertyNames().length; j++) {
String parentColumnName = sd + getColumnName(joinInfo.parentPropertyNames()[j], parentProperties) + ed;
where.append(sep).append(parentColumnName);
sep = ",";
}
where.append(" FROM ");
where.append(parentTable);
if (Util.isNotEmpty(parentAlias)) {
where.append(" ").append(parentAlias);
}
where.append(" WHERE ").append(parentWhere).append(" ");
sep = Util.isEmpty(parentWhere) ? "" : " AND ";
for (int j = 0; j < joinInfo.parentPropertyNames().length; j++) {
String parentColumnName = sd + getColumnName(joinInfo.parentPropertyNames()[j], parentProperties) + ed;
String childColumnName = sd + getColumnName(joinInfo.childPropertyNames()[j], childProperties) + ed;
if (Util.isNotEmpty(parentAlias)) {
where.append(sep).append(parentAlias);
} else {
where.append(sep).append(parentTable);
}
where.append(".").append(parentColumnName).append(" = ").
append(childTable).append(".").append(childColumnName);
sep = " AND ";
}
where.append(") ");
String sql = where.toString();
session.metaData.childWhereClauses.putIfAbsent(joinInfo, new HashMap<>());
session.metaData.childWhereClauses.get(joinInfo).put(parentWhere, sql);
if (log.isDebugEnabled()) {
log.debug("determineChildWhereClause: %s", sql);
}
return sql;
}
private String getColumnName(String propertyName, Map<String, PropertyInfo> properties) {
for (String key : properties.keySet()) {
if (properties.get(key).propertyName.equals(propertyName)) {
return key;
}
}
return null;
}
boolean isSelect(String sql) {
assert sql != null;
return sql.trim().substring(0, 6).equalsIgnoreCase("select");
}
<T> void checkIfStoredProcOrSQL(Class<T> objectClass, SQL sql) {
boolean startsWithSelect = isSelect(sql.sql);
if (sql.type == SQL.SQLType.Select) {
if (!startsWithSelect) {
log.warnNoDuplicates(Message.InappropriateMethodUsedForSQLTypeInstance.message(objectClass, "proc()", "an SQL query", "sql()"));
}
} else {
if (startsWithSelect) {
log.warnNoDuplicates(Message.InappropriateMethodUsedForSQLTypeInstance.message(objectClass, "sql()", "a stored proc", "proc()"));
}
}
}
}