GitHub项目:jkrasnay/sqlbuilder的使用

image.png

技术选型:

在报表查询时,通常需要做可以动态添加的条件

在老项目中使用的是一种Tcondition的对象分装sql


import java.util.HashMap;

public class TCondition {

    private String sql;

    private Object[] paraValues;

    private String orderBy;

    private String groupBy;

    private String having;

    /**
     * key为表名,value为 left join on后的内容(如何关联的)
     */
    private HashMap<String,String> leftJoinMap;

    /**
     * count方法计数时可指定 count哪个字段,默认为count(1)
     */
    private String mainId;

    public TCondition() {

    }

    public TCondition(String sql, Object... paraValues) {
        this.setSql(sql);
        this.setParaValues(paraValues);
    }

    public static TCondition of() {
        return new TCondition();
    }

    public static TCondition of(String sql, Object... paraValues) {
        return new TCondition(sql, paraValues);
    }

    public String getSql() {
        return sql;
    }

    public TCondition setSql(String sql) {
        this.sql = sql;
        return this;
    }

    public Object[] getParaValues() {
        return paraValues;
    }

    public TCondition setParaValues(Object[] paraValues) {
        this.paraValues = paraValues;
        return this;
    }

    public String getOrderBy() {
        return orderBy;
    }

    public TCondition setOrderBy(String orderBy) {
        this.orderBy = orderBy;
        return this;
    }

    public String getGroupBy() {
        return groupBy;
    }

    public TCondition setGroupBy(String groupBy) {
        this.groupBy = groupBy;
        return this;
    }

    public String getHaving() {
        return having;
    }

    public TCondition setHaving(String having) {
        this.having = having;
        return this;
    }

    public String getMainId() {
        return mainId;
    }

    public TCondition setMainId(String mainId) {
        this.mainId = mainId;
        return this;
    }

    @Override
    public String toString() {
        StringBuffer result = new StringBuffer(this.getSql() + ":");
        for (int i = 0; i < paraValues.length; i++) {
            result.append(paraValues[i]).append(",");
        }
        result.append(" || ");
        return result.toString();
    }

    public HashMap<String, String> getLeftJoinMap() {
        return leftJoinMap;
    }

