有条件地将文本添加到 2 个指定点之间的字符串构建器的中间

Conditionally adding text to a the middle of a string builder between 2 specified points

我有一个 StringBuilder() sb 包含以下文本:

SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation
FROM Program p
INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId
INNER JOIN Category c on c.CategoryId = r.CategoryId
WHERE
p.ProgramName = 'Jubilee Baltimore'
c.CategoryName = 'Community Stability and Growth'
p.BaseLocation = 'Community Based'
ORDER BY p.ProgramName;

为了执行此查询,我需要在第一个参数和每个附加参数之间的 where 子句中添加单词 AND。因为有很多可能的 where 参数,而且不知道哪些参数将为空,哪些不会为空,这是实现我的目标的有效方法。所以期望的结果是:

SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation
FROM Program p
INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId
INNER JOIN Category c on c.CategoryId = r.CategoryId
WHERE
p.ProgramName = 'Jubilee Baltimore'
AND
c.CategoryName = 'Community Stability and Growth'
AND
p.BaseLocation = 'Community Based'
ORDER BY p.ProgramName;

我怎么能做类似sb.insert([every new line break after 2nd line break after 'WHERE' but before 'ORDER'], 'AND');

的事情

因此,期望结果的替代示例也可能如下所示:

SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation, p.Support
FROM Program p
INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId
INNER JOIN Category c on c.CategoryId = r.CategoryId
WHERE
c.CategoryName = 'Community Stability and Growth'
AND
p.BaseLocation = 'Community Based'
AND
p.Support= 'Financially Supported'
ORDER BY p.ProgramName;

所以我需要在“WHERE”之后的第二个换行符之后的每个换行符之后添加 AND,并在“ORDER”一词处停止。

我知道这非常具体,因此欢迎使用其他方法来实现这一目标。还值得注意的是 WHERE 和它的换行符总是在同一个位置,所以我可以很容易地得到那个位置。另一方面,ORDER 将始终位于不同的位置,但始终是最后一行。

编辑:为了清楚起见,我将展示我正在执行此操作的整个功能:

   @Override
    public List<ProgramTable> getProgramsByAdvancedSearch(SearchModel searchContext) {
        System.out.println(searchContext.getName());

        StringBuilder sb = new StringBuilder();

        sb.append("SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation\n" +
                "FROM Program p\n" +
                "INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId\n" +
                "INNER JOIN Category c on c.CategoryId = r.CategoryId\n" +
                "WHERE\n");

        if(searchContext.getName() != null){
            sb.append("p.ProgramName = " + "'" + searchContext.getName() + "'" + "\n");
        }

        if(searchContext.getCategory() != null){
            sb.append("c.CategoryName = " + "'" + searchContext.getCategory() + "'" + "\n");
        }

        sb.append("ORDER BY p.ProgramName;");

        //The line break after WHERE is character number 241
        //Would like to do the AND insert here

      
        //Getting "java.lang.NullPointerException: null" here
        Query query = entityManager.createNativeQuery(sb.toString());

        List<ProgramTable> searchList = query.getResultList();

        return searchList;
    }

更新:我已经添加了我需要的所有参数,并且正在使用第一个答案中提供的代码,但我得到了一个额外的并且我不想要:

@Override
    public List<ProgramTable> getProgramsByAdvancedSearch(SearchModel searchContext) {

        String baseStatement = "SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation, s.SupportDescription, p.StreetAddress, p.AreaServed\n" +
                "FROM Program p\n" +
                "INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId\n" +
                "INNER JOIN Category c on c.CategoryId = r.CategoryId\n" +
                "INNER JOIN Ref_ProgramSupport rs on rs.ProgramId = p.ProgramId\n" +
                "INNER JOIN Support s on s.SupportId = rs.SupportId\n" +
                "WHERE\n";

        List<String> clauses = new ArrayList<>();
        if(searchContext.getName() != null){
            clauses.add("p.ProgramName = " + "'" + searchContext.getName() + "'" + "\n");
        }

        if(searchContext.getCategory() != null){
            clauses.add("c.CategoryName = " + "'" + searchContext.getCategory() + "'" + "\n");
        }

        if(searchContext.getBaseLocation() != null){
            clauses.add("p.BaseLocation = " + "'" + searchContext.getBaseLocation() + "'" + "\n");
        }

        //Might need to strip the brackets off the result here
        if(searchContext.getSupport() != null){
            clauses.add("s.SupportDescription = " + "'" + searchContext.getSupport() + "'" + "\n");
        }

        if(searchContext.getAddress() != null){
            clauses.add("p.StreetAddress = " + "'" + searchContext.getAddress() + "'" + "\n");
        }

        if(searchContext.getAreaServed() != null){
            clauses.add("p.AreaServed = " + "'" + searchContext.getAreaServed() + "'" + "\n");
        }

        StringBuilder sb = new StringBuilder(baseStatement);
        for (int i = 0; i < clauses.size() - 2 ; i++) {
            sb.append(clauses.get(i));
            sb.append("AND\n");
        }

        sb.append("ORDER BY p.ProgramName;");

        System.out.println(clauses);
        System.out.println(sb);

        //Getting "java.lang.NullPointerException: null" here
        Query query = entityManager.createNativeQuery(sb.toString());

        List<ProgramTable> searchList = query.getResultList();

        return searchList;
    }

