像写SQL同样编写Java数据应用-TinySqlDsl

前言

话说企业应用,通常离不开数据库。要作数据库,能够有N种方案,好比:直接采用JDBC层本身封装下使用的,采用一些框架的,如:iBatis,Hiberate,Spring JDBC Template等等(这个太多了,所以不一一列举)的,这些方案也都在各自的领域展现了本身的特色,解决了至关部分的技术问题,并取得了至关好的应用效果。 java

可是不论是哪一种方案,其优势和缺点每每也是连在一块儿的,究其缘由是由于SQL和Java编程之间是割裂的,若是封装得不到位,作Java的人太难使用;若是封装得太多,在作一些用复杂SQL的时候又很是麻烦。好比:Hibernate就采用了封装HQL的方式来解决这方面的问题。iBatis对于SQL支持比较好,可是又会有一些割裂感,同时在解决时还要引入动态SQL来解决须要根据一些运行时条件来处理的问题,必定程度上又增长了使用的复杂度。 git

那么问题就来了,有没有更好的方式来解决数据库应用开发过程当中的问题呢?究其根本缘由是要如何解决数据库开发中的SQL与Java代码之间的割裂问题,若是能把这个问题解决掉,理论上会有一个不错的解。 sql

咱们知道SQL实际是是一种数据为领域的DSL语言,若是咱们能直接在Java中编写SQL,而后执行结果就能够直接返回Java对象,这个问题不就有了良好的解决方案么? 数据库

TinySqlDsl解决方案

实际上这方面已经有一些现成的解决方案,可是有的不是开源的,有的支持的还不是很是到位,所以悠然就决定尝试着写一下,写了半天时间看了看效果,详见RESTful风格的支持实践一文,内部讨论了一下,感受还不错,因而正式决定正式花时间来编写一个TinySqlDsl,固然实际编写的时候,仍是有许多的问题点的,以致于最终的风格与上面的文章还有一些不一致,固然这也是正常的,容易理解的,不然那什么也太神了。 编程

咱们常见的SQL语句有Select、Insert、Update、Delete,所以咱们的方案中也实现了这几个语句的编写方式。 数组

首先来看看看TinySqlDsl版的Dao是怎么写的。 框架

第一步:定义POJO

public class Custom {
	
	private String id;
	
	private String name;
	
	private int age;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
}

第二步:定义表结构定义文件

public class CustomTable extends Table {
    public static final CustomTable CUSTOM = new CustomTable();
    public final Column ID = new Column(this, "id");
    public final Column NAME = new Column(this, "name");
    public final Column AGE = new Column(this, "age");

    private CustomTable() {
        super("custom");
    }
}

第三步:编写DAO类

public class CustomDao {
	private DslSession dslSession;

	public DslSession getDslSession() {
		return dslSession;
	}

	public void setDslSession(DslSession dslSession) {
		this.dslSession = dslSession;
	}

	public void insertCustom(Custom custom) {
		dslSession.execute(
			insertInto(CUSTOM).values(
				CUSTOM.ID.value(custom.getId()),
				CUSTOM.NAME.value(custom.getName()),
				CUSTOM.AGE.value(custom.getAge())
			)
		);
	}

	public void updateCustom(Custom custom) {
		dslSession.execute(
			update(CUSTOM).set(
				CUSTOM.NAME.value(custom.getName()),
				CUSTOM.AGE.value(custom.getAge())).where(
				CUSTOM.ID.eq(custom.getId())
			)
		);
	}

	public void deleteCustom(String id) {
		dslSession.execute(
				delete(CUSTOM).where(
						CUSTOM.ID.eq(id)
				)
		);
	}

	public Custom getCustomById(String id) {
		return dslSession.fetchOneResult(
			selectFrom(CUSTOM).where(
					CUSTOM.ID.eq(id)
			)
		, Custom.class);
	}

	public List<Custom> queryCustom(Custom custom) {
		return dslSession.fetchList(
			selectFrom(CUSTOM).where(
				and(
						CUSTOM.ID.eq(custom.getId()),
						CUSTOM.NAME.equal(custom.getName()),
						CUSTOM.AGE.equal(custom.getAge())
				)
			)
		, Custom.class);
	}
}
看了上面的示例,会不会感受有点奇怪,怎么能够这么写?呵呵,先别着急了解实际的实现机理,咱们先品味一下这种DSL风格的数据库编写方式,嗯嗯,具体的来讲就是像写SQL同样的方式来写SQL。

代码说明

每一个数据表都要有两个类进行映射,一个是POJO类,这个你们都很是熟悉就再也不花时间进行说明了,用于构建Dao代码的时候使用。另外一个是表结构,用于在Java中定义数据库的表结构。 函数

public class CustomTable extends Table {
    public static final CustomTable CUSTOM = new CustomTable();
    public final Column ID = new Column(this, "id");
    public final Column NAME = new Column(this, "name");
    public final Column AGE = new Column(this, "age");

    private CustomTable() {
        super("custom");
    }
}
这个类主要由以下几部分组成:

CustomTable对应于一个表结构类型,它继承自Table类。 工具

构造函数,中的super("custom")使之与数据库的表名进行映射。 fetch

public static final CustomTable CUSTOM = new CustomTable();这句定义了一个常量CUSTOM,对应于具备的表,它的用得中在DSL语法用要用到表的时候使用

这个类里定义了3个public成员变量,这些成员变量和具体的字段数相对应,表里有几个字段,这里就定义几个字段,这个实例化自Column。

OK,这样表结构的定义就作好了。

正由于有了上面的定义,才能够在Dao中用Java代码像SQL同样的编写程序,可是这些语句是怎么才能执行出结果的呢?这就要看DslSession的了。

DslSesssion

