Множественные, комбинированные условия OR в ORMLite

Мне нравится иметь такой запрос:

select data from table
 where (x > 1 and x < 100)
    or (x > 250 and x < 300)

В ORMlite возможно использование этого кода:

final QueryBuilder<Data,Integer> qb = queryBuilder();
final Where<Data, Integer> w = qb.where();

w.or(
    w.gt("x", 1).and().lt("x", 100),
    w.gt("x", 250).and().lt("x", 300)
)

Хотя это здорово, если заранее знать условия и во время кодирования, мне нужны условия для динамического добавления.

В принципе, этого метода public com.j256.ormlite.stmt.Where<T,ID> or(com.j256.ormlite.stmt.Where<T,ID> left, com.j256.ormlite.stmt.Where<T,ID> right, com.j256.ormlite.stmt.Where<T,ID>... others) недостаточно. Ему нужен еще один метод or, который поддерживает условия ArrayList Where.

Спасибо за любые предложения.

Ответ 1

В ORMLite Where.or(Where<T, ID> left, Where<T, ID> right, Where<T, ID>... others) - это немного синтаксический взлом. Когда вы вызываете:

w.or(
    w.gt("x", 1).and().lt("x", 100),
    w.gt("x", 250).and().lt("x", 300)
);

Что получает метод or():

w.or(w, w);

Вы действительно можете переписать его как:

w.gt("x", 1).and().lt("x", 100);
w.gt("x", 250).and().lt("x", 300);
w.or(w, w);

В методе or используются только аргументы, чтобы подсчитать, сколько клауз он должен выскочить из стека. Когда вы вызываете gt и lt и другие, он перемещает элементы в стек статей. Метод and() извлекает 1 элемент из стека, а затем берет другой объект в будущем. Мы делаем эти синтаксические хаки, потому что хотим поддерживать линейные, связанные и аргументированные запросы:

w.gt("x", 1);
w.and();
w.lt("x", 100);

против

w.gt("x", 1).and().lt("x", 100);

против

w.and(w.gt("x", 1), w.lt("x", 100));

Но это означает, что вы можете значительно упростить свой код, используя метод Where.or(int many). Поэтому в приведенном выше примере or также может быть:

w.gt("x", 1).and().lt("x", 100);
w.gt("x", 250).and().lt("x", 300);
// create an OR statement from the last 2 clauses on the stack
w.or(2);

Таким образом, вам не нужен список conditions. Все, что вам нужно, это счетчик. Таким образом, вы можете сделать что-то вроде:

int clauseC = 0;
for (int i : values) {
    if (i == 1) {
        w.le(C_PREIS, 1000);
        clauseC++;
    } else if (i == 2) {
        w.gt(C_PREIS, 1000).and().le(C_PREIS, 2500);
        clauseC++;
    } else if (i == 3) {
        w.gt(C_PREIS, 2500).and().le(C_PREIS, 5000);
        clauseC++;
    } else if (i == 4) {
        w.gt(C_PREIS, 5000).and().le(C_PREIS, 10000);
        clauseC++;
    } else if (i == 5) {
        w.gt(C_PREIS, 10000);
        clauseC++;
    }
}
// create one big OR(...) statement with all of the clauses pushed above
if (clauseC > 1) {
    w.or(clauseC);
}

Если i может быть только от 1 до 5, вы можете просто использовать values.size() и пропустить clauseC. Обратите внимание: если мы добавляем только одно предложение, мы можем полностью пропустить вызов метода or.

О, и следующий оператор не будет работать:

target.or().raw(first.getStatement());

потому что target и first - это один и тот же объект. first.getStatement() сбрасывает все предложение SQL WHERE, которое я не думаю, что вы хотите.

Ответ 2

Вы понимаете, что означает ... часть декларации? Это означает, что вы можете передать массив (и что компилятор будет строить массив для вас, если вы просто укажете значения).

Итак, просто создайте список, если хотите, затем преобразуйте его в массив (для всех, кроме первого условия), а затем вызовите метод. Возможно, вы захотите сделать статический метод, чтобы сделать последнюю часть легко:

public static <T, ID> void or(Where<T, ID> target,
                              List<Where<T, ID>> conditions)
{
    // TODO: Argument validation
    Where<T, ID> first = conditions.get(0);
    Where<T, ID> second = conditions.get(1);
    List<Where<T, ID>> rest = conditions.subList(2, conditions.size());
    // You'll to suppress a warning here due to generics...
    Where<T, ID>[] restArray = rest.toArray(new Where[0]);
    target.where(first, second, restArray);
}