结果:

SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation, s.SupportDescription, p.StreetAddress, p.AreaServed
FROM Program p
INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId
INNER JOIN Category c on c.CategoryId = r.CategoryId
INNER JOIN Ref_ProgramSupport rs on rs.ProgramId = p.ProgramId
INNER JOIN Support s on s.SupportId = rs.SupportId
WHERE
p.ProgramName = 'Jubilee Baltimore  '
AND
c.CategoryName = 'Community Stability and Growth'
AND
p.BaseLocation = 'Community Based '
AND
s.SupportDescription = '[Financially Supported]'
AND
ORDER BY p.ProgramName;

TL:DR 您的解决方案

@Override
public List<ProgramTable> getProgramsByAdvancedSearch(SearchModel searchContext) {
    System.out.println(searchContext.getName());

    String baseStatement = "SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation\n" +
        "FROM Program p\n" +
        "INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId\n" +
        "INNER JOIN Category c on c.CategoryId = r.CategoryId\n" +
        "WHERE\n";

    List<String> clauses = new ArrayList<>();
    if(searchContext.getName() != null){
        clauses.add("p.ProgramName = " + "'" + searchContext.getName() + "'" + "\n");
    }

    if(searchContext.getCategory() != null){
        clauses.add("c.CategoryName = " + "'" + searchContext.getCategory() + "'" + "\n");
    }

    StringBuilder sb = new StringBuilder(baseStatement);
    for (int i = 0; i < clauses.size(); i++) {
        sb.append(clauses.get(i));
        if (i < clauses.size() - 1) {
            sb.append("AND\n");
        }
    }

    sb.append("ORDER BY p.ProgramName;");

    // The line break after WHERE is character number 241
    // Would like to do the AND insert here

    // Getting "java.lang.NullPointerException: null" here
    Query query = entityManager.createNativeQuery(sb.toString());

    List<ProgramTable> searchList = query.getResultList();

    return searchList;
}

这里有很多要解包的问题,​​但我只关注主要问题(在两个特定点之间的字符串中间添加“AND”)。由于此方法(按程序)创建了 SQL 语句,您可以这样做:

if(searchContext.getName() != null){
    sb.append("p.ProgramName = " + "'" + searchContext.getName() + "'" + "\n");
}

if(searchContext.getCategory() != null){
    if (searchContext.getName() != null) {
        sb.append("AND\n");
    sb.append("c.CategoryName = " + "'" + searchContext.getCategory() + "'" + "\n");
}

您可以在多个 if 语句中继续该模式,直到到达 ORDER by 行。这是最简单的解决方案,尽管不是最优雅的。

或者,您可以完全构建 SQL 语句并在 StringBuilder 对象上调用 toString() 方法,然后想办法将“AND”插入结果字符串在尽可能多的地方。

String sqlStatement = sb.toString();
// Call String's replace(...), replaceFirst(...), or replaceAll(...) here

显然,这更难,因为您必须知道需要插入的精确索引、要替换的字符序列,或者要匹配的正则表达式才能在正确的位置插入。这就是为什么我相信我给你的第一个选项是解决这个问题的正确方法。

现在....如果将其分解为单独的字符串缓冲区,您将获得更大的灵活性。例如,这个

sb.append("SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation\n" +
    "FROM Program p\n" +
    "INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId\n" +
    "INNER JOIN Category c on c.CategoryId = r.CategoryId\n" +
    "WHERE\n");

可以是它自己的字符串,因为它都是 hard-coded.

String baseStatement = "SELECT p.ProgramId, c.CategoryId, c.CategoryName, p.ProgramName, p.Description, p.Active, p.BaseLocation\n" +
    "FROM Program p\n" +
    "INNER JOIN Ref_ProgramCategory r on r.ProgramId = p.ProgramId\n" +
    "INNER JOIN Category c on c.CategoryId = r.CategoryId\n" +
    "WHERE\n";

然后,您可以捕获列表中的后续项目:

List<String> clauses = new ArrayList<>();
if(searchContext.getName() != null){
    clauses.add("p.ProgramName = " + "'" + searchContext.getName() + "'" + "\n");
}

if(searchContext.getCategory() != null){
    clauses.add("c.CategoryName = " + "'" + searchContext.getCategory() + "'" + "\n");
}
// many other clauses

最后,您可以遍历子句列表并在它们之间添加“AND”。这样,您就不需要检查前面的子句是否存在来添加“AND”。如果您有两个以上的子句并且缺少中间的某些子句,这将特别有用。

StringBuilder sb = new StringBuilder(baseStatement);
for (int i = 0; i < clauses.size(); i++) {
    sb.append(clauses.get(i));
    if (i < clauses.size() - 1) {
        sb.append("AND\n");
    }
}

您遍历整个列表,但停止在 size() - 1 处插入“AND”,以便您只将“AND”添加到倒数第二个子句。之后,您就可以调用 sb.toString().

P.S。无法在任意点 append()StringBuffer 。您只能追加到缓冲区的末尾。您可以 insert()replace() 但同样,您需要知道要插入的确切索引位置。这不是一件容易的事。对于这样的情况,如果您可以放置​​奇怪的字符模式,您知道这些字符模式不会存在于您的字符串中的任何其他位置,然后您可以用您想要的字符串值替换该字符序列(模式)的所有出现;在这种情况下,“和”。