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

前言

话说企业应用,一般离不开数据库。要做数据库,可以有N种方案,比如:直接采用JDBC层自己封装下使用的,采用一些框架的,如:iBatis,Hiberate,Spring JDBC Template等等(这个太多了,因此不一一列举)的,这些方案也都在各自的领域展示了自己的特点,解决了相当部分的技术问题,并取得了相当好的应用效果。

但是不管是哪种方案,其优点和缺点往往也是连在一起的,究其原因是因为SQL和Java编程之间是割裂的,如果封装得不到位,做Java的人太难使用;如果封装得太多,在做一些用复杂SQL的时候又非常麻烦。比如:Hibernate就采用了封装HQL的方式来解决这方面的问题。iBatis对于SQL支持比较好,但是又会有一些割裂感,同时在解决时还要引入动态SQL来解决需要根据一些运行时条件来处理的问题,一定程度上又增加了使用的复杂度。

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

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

TinySqlDsl解决方案

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

我们常见的SQL语句有Select、Insert、Update、Delete,因此我们的方案中也实现了这几个语句的编写方式。

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

第一步:定义POJO

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

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;

    }

     

}

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

?


1

2

3

4

5

6

7

8

9

10

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类

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

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中定义数据库的表结构。

?


1

2

3

4

5

6

7

8

9

10

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")使之与数据库的表名进行映射。

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

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

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

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

DslSesssion

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

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编写注意事项

?


1

2

3

4

5

6

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

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

时间: 2024-11-03 19:06:10

像写SQL一样编写Java数据应用-TinySqlDsl的相关文章

java-mongodb写sql能查出正确数据,但是Java代码写就查不出

问题描述 mongodb写sql能查出正确数据,但是Java代码写就查不出 sql是这样写的 查出来的东西是对的 但是我用程序写的时候就查不出正确的数据 感觉这个columns.append("*", 0);写不写都没用

如何写SQL实现:在数据表中增加一列,该列的值是出库数的累加 (按制单日期升序累加)

问题描述 如何写SQL实现:在数据表中增加一列,该列的值是出库数的累加 (按制单日期升序累加) 如上图,在数据表中增加一列[累计出库数],累计出库数的值等于出库数的累加数(按制单日期升序累加),如何写SQL实现 解决方案 Oracle有现成的lead,lag函数 其他数据库看下面我的博客 sqlserver http://blog.csdn.net/danielinbiti/article/details/45308867 mysqlhttp://blog.csdn.net/danielinbi

Java程序员在写SQL程序时候常犯的10个错误

  Java程序员编程时需要混合面向对象思维和一般命令式编程的方法,能否完美的将两者结合起来完全得依靠编程人员的水准: 技能(任何人都能容易学会命令式编程) 模式(有些人用"模式-模式",举个例子,模式可以应用到任何地方,而且都可以归为某一类模式) 心境(首先,要写个好的面向对象程序是比命令式程序难的多,你得花费一些功夫) 但当Java程序员写SQL语句时,一切都不一样了.SQL是说明性语言而非面向对象或是命令式编程语言.在SQL中要写个查询语句是很简单的.但在Java里类似的语句却不

编程-Eclipse环境下编写Java程序,使用float类型的数据为什么无法进行算数运算?

问题描述 Eclipse环境下编写Java程序,使用float类型的数据为什么无法进行算数运算? float f=5+5.5; 或者 float f ; f= 1*5.5; 这两个均会报错,我就纳闷了.为什么这样不行?后来我又试了: int a= 5; float f ; f= a*5.5; 还是不行,报错的语句就是给f赋值的语句. 请问为什么?C++下亲测这三者均可运行,难道这是Java的特性吗?不可能啊,如果这种操作都会编译报错,那这float基本上就没用处了,这太不科学了. 为了避免一些网

怎用JAVA写语句向数据库插入数据呢?

问题描述 create table student(id int,name nvarchar(7),age nvarchar(5))怎用JAVA写语句向表里插入数据呢? 问题补充:执行后数据没插进数据库哦 解决方案 String user = "root"; //用户名 String pass = "root"; //密码看看你的用户名.密码对不对.解决方案二:drop table student ;create table student ( id int pri

omact-求助:编写java包连接SQL Server数据源

问题描述 求助:编写java包连接SQL Server数据源 代码如下 //数据库连接类 package cc.dbc; import java.sql.Connection; import java.sql.DriverManager; public class DatabaseConnection{ private static final String DBDRIVER="sun.jdbc.odbc.JdbcOdbcDriver"; private static final St

[Java]log4j写SQL Server数据库日志的统一写法

[Java]log4j写SQL Server数据库日志的统一写法 编写者 日期 关键词 郑昀@ultrapower 2005-7-19 Java log4j sqlserver   SQL Server 2000 Driver for JDBC Service Pack 3的下载 之前需要注意一点,sql server同样必须打上了sp3的补丁,然后才能与jdbc for sqlserver sp3连接.如果是"Error establishing socket"错误,很有可能是要在s

update-Update 数据表时自动求如何写SQL语句呢(使用SQL Sever)?(设计触发器或存储过程吧)

问题描述 Update 数据表时自动求如何写SQL语句呢(使用SQL Sever)?(设计触发器或存储过程吧) 我有一个学生考试信息表: 考号,姓名,语文成绩,数学成绩,英语成绩,文综成绩,总成绩 (PS:默认各科成绩,总成绩都为 0)在老师登分时只会登入各个科目的成绩,我使用的是SQL Sever数据库,当老师登入各科成绩时(使用Update),如何触发自动求和?_谢谢!_ 解决方案 create trigger trig_学生考试信息表 on 学生考试信息表 for insert as be

sqlserver中一条sql语句把插入数据的ID返回出来,oracle中sql语句要怎么写?

问题描述 sqlserver中一条sql语句把插入数据的ID返回出来,oracle中sql语句要怎么写? sqlserver中一条sql语句把插入数据的ID返回出来,oracle中sql语句要怎么写? 解决方案 oracle,sqlserver分页查询sql语句重复的数据只取一条,SQL语句如何写 解决方案二: select test.currval from dual 解决方案三: oracle里面不是差不多的吗