Skip to content

Commit

Permalink
schema is now optional, add support for union query
Browse files Browse the repository at this point in the history
  • Loading branch information
grabdoc committed Jan 14, 2024
1 parent 5571a65 commit ffa26be
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 55 deletions.
20 changes: 20 additions & 0 deletions src/main/java/com/homihq/db2rest/mybatis/MyBatisTable.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.homihq.db2rest.mybatis;

import lombok.*;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.mybatis.dynamic.sql.SqlColumn;
import org.mybatis.dynamic.sql.SqlTable;
import schemacrawler.schema.Column;
Expand All @@ -22,6 +24,8 @@ public class MyBatisTable extends SqlTable{

Table table;

boolean root;

public MyBatisTable(String schemaName, String tableName, Table table) {
super(tableName);
this.tableName = tableName;
Expand All @@ -41,4 +45,20 @@ public void addColumn(Column column) {
public void addColumn(String columnName, String alias) {
sqlColumnList.add(column(columnName).as(alias));
}

@Override
public boolean equals(Object o) {
if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

MyBatisTable table = (MyBatisTable) o;

return new EqualsBuilder().append(tableName, table.tableName).isEquals();
}

@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(tableName).toHashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class ReadController {

@GetMapping(value = "/{tableName}" , produces = "application/json")
public Object findByJoinTable(@PathVariable String tableName,
@RequestHeader(name = "Accept-Profile") String schemaName,
@RequestHeader(name = "Accept-Profile", required = false) String schemaName,
@RequestParam(name = "select", required = false, defaultValue = "") String select,
@RequestParam(name = "filter", required = false, defaultValue = "") String filter,
Sort sort,
Expand Down
31 changes: 8 additions & 23 deletions src/main/java/com/homihq/db2rest/rest/read/ReadService.java
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
package com.homihq.db2rest.rest.read;

import com.homihq.db2rest.config.Db2RestConfigProperties;
import com.homihq.db2rest.rest.read.helper.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.mybatis.dynamic.sql.SqlTable;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Map;


@Service
@Slf4j
@RequiredArgsConstructor
public class ReadService {

private final JdbcTemplate jdbcTemplate;
private final SelectBuilder selectBuilder;
private final JoinBuilder joinBuilder;
private final WhereBuilder whereBuilder;
private final LimitPaginationBuilder limitPaginationBuilder;
private final SortBuilder sortBuilder;

private final Db2RestConfigProperties db2RestConfigProperties;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public Object findAll(String schemaName, String tableName, String select, String filter,
Pageable pageable, Sort sort) {
ReadContext ctx = ReadContext.builder().from(SqlTable.of(tableName))
ReadContext ctx = ReadContext.builder()
.pageable(pageable).sort(sort)
.schemaName(schemaName).tableName(tableName).select(select).filter(filter).build();

.schemaName(schemaName)
.tableName(tableName).select(select).filter(filter).build();

selectBuilder.build(ctx);
joinBuilder.build(ctx);
Expand All @@ -42,23 +37,13 @@ public Object findAll(String schemaName, String tableName, String select, String
sortBuilder.build(ctx);

String sql = ctx.prepareSQL();

log.info("SQL - {}", sql);

/*
Query query = createQuery(schemaName, tableName,select,filter, joinTable, pageable);
String sql = query.getSQL();
List<Object> bindValues = query.getBindValues();
Map<String,Object> bindValues = ctx.prepareParameters();

log.info("SQL - {}", sql);
log.info("Bind variables - {}", bindValues);

return jdbcTemplate.queryForList(sql, bindValues.toArray());
*/
return namedParameterJdbcTemplate.queryForList(sql, bindValues);

return new ArrayList();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ public class JoinBuilder {
private final SchemaManager schemaManager;

public void build(ReadContext context) {

if(context.isUnion()) return;

List<MyBatisTable> tableList = context.getTables();

log.info("Table list - {}", tableList);

if(tableList.size() > 1) { //table join required
for(int i = 0 ; i < tableList.size() ; i = i + 2) {
MyBatisTable root = tableList.get(i);
Expand Down
84 changes: 72 additions & 12 deletions src/main/java/com/homihq/db2rest/rest/read/helper/ReadContext.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package com.homihq.db2rest.rest.read.helper;

import com.homihq.db2rest.exception.GenericDataAccessException;
import com.homihq.db2rest.mybatis.MyBatisTable;
import lombok.*;
import org.mybatis.dynamic.sql.BasicColumn;
import org.mybatis.dynamic.sql.SqlColumn;
import org.mybatis.dynamic.sql.SqlCriterion;
import org.mybatis.dynamic.sql.SqlTable;
import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
import org.mybatis.dynamic.sql.select.SelectModel;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.mybatis.dynamic.sql.select.SelectDSL.select;

Expand All @@ -30,22 +36,19 @@ public class ReadContext {
Pageable pageable;
Sort sort;

SqlTable from;
MyBatisTable from;
List<MyBatisTable> tables;

QueryExpressionDSL<SelectModel> queryExpressionDSL;
boolean union;

QueryExpressionDSL<SelectModel> queryExpressionDSL;
SelectStatementProvider selectStatementProvider;

public void addWhereClause(SqlCriterion condition) {
queryExpressionDSL.where(condition);
}

private List<BasicColumn> getAllColumns() {
return tables.stream()
.flatMap(t -> t.getSqlColumnList().stream())
.map(t -> (BasicColumn)t)
.toList();
}


public SqlColumn<?> getSortColumn(String columnName) {
//for now just support root table
Expand All @@ -57,16 +60,73 @@ public void createSelect() {

List<BasicColumn> columns = getAllColumns();

queryExpressionDSL = select(columns).from(from);
detectUnion();

if(union) {
createUnionQuery();
}
else{
from = getRootTable();
queryExpressionDSL = select(columns).from(from);
}

}

public String prepareSQL() {
private MyBatisTable getRootTable() {
return
tables.stream().filter(MyBatisTable::isRoot).findFirst()
.orElseThrow(() -> new GenericDataAccessException("Unable to detect root table."));
}

private void createUnionQuery() {

for(MyBatisTable table : tables) {

if(Objects.isNull(queryExpressionDSL)) {

queryExpressionDSL = select(getAllColumns()).from(table, table.getAlias());
}
else {
queryExpressionDSL.union().
select(getAllColumns()).from(table, table.getAlias());
}
}

return queryExpressionDSL.build().render(RenderingStrategies.SPRING_NAMED_PARAMETER).getSelectStatement();

}

private void detectUnion() {
Set<MyBatisTable> result = tables.stream()
.collect(Collectors.groupingBy(Function.identity()
, Collectors.counting()))
.entrySet().stream()
.filter(m -> m.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toSet());

union = (this.tables.size() == result.size()) && result.size() > 1;

if(result.size() > 1 && this.tables.size() > result.size())
throw new GenericDataAccessException("Unable to create SQL. Seems union but too many tables.");
}

private List<BasicColumn> getAllColumns() {
return tables.stream()
.flatMap(t -> t.getSqlColumnList().stream())
.map(t -> (BasicColumn)t)
.toList();
}

public String prepareSQL() {

selectStatementProvider = queryExpressionDSL.build().render(RenderingStrategies.SPRING_NAMED_PARAMETER);

return this.selectStatementProvider.getSelectStatement();

}

public Map<String,Object> prepareParameters() {
return this.selectStatementProvider.getParameters();
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.homihq.db2rest.rest.read.helper;

import com.homihq.db2rest.config.Db2RestConfigProperties;
import com.homihq.db2rest.mybatis.MyBatisTable;
import com.homihq.db2rest.schema.SchemaManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
Expand All @@ -15,6 +17,7 @@
public class SelectBuilder{

private final SchemaManager schemaManager;
private final Db2RestConfigProperties db2RestConfigProperties;

public void build(ReadContext context) {
context.setTables(createTables(context));
Expand All @@ -24,30 +27,42 @@ public void build(ReadContext context) {

private List<MyBatisTable> createTables(ReadContext context) {
List<MyBatisTable> tables = new ArrayList<>();

log.info("context.select - {}", context.select);
//split to get all fragments
String [] tabCols = context.select.split(";");

int counter = 0;

log.info("tabCols - {}", tabCols.length);

//process the fragments
for(String tabCol : tabCols) {
MyBatisTable table;
List<MyBatisTable> myBatisTables;

//check for presence of open '(' and close ')' brackets
//now check for embedded table and columns.
if(tabCol.contains("(") && tabCol.contains(")")) { //join table
if(tabCol.contains("(") && tabCol.contains(")")) { //join tables

String joinTableName = tabCol.substring(0, tabCol.indexOf("("));
//look for columns
String colString = tabCol.substring(tabCol.indexOf("(") + 1 , tabCol.indexOf(")"));
table = createTable(context.schemaName, joinTableName, colString, counter);
myBatisTables = createTables(context.schemaName, joinTableName, colString, counter);
}
else{ //root table
table = createTable(context.schemaName, context.tableName, tabCol, counter);

log.info("Creating root tables");
myBatisTables = createTables(context.schemaName, context.tableName, tabCol, counter);

log.info("myBatisTables - {}", myBatisTables);

for(MyBatisTable table : myBatisTables) {
table.setRoot(true);
}

//TODO - multiple root tables and no other table = union query, else unsupported exception

}
tables.add(table);
tables.addAll(myBatisTables);

counter++;
}
Expand All @@ -56,15 +71,40 @@ private List<MyBatisTable> createTables(ReadContext context) {
return tables;
}

private MyBatisTable createTable(String schemaName, String tableName, String colStr, int counter) {

//MyBatisTable table = schemaManager.findTable(schemaName, tableName, counter);
private List<MyBatisTable> createTables(String schemaName, String tableName, String colStr, int counter) {

String sName = schemaName;
String tName = tableName;


if(!this.db2RestConfigProperties.getMultiTenancy().isEnabled()) {
String [] tableNameParts = tableName.split("|.");

if(tableNameParts.length == 2) { //table name contains schema
sName = tableNameParts[0];
tName = tableNameParts[1];
}
}

if(StringUtils.isNotBlank(sName)) {
MyBatisTable table = schemaManager.findTable(sName, tName, counter);

addColumns(table, colStr);

return List.of(table);
}
else {
List<MyBatisTable> tables = schemaManager.findTables(tName);

MyBatisTable table = schemaManager.findTable(schemaName, tableName, counter);
for(MyBatisTable table : tables) {
addColumns(table, colStr);
}

return tables;
}

addColumns(table, colStr);

return table;
}

private void addColumns(MyBatisTable table, String colStr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class WhereBuilder{
private final SchemaManager schemaManager;

public void build(ReadContext context) {
if(context.isUnion()) return;

if(StringUtils.isNotBlank(context.filter)) {

log.info("-Creating where condition -");
Expand Down
Loading

0 comments on commit ffa26be

Please sign in to comment.