您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
像写SQL一样编写Java数据应用
 
 作者:  博兔   来源: CSDN  火龙果软件 发布于 2015-9-29
   次浏览      
 

摘要:做数据库,可以有N种方案,但不管哪种方案,其优点和缺点往往也是连在一起的。究其原因,是因为SQL和Java之间是割裂的,如果封装不到位,做Java的人太难使用;如果封装得太多,在做一些用复杂SQL的时候又非常麻烦。

话说企业应用,一般离不开数据库。要做数据库,可以有N种方案,比如:直接采用JDBC层自己封装下使用的,采用一些框架的,如:iBatis、Hiberate、Spring JDBC Template等等,这些方案也都在各自的领域展示了自己的特点,解决了相当部分的技术问题,并取得了相当好的应用效果。但是,不管是哪种方案,其优点和缺点往往也是连在一起的。究其原因,是因为SQL和Java编程之间是割裂的,如果封装得不到位,做Java的人太难使用;如果封装得太多,在做一些用复杂SQL的时候又非常麻烦。比如:Hibernate就采用了封装HQL的方式来解决这方面的问题。iBatis对于SQL支持比较好,但是又会有一些割裂感,同时在解决时还要引入动态SQL来解决需要根据一些运行时条件来处理的问题,一定程度上又增加了使用的复杂度。

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

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

为了解决这个问题,我首先查阅了一些现成的解决方案,但是有的不是开源的,有的支持的还不是非常到位。因此,我们决定尝试着写一下,写了半天时间看了看效果,感觉还不错。为了帮助更多的朋友解决这个问题,同时体现开源精神,我决定正式花时间来编写一个开源解决方案——TinySqlDsl。

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

一、解决方案

1. 实现

首先来看看看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。

2. 代码说明

每个数据表都要有两个类进行映射,一个是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"),使之与数据库的表名进行映射。

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

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

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

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

3. 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要返回的类型是什么。

二、应用实践

1. 支持类编写

(1)使用Tiny元数据开发

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

也就是只要选中表定义文件,选择右键->TinyStudio->生成DSL JAVA类,就可以自动生成Dao层的所有代码,如果需要可以对生成的类进行修改或扩展,但是一般情况下都足够使用了。

(2)自行编写或生成

如果没有使用Tiny的元数据,那么可以自己写个工具类来生成这几个类,也可以手工编写,也可以分分钟编写出来。

2. DAO编写注意事项

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

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;

三、小结

1. 优缺点对比

任意一个方案都有它的优点,也有它的缺点,TinySqlDsl也不例外。

简单分析如下:

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

2. 缺点。这种方式毕竟和写SQL还是有一点区别,需要花一点时间熟悉。

Q&A环节:

(1)是不是支持复杂的SQL?

答:必须支持,不管是Union,子查询,各种连接都可以支持

(2)是不是支持分页?

答:必须支持,不管是拼SQL语句分页的还是SQL默认就支持分页的,都可以支持

(3)你这个SQL条件一路写下来,是不是需要所有的条件都必须存在?

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

(4)是不是支持数据库中的函数?

答:必须支持,所有的函数都可以使用,只是如果写了与某种数据库相关的函数,跨数据库时将不再有兼容性。

(5)是不是支持多表关联查询?

答:必须支持,不管是多表联合查询还是子查询啥的,全都支持。

(6)有啥不支持的不?

答:好像没有啥不支持的,只有写得漂亮不漂亮的,没有支持不支持的。由于支持自已编写SQL片断,因此理论上你可以用SQL片断完成所有的事情,只是看起来不够漂亮而已。

(7)确实很完美的解决了SQL和AVA之间的割裂。但是对于不用关系型数据库做存储的场景,DSL能否处理?还有现在DAO有了, 怎么保证事务呢

答:DSL是领域语意语言,不同场景不同扩展即可。事务可以手工控制也可spring托管。

   
次浏览       
相关文章

Java微服务新生代之Nacos
深入理解Java中的容器
Java容器详解
Java代码质量检查工具及使用案例
相关文档

Java性能优化
Spring框架
SSM框架简单简绍
从零开始学java编程经典
相关课程

高性能Java编程与系统性能优化
JavaEE架构、 设计模式及性能调优
Java编程基础到应用开发
JAVA虚拟机原理剖析
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

Java 中的中文编码问题
Java基础知识的三十个经典问答
玩转 Java Web 应用开发
使用Spring更好地处理Struts
用Eclipse开发iPhone Web应用
插件系统框架分析
更多...   


Struts+Spring+Hibernate
基于J2EE的Web 2.0应用开发
J2EE设计模式和性能调优
Java EE 5企业级架构设计
Java单元测试方法与技术
Java编程方法与技术


Struts+Spring+Hibernate/EJB+性能优化
华夏基金 ActiveMQ 原理与管理
某民航公司 Java基础编程到应用开发
某风电公司 Java 应用开发平台与迁移
日照港 J2EE应用开发技术框架与实践
某跨国公司 工作流管理JBPM
东方航空公司 高级J2EE及其前沿技术
更多...