    public TCondition setLeftJoinMap(HashMap<String, String> leftJoinMap) {
        this.leftJoinMap = leftJoinMap;
        return this;
    }

但是由于与老代码的数据库访问层紧紧的耦合在一起,无法在另外的项目中进行复用,因此,在GitHub中找到了一款类似的封装SQL查询的对象

封装的内容:

package ca.krasnay.sqlbuilder;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Tool for programmatically constructing SQL select statements. This class aims
 * to simplify the task of juggling commas and SQL keywords when building SQL
 * statements from scratch, but doesn't attempt to do much beyond that. Here are
 * some relatively complex examples:
 *
 * <pre>
 * String sql = new SelectBuilder()
 * .column("e.id")
 * .column("e.name as empname")
 * .column("d.name as deptname")
 * .column("e.salary")
 * .from(("Employee e")
 * .join("Department d on e.dept_id = d.id")
 * .where("e.salary > 100000")
 * .orderBy("e.salary desc")
 * .toString();
 * </pre>
 *
 * <pre>
 * String sql = new SelectBuilder()
 * .column("d.id")
 * .column("d.name")
 * .column("sum(e.salary) as total")
 * .from("Department d")
 * .join("Employee e on e.dept_id = d.id")
 * .groupBy("d.id")
 * .groupBy("d.name")
 * .having("total > 1000000").toString();
 * </pre>
 *
 * Note that the methods can be called in any order. This is handy when a base
 * class wants to create a simple query but allow subclasses to augment it.
 *
 * It's similar to the Squiggle SQL library
 * (http://code.google.com/p/squiggle-sql/), but makes fewer assumptions about
 * the internal structure of the SQL statement, which I think makes for simpler,
 * cleaner code. For example, in Squiggle you would write...
 *
 * <pre>
 * select.addCriteria(new MatchCriteria(orders, "status", MatchCriteria.EQUALS, "processed"));
 * </pre>
 *
 * With SelectBuilder, we assume you know how to write SQL expressions, so
 * instead you would write...
 *
 * <pre>
 * select.where("status = 'processed'");
 * </pre>
 *
 * To include parameters, it's highly recommended to use the
 * {@link ParameterizedPreparedStatementCreatorTest}, like this:
 *
 * <pre>
 * String sql = new SelectBuilder("Employee e")
 * .where("name like :name")
 * .toString();
 *
 * PreparedStatement ps = new ParameterizedPreparedStatementCreator(sql)
 * .setParameter("name", "Bob%")
 * .createPreparedStatement(conn);
 * </pre>
 *
 *
 * @author John Krasnay <john@krasnay.ca>
 */
public class SelectBuilder extends AbstractSqlBuilder implements Cloneable, Serializable {

    private static final long serialVersionUID = 1;

    private boolean distinct;

    private List<Object> columns = new ArrayList<Object>();

    private List<String> tables = new ArrayList<String>();

    private List<String> joins = new ArrayList<String>();

    private List<String> leftJoins = new ArrayList<String>();

    private List<String> wheres = new ArrayList<String>();

    private List<String> groupBys = new ArrayList<String>();

    private List<String> havings = new ArrayList<String>();

    private List<SelectBuilder> unions = new ArrayList<SelectBuilder>();

    private List<String> orderBys = new ArrayList<String>();

    private boolean forUpdate;

    private boolean noWait;

    public SelectBuilder() {

    }

    public SelectBuilder(String table) {
        tables.add(table);
    }

    /**
     * Copy constructor. Used by {@link #clone()}.
     *
     * @param other
     *            SelectBuilder being cloned.
     */
    protected SelectBuilder(SelectBuilder other) {

        this.distinct = other.distinct;
        this.forUpdate = other.forUpdate;
        this.noWait = other.noWait;

        for (Object column : other.columns) {
            if (column instanceof SubSelectBuilder) {
                this.columns.add(((SubSelectBuilder) column).clone());
            } else {
                this.columns.add(column);
            }
        }

        this.tables.addAll(other.tables);
        this.joins.addAll(other.joins);
        this.leftJoins.addAll(other.leftJoins);
        this.wheres.addAll(other.wheres);
        this.groupBys.addAll(other.groupBys);
        this.havings.addAll(other.havings);

        for (SelectBuilder sb : other.unions) {
            this.unions.add(sb.clone());
        }

        this.orderBys.addAll(other.orderBys);
    }

    /**
     * Alias for {@link #where(String)}.
     */
    public SelectBuilder and(String expr) {
        return where(expr);
    }

    public SelectBuilder column(String name) {
        columns.add(name);
        return this;
    }

    public SelectBuilder column(SubSelectBuilder subSelect) {
        columns.add(subSelect);
        return this;
    }

    public SelectBuilder column(String name, boolean groupBy) {
        columns.add(name);
        if (groupBy) {
            groupBys.add(name);
        }
        return this;
    }

    @Override
    public SelectBuilder clone() {
        return new SelectBuilder(this);
    }

    public SelectBuilder distinct() {
        this.distinct = true;
        return this;
    }

    public SelectBuilder forUpdate() {
        forUpdate = true;
        return this;
    }

    public SelectBuilder from(String table) {
        tables.add(table);
        return this;
    }

    public List<SelectBuilder> getUnions() {
        return unions;
    }

    public SelectBuilder groupBy(String expr) {
        groupBys.add(expr);
        return this;
    }

    public SelectBuilder having(String expr) {
        havings.add(expr);
        return this;
    }

    public SelectBuilder join(String join) {
        joins.add(join);
        return this;
    }

    public SelectBuilder leftJoin(String join) {
        leftJoins.add(join);
        return this;
    }

    public SelectBuilder noWait() {
        if (!forUpdate) {
            throw new RuntimeException("noWait without forUpdate cannot be called");
        }
        noWait = true;
        return this;
    }

    public SelectBuilder orderBy(String name) {
        orderBys.add(name);
        return this;
    }

    /**
     * Adds an ORDER BY item with a direction indicator.
     *
     * @param name
     *            Name of the column by which to sort.
     * @param ascending
     *            If true, specifies the direction "asc", otherwise, specifies
     *            the direction "desc".
     */
    public SelectBuilder orderBy(String name, boolean ascending) {
        if (ascending) {
            orderBys.add(name + " asc");
        } else {
            orderBys.add(name + " desc");
        }
        return this;
    }

    @Override
    public String toString() {

        StringBuilder sql = new StringBuilder("select ");

        if (distinct) {
            sql.append("distinct ");
        }

        if (columns.size() == 0) {
            sql.append("*");
        } else {
            appendList(sql, columns, "", ", ");
        }

        appendList(sql, tables, " from ", ", ");
        appendList(sql, joins, " join ", " join ");
        appendList(sql, leftJoins, " left join ", " left join ");
        appendList(sql, wheres, " where ", " and ");
        appendList(sql, groupBys, " group by ", ", ");
        appendList(sql, havings, " having ", " and ");
        appendList(sql, unions, " union ", " union ");
        appendList(sql, orderBys, " order by ", ", ");

        if (forUpdate) {
            sql.append(" for update");
            if (noWait) {
                sql.append(" nowait");
            }
        }

        return sql.toString();
    }

    /**
     * Adds a "union" select builder. The generated SQL will union this query
     * with the result of the main query. The provided builder must have the
     * same columns as the parent select builder and must not use "order by" or
     * "for update".
     */
    public SelectBuilder union(SelectBuilder unionBuilder) {
        unions.add(unionBuilder);
        return this;
    }

    public SelectBuilder where(String expr) {
        wheres.add(expr);
        return this;
    }
}

在后续的文章中我将介绍如何使用该组件,在报表项目中进行使 ^_^

github地址:

https://github.com/jkrasnay/sqlbuilder

博客:

http://john.krasnay.ca/2010/02/15/building-sql-in-java.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,488评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,034评论 3 414
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 175,327评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,554评论 1 307
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,337评论 6 404
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,883评论 1 321
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,975评论 3 439
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,114评论 0 286
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,625评论 1 332
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,555评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,737评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,244评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,973评论 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,362评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,615评论 1 280
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,343评论 3 390
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,699评论 2 370

推荐阅读更多精彩内容