com.nortal.petit.orm.statement.StatementBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.nortal.petit.orm.statement.StatementBuilder.java

Source

/**
 *   Copyright 2014 Nortal AS
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package com.nortal.petit.orm.statement;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.nortal.petit.orm.statement.interceptor.StatementInterceptor;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.nortal.petit.beanmapper.BeanMapping;
import com.nortal.petit.beanmapper.Property;
import com.nortal.petit.orm.BeanPropertyConverter;
import com.nortal.petit.orm.DefaultBeanPropertyConverter;
import com.nortal.petit.orm.statement.clause.Limit;
import com.nortal.petit.orm.statement.clause.Order;
import com.nortal.petit.orm.statement.clause.OrderSql;
import com.nortal.petit.orm.statement.clause.SelectClause;
import com.nortal.petit.orm.statement.clause.SetClause;
import com.nortal.petit.orm.statement.clause.SqlPart;
import com.nortal.petit.orm.statement.clause.SqlPropertyParam;
import com.nortal.petit.orm.statement.clause.Where;
import com.nortal.petit.orm.statement.clause.WhereClause;

/**
 * @author Lauri Ltteme (lauri.lattemae@nortal.com)
 * @created 18.03.2013
 * 
 * @author Aleksei Lissitsin
 * 
 */
