package io.ebeaninternal.server.deploy; import io.ebean.Transaction; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.api.SpiSqlUpdate; import io.ebeaninternal.dbmigration.model.visitor.BaseTablePropertyVisitor; import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing; import io.ebeaninternal.server.core.DefaultSqlUpdate; import java.util.List; class BeanPropertyAssocManySqlHelp { private final BeanPropertyAssocMany many; private final ExportedProperty[] exportedProperties; private final boolean hasJoinTable; private final BeanDescriptor descriptor; private final String exportedPropertyBindProto; private final String deleteByParentIdSql; private final String deleteByParentIdInSql; private final String elementCollectionInsertSql; BeanPropertyAssocManySqlHelp(BeanPropertyAssocMany many, ExportedProperty[] exportedProperties) { this.many = many; this.exportedProperties = exportedProperties; this.hasJoinTable = many.hasJoinTable(); this.descriptor = many.getBeanDescriptor(); this.exportedPropertyBindProto = deriveExportedPropertyBindProto(); String delStmt; if (hasJoinTable) { delStmt = "delete from " + many.inverseJoin.getTable() + " where "; } else { delStmt = "delete from " + many.targetTable() + " where "; } deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, ""); deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, ""); if (many.isElementCollection()) { elementCollectionInsertSql = elementCollectionInsert(); } else { elementCollectionInsertSql = null; } } private String elementCollectionInsert() { StringBuilder sb = new StringBuilder(200); sb.append("insert into ").append(many.targetTable()).append(" ("); append(sb); Cols cols = new Cols(sb); VisitAllUsing.visitOne(many.targetDescriptor, cols); sb.append(") values ("); appendBind(sb, exportedProperties.length, true); appendBind(sb, cols.colCount, false); sb.append(")"); return sb.toString(); } SpiSqlUpdate insertElementCollection() { return new DefaultSqlUpdate(elementCollectionInsertSql); } private static class Cols extends BaseTablePropertyVisitor { int colCount; private final StringBuilder sb; private Cols(StringBuilder sb) { this.sb = sb; } @Override public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne embedded) { sb.append(",").append(p.getDbColumn()); colCount++; } @Override public void visitOneImported(BeanPropertyAssocOne p) { } @Override public void visitScalar(BeanProperty p) { sb.append(",").append(p.getDbColumn()); colCount++; } @Override public void visitEnd() { } } String lazyFetchOrderBy(String fetchOrderBy) { // derive lazyFetchOrderBy StringBuilder sb = new StringBuilder(50); for (int i = 0; i < exportedProperties.length; i++) { if (i > 0) { sb.append(", "); } // these fk columns are either on the intersection (int_) or base table (t0) String fkTableAlias = hasJoinTable ? "int_" : "t0"; sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn()); } sb.append(", ").append(fetchOrderBy); return sb.toString().trim(); } /** * Add a where clause to the query for a given list of parent Id's. */ void addWhereParentIdIn(SpiQuery query, List parentIds) { String tableAlias = hasJoinTable ? "int_." : "t0."; if (hasJoinTable) { query.setM2MIncludeJoin(many.inverseJoin); } String rawWhere = deriveWhereParentIdSql(true, tableAlias); String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere); many.bindParentIdsIn(expr, parentIds, query); } List findIdsByParentId(Object parentId, Transaction t, List excludeDetailIds, boolean hard) { String rawWhere = deriveWhereParentIdSql(false, ""); SpiEbeanServer server = descriptor.getEbeanServer(); SpiQuery q = many.newQuery(server); many.bindParentIdEq(rawWhere, parentId, q); if (hard) { q.setIncludeSoftDeletes(); } if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { q.where().not(q.getExpressionFactory().idIn(excludeDetailIds)); } return server.findIds(q, t); } List findIdsByParentIdList(List parentIds, Transaction t, List excludeDetailIds, boolean hard) { String rawWhere = deriveWhereParentIdSql(true, ""); String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto); String expr = rawWhere + inClause; SpiEbeanServer server = descriptor.getEbeanServer(); SpiQuery q = many.newQuery(server); //Query q = server.find(propertyType); many.bindParentIdsIn(expr, parentIds, q); if (hard) { q.setIncludeSoftDeletes(); } if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { q.where().not(q.getExpressionFactory().idIn(excludeDetailIds)); } return server.findIds(q, t); } SpiSqlUpdate deleteByParentId(Object parentId) { DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql); many.bindParentId(sqlDelete, parentId); return sqlDelete; } SpiSqlUpdate deleteByParentIdList(List parentIds) { StringBuilder sb = new StringBuilder(100); sb.append(deleteByParentIdInSql); sb.append(buildInClauseBinding(parentIds.size(), exportedPropertyBindProto)); DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); many.bindParentIds(delete, parentIds); return delete; } private void appendBind(StringBuilder sb, int count, boolean skipComma) { for (int i = 0; i < count; i++) { if (!skipComma || i > 0) { sb.append(","); } sb.append("?"); } } private void append(StringBuilder sb) { for (int i = 0; i < exportedProperties.length; i++) { String fkColumn = exportedProperties[i].getForeignDbColumn(); if (i > 0) { sb.append(","); } sb.append(fkColumn); } } private String deriveWhereParentIdSql(boolean inClause, String tableAlias) { StringBuilder sb = new StringBuilder(); if (inClause) { sb.append("("); } for (int i = 0; i < exportedProperties.length; i++) { String fkColumn = exportedProperties[i].getForeignDbColumn(); if (i > 0) { String s = inClause ? "," : " and "; sb.append(s); } sb.append(tableAlias).append(fkColumn); if (!inClause) { sb.append("=? "); } } if (inClause) { sb.append(")"); } return sb.toString(); } private String buildInClauseBinding(int size, String bindProto) { if (descriptor.isSimpleId()) { return descriptor.getIdBinder().getIdInValueExpr(false, size); } StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1))); sb.append(" in"); sb.append(" ("); for (int i = 0; i < size; i++) { if (i > 0) { sb.append(","); } sb.append(bindProto); } sb.append(") "); return sb.toString(); } private String deriveExportedPropertyBindProto() { if (exportedProperties.length == 1) { return "?"; } StringBuilder sb = new StringBuilder(); sb.append("("); for (int i = 0; i < exportedProperties.length; i++) { if (i > 0) { sb.append(","); } sb.append("?"); } sb.append(")"); return sb.toString(); } }