DslSession是与数据库打交道的类,说白了,它就是一个SQL执行器。

public interface DslSession {
    /**
     * 执行Insert语句关返回
     *
     * @param insert
     * @return
     */
    int execute(Insert insert);

    /**
     * 执行更新语句
     *
     * @param update
     * @return
     */
    int execute(Update update);

    /**
     * 执行删除语句
     *
     * @param delete
     * @return
     */
    int execute(Delete delete);

    /**
     * 返回一个结果,既然是有多个结果也只返回第一个结果
     *
     * @param select
     * @param requiredType
     * @param <T>
     * @return
     */
    <T> T fetchOneResult(Select select, Class<T> requiredType);

    /**
     * 把全部的结果变成一个对象数组返回
     *
     * @param select
     * @param requiredType
     * @param <T>
     * @return
     */
    <T> T[] fetchArray(Select select, Class<T> requiredType);

    /**
     * 把全部的结果变成一个对象列表返回
     *
     * @param select
     * @param requiredType
     * @param <T>
     * @return
     */
    <T> List<T> fetchList(Select select, Class<T> requiredType);
    
    /**
     * 返回一个结果,既然是有多个结果也只返回第一个结果
     *
     * @param complexSelect
     * @param requiredType
     * @param <T>
     * @return
     */
    <T> T fetchOneResult(ComplexSelect complexSelect, Class<T> requiredType);

    
    /**
     * 把全部的结果变成一个对象数组返回
     *
     * @param complexSelect
     * @param requiredType
     * @param <T>
     * @return
     */
    <T> T[] fetchArray(ComplexSelect complexSelect, Class<T> requiredType);

    /**
     * 把全部的结果变成一个对象列表返回
     *
     * @param complexSelect
     * @param requiredType
     * @param <T>
     * @return
     */
    <T> List<T> fetchList(ComplexSelect complexSelect, Class<T> requiredType);

}
它的方法也比较简单,主要功能就是执行这几个语句。正是因为把复杂的SQL都封装到了Insert、Select、Update、Delete当中,所以这个执行器的接口方法反而是很是的简单,正由于它太简单了,所以根本就不须要介绍。仅仅要说明的是,当Select的时候,须要指定返回的类型,以便于告诉DslSession要返回的类型是什么。

Q&A

Q:是否是支持复杂的SQL?

A:必须支持,不论是Union,子查询,各类链接均可以支持

Q:是否是支持分页?

A:必须支持,不论是拼SQL语句分页的仍是SQL默认就支持分页的,均可以支持

Q:你这个SQL条件一路写下来,是否是须要全部的条件都必须存在?

A:不用,对于没有给值的条件,框架会自动忽略此条件,因此你只要写一个大而全的就能够了。

Q:是否是支持数据库中的函数?

A:必须支持,全部的函数均可以使用,只是若是写了与某种数据库相关的函数,跨数据库时将再也不有兼容性。

Q:是否是支持多表关联查询?

A:必须支持,不论是多表联合查询仍是子查询啥的,全都支持。

Q:有啥不支持的不?

好像没有啥不支持的,只有写得漂亮不漂亮的,没有支持不支持的。因为支持自已编写SQL片段,所以理论上你能够用SQL片段完成全部的事情,只是看起来不够漂亮而已。

应用实践

支持类编写

使用Tiny元数据开发

若是使用Tiny元数据管理数据表,那么只要在工具中以下操做,便可自动生成POJO、表定义、及Dao层代码实现:

也就是只要选中表定义文件,选择右键->TinyStudio->生成DSL JAVA类,就能够自动生成Dao层的全部代码,若是须要能够对生成的类进行修改或扩展,可是通常状况下都足够使用了。

自行编写或生成

若是没有使用Tiny的元数据,那么能够本身写个工具类来生成这几个类,也能够手工编写,也能够分分钟编写出来。

DAO编写注意事项

import static org.tinygroup.tinysqldsl.CustomTable.CUSTOM;
import static org.tinygroup.tinysqldsl.Delete.delete;
import static org.tinygroup.tinysqldsl.Insert.insertInto;
import static org.tinygroup.tinysqldsl.Select.selectFrom;
import static org.tinygroup.tinysqldsl.base.StatementSqlBuilder.and;
import static org.tinygroup.tinysqldsl.Update.update;



这里用到一个技巧,就是经过静态引入这些要用到的语句或表定义,这样才能够方便的编写DSL格式的语句。

优缺点对比

任意一个方案都有它的优势,也有它的缺点,TinySqlDsl也不例外,这里简单的分析一下,若是不全面,请同窗们下面补充,先谢谢了。

优势

  1. 熟悉SQL的同窗,上手很是方便,能够说熟悉SQL的同窗,能够很是快的上手,甚至不会Java均可以快速编写
  2. 即时提示效果很是好,全部的IDE都提供的语法提示,使得编写SQL时,对于表结构没必要再记得一清二楚,第一编写速度快许多,第二不用担忧拼写错误而花费大量的调试时间
  3. SQL的构建和Java的处理一体化完成,开发过程没必要两个部分先分开再分离
  4. 完美的解决动态SQL方面的问题,不须要复杂的配置,不须要复杂的处理,一切浑然天成
  5. 像写SQL同样写Java数据库业务代码

缺点

  1. 这种方式毕竟和写SQL仍是有一点区别,须要花一点时间熟悉
  2. 更多的亲们在下面补充

总结

目前,咱们内部进行了试用,总体运行效果良好,后面准备主力推这种方式。

关心代码的同窗,能够查看下面的URL:http://git.oschina.net/tinyframework/tiny/tree/master/db/org.tinygroup.tinysqldsl

亲,你有什么意见、建议,请告诉咱们吧!

相关文章
相关标签/搜索