// TODO: maybe we should create sub builder for every CRUD operation
public abstract class StatementBuilder
        implements SelectClause<StatementBuilder>, SetClause, WhereClause<StatementBuilder> {
    private BeanPropertyConverter converter = new DefaultBeanPropertyConverter();

    private String table;
    private String alias;

    private List<String> select;
    private List<String> setBy;
    private List<Object> setWith;
    private List<String> group;

    private OrderSql orderSql;

    private SqlPart where;

    private Function<String, String> propertyNameMapper = Functions.<String>identity();

    private StatementInterceptor interceptor;

    public void setPropertyNameMapper(Function<String, String> propertyNameMapper) {
        this.propertyNameMapper = propertyNameMapper;
    }

    public void setConverter(BeanPropertyConverter converter) {
        this.converter = converter;
    }

    public StatementBuilder table(String table) {
        this.table = table;
        return this;
    }

    public StatementBuilder alias(String alias) {
        this.alias = alias;
        return this;
    }

    public StatementBuilder setBy(List<String> properties) {
        this.setBy = properties;
        return this;
    }

    protected List<String> getSetBy() {
        return setBy;
    }

    @Override
    @SuppressWarnings("unchecked")
    public StatementBuilder setBy(String... properties) {
        return setBy(Arrays.asList(properties));
    }

    @Override
    @SuppressWarnings("unchecked")
    public StatementBuilder setWith(Object... params) {
        setWith = Arrays.asList(params);
        return this;
    }

    // === SELECT ===
    @Override
    public StatementBuilder select(String... columns) {
        this.select = Arrays.asList(columns);
        return this;
    }

    public String getSelectClause() {
        StringBuilder clause = new StringBuilder("SELECT ");
        if (select != null && !select.isEmpty()) {
            List<String> selectBy = new ArrayList<String>();
            if (StringUtils.isNotEmpty(alias)) {
                for (String item : select) {
                    selectBy.add(alias + "." + propertyNameMapper.apply(item));
                }
            } else {
                selectBy = Lists.newArrayList(Iterables.transform(select, propertyNameMapper));
            }
            clause.append(StringUtils.join(selectBy, ", "));
        } else {
            if (StringUtils.isNotEmpty(alias)) {
                clause.append(alias).append(".");
            }
            clause.append("*");
        }
        return clause.toString();
    }

    // ##SELECT##

    // === WHERE ===

    /**
     * Replaces current where clause.
     */
    public StatementBuilder setWhere(SqlPart where) {
        this.where = where;
        return this;
    }

    /**
     * Adds a where condition. If there already was a where condition, joins
     * them with an and.
     */
    public StatementBuilder where(SqlPart where) {
        if (this.where == null) {
            setWhere(where);
        } else {
            setWhere(Where.and(this.where, where));
        }
        return this;
    }

    /**
     * Convenience method for adding and AND clause with equals operations
     * 
     * @param property
     * @param value
     */
    public StatementBuilder where(String property, Object value) {
        return where(Where.eq(property, value));
    }

    public String getWhereClause() {
        return where == null ? null : "WHERE " + where.sql(propertyNameMapper);
    }

    // ##WHERE##

    // === GROUP ===
    public StatementBuilder group(String... columns) {
        this.group = Arrays.asList(columns);
        return this;
    }

    public String getGroupClause() {
        if (group != null && !group.isEmpty()) {
            return "GROUP BY " + StringUtils.join(group, ", ");
        } else {
            return null;
        }
    }

    // ##GROUP##

    // === ORDER ===
    /**
     * Replaces current order clause.
     */
    public StatementBuilder setOrder(OrderSql orderSql) {
        this.orderSql = orderSql;
        return this;
    }

    /**
     * Adds orders to the current order clause.
     */
    public StatementBuilder order(OrderSql orderSql) {
        if (this.orderSql == null) {
            setOrder(orderSql);
        } else {
            this.orderSql.add(orderSql);
        }
        return this;
    }

    /**
     * Adds orders to the current order clause.
     */
    public StatementBuilder order(Order... orderClauses) {
        if (this.orderSql == null) {
            this.orderSql = OrderSql.order(orderClauses);
        } else {
            this.orderSql.add(orderClauses);
        }
        return this;
    }

    /**
     * Adds asc orders to the current order clause.
     */
    public StatementBuilder asc(String... properties) {
        for (String p : properties) {
            order(Order.asc(p));
        }
        return this;
    }

    /**
     * Adds desc orders to the current order clause.
     */
    public StatementBuilder desc(String... properties) {
        for (String p : properties) {
            order(Order.desc(p));
        }
        return this;
    }

    public String getOrderClause() {
        return orderSql == null ? null : orderSql.sql(propertyNameMapper);
    }

    // ##ORDER##

    public String getFromClause() {
        StringBuilder clause = new StringBuilder("FROM ");
        if (StringUtils.isNotEmpty(table)) {
            clause.append(table);
            if (StringUtils.isNotEmpty(alias)) {
                clause.append(" ").append(alias);
            }
        } else {
            throw new IllegalStateException("from clause unspecified");
        }
        return clause.toString();
    }

    public Object[] getParams(Function<String, Object> paramMapper) {
        List<Object> params = new ArrayList<Object>();

        if (setWith != null && !setWith.isEmpty()) {
            params.addAll(SqlPropertyParam.resolvePropParams(setWith, paramMapper));
        }

        if (where != null) {
            params.addAll(where.params(paramMapper));
        }

        return FluentIterable.from(params).transform(converter).toArray(Object.class);
    }

    public String getLoad() {
        return Joiner.on(" ").skipNulls().join(getSelectClause(), getFromClause(), getWhereClause(),
                getGroupClause(), getOrderClause());
    }

    public abstract String limitSql(Limit limit);

    public String countSql() {
        StringBuilder sb = new StringBuilder("SELECT COUNT(*) FROM (");
        sb.append(getLoad()).append(") t");
        return sb.toString();
    }

    public String getInsert() {
        StringBuilder expression = new StringBuilder("INSERT INTO ").append(table).append(" (");
        Joiner.on(", ").appendTo(expression, Iterables.transform(setBy, propertyNameMapper));
        expression.append(") VALUES (").append(StringUtils.repeat("?", ", ", setBy.size())).append(")");
        return expression.toString();
    }

    public String getUpdate() {
        StringBuilder expression = new StringBuilder("UPDATE ").append(table).append(" ");
        if (StringUtils.isNotEmpty(alias)) {
            expression.append(alias).append(" ");
        }
        expression.append("SET ");
        if (setBy.size() > 1) {
            Joiner.on("=?, ").appendTo(expression, Iterables.transform(setBy, propertyNameMapper));
            expression.append("=? ");
        } else {
            expression.append(propertyNameMapper.apply(setBy.get(0))).append("=? ");
        }
        expression.append(getWhereClause());
        return expression.toString();
    }

    public String getDelete() {
        StringBuilder expression = new StringBuilder("DELETE FROM ").append(table).append(" ");
        if (StringUtils.isNotEmpty(alias)) {
            expression.append(alias).append(" ");
        }
        expression.append(getWhereClause());
        return expression.toString();
    }

    public <B> List<String> getWritableProps(BeanMapping<B> mapping, StatementType stmtType) {
        List<String> res = new ArrayList<String>();
        for (Property<B, Object> p : mapping.props().values()) {
            if (!p.readOnly()) {
                res.add(p.name());
            }
        }
        return res;
    }

    public void setInterceptor(StatementInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    public StatementInterceptor getInterceptor() {
        return this.interceptor;
    }
}