14 Star 18 Fork 5

wkgcass / DAF4J

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

#Data Access Facade for Java (DAF4J) 这是一个针对数据访问的统一接口,提供简便的,带类型检查的数据访问DSL

运行于 Java 1.6 以及更高版本
引入daf4j-api.jar和您需要的DataAccess实现即可
DAF4j针对Scala提供了的简化操作

update.md中查看更新日志

###使用 方式1: 在git@osc上下载附件并引入包,以及根据说明引入依赖项(daf4j-api.jar本身不需要引入依赖,但DataAccess实现会需要,后文有详细说明)
方式2: 使用maven引入

<dependencies>
	<dependency>
		<groupId>net.cassite</groupId>
		<artifactId>daf4j-api</artifactId>
		<version>0.1.1-RELEASE</version>
	</dependency>
</dependencies>

###使用预览

Java | Stream风格

query
	.from(user)
	.stream()
	.filter(user.age.$gt(18).and(user.name.$ne("cass")))
	.filter(role.name.$eq("admin"))
	.sorted(user.id.desc())
	.limit(10)
.list();

// 查询年龄大于18,姓名不为'cass',角色等于'admin'的用户,根据用户id降序排列,并取前10条记录

Scala | SQL风格

query from user where user.age > 18 & user.name <> "cass" & role.name === "admin" param (orderBy(user.id.desc) top 10) list

// 功能与上相同

###开发这个类库的原因

DAF4J用于简化数据访问。
三层架构中数据层的结构会很大程度上依赖于业务逻辑。很多时候业务层仅仅做一些数据校验和数据类型的转换,剩下就是直接的调用数据层,而数据层有时候也会针对业务层方法写一些“专用”方法。

我认为数据层之所以依赖于业务的根源,是查询/修改条件无法完整的从业务中分离出去。

即使是类似Spring Data JPA或者JPA Criteria,对于条件的处理也略显笨重(小于(字段, 值)而不是字段.小于(值),而且代码量略高)。

我想,既然JPA支持从方法注入值,那么为何不转变一下思路,把字段替换成封装好,支持各种条件和表达式的类型,而方法签名不变。这样既可以使用已有框架,又可以更轻松的调用数据。

设计了一番后便开始了开发。

##使用方式

此处给出一个前瞻,详细使用方式可在Wiki页查看
此外,在Wiki页提供一篇了教学,在一个简单的RBAC的模型的情景下使用DAF4J

使用需要引入daf4j-api.jar(或者使用maven)

本例以daf4j-ds-jpa.jar中的JPQLDataAccess实现为例
我们会书写一个实体类,现在把实现方式作微小的调整:

我针对IDEA写了一个getter和setter的生成器,来完成DAF4J的JavaBean生成,生成代码见仓库根目录GetterGeneratorForIDEA.txtSetterGeneratorForIDEA.txt

@Entity
class User{
	public final XInt id = new XInt(this);
	public final XString name = new XString(this);
	public final XInt age = new XInt(this);
		
	@Id
	public Integer getId(){ return id.get(); }
	public String getName(){ return name.get(); }
	public Integer getAge(){ return age.get(); }
		
	public void setId(Integer id){ DataUtils.set(this.id, id); }
	public void setName(String name){ DataUtils.set(this.name, name); }
	public void setAge(Integer age){ DataUtils.set(this.age, age); }
}

为了使用JPA的功能,还需要获取一个EntityManager,然后用它初始化JPQLDataAccessQuery

Query query=new Query(new JPQLDataAccess(entityManager));

接着就可以开始使用了:

User user=new User();
	
// 列出所有年龄大于18岁的用户
query
	.from(user)
	.where(user.age.$gt(18))
.list();
	
// 以List<Map<String,Object>>的形式列出用户id和用户名
// Map中的键包括 User.id 和 user_name
// 结果根据年龄降序排序
query
	.from(user)
	.where(user.age.$gt(18))
	.param(
		new QueryParameter().orderBy(user.age.desc())
	)
.select(
	new Focus()
	.focus(user.id) // 别名为类型简称.字段名
	.focus(user.name, "user_name")
);
	
// 计算所有用户年龄平均值
query
	.from(user)
	.where(null)
.avg(user.age);

// 将所有用户的年龄+1
query
	.from(user)
	.where(null)
.update(
	user.age.as(user.age.add(1))
);

// 删除所有用户
query
	.from(user)
	.where(null)
.remove();

上述查询风格是sql风格,有时候并不操作关系型数据库,或者不喜欢类sql的语法,所以此处还提供了stream风格的查询语句

// 列出所有年龄大于18岁的用户
query
	.from(user).stream()
	.filter(user.age.$gt(18))
.list();
	
// 以List<Map<String,Object>>的形式列出用户id和用户名
// Map中的键包括 User.id 和 user_name
// 结果根据年龄降序排序
query
	.from(user).stream()
	.filter(user.age.$gt(18))
	.sorted(user.age.desc())
	.map(
		new Focus()
			.focus(user.id)
			.focus(user.name, "user_name")
	)
.list();

// 计算所有用户年龄平均值
query
	.from(user).stream()
	.mapToInt(user.age)
.average();

以上查询都是带类型检查的。

##已有的DataAccess实现 现在开发了针对JPA标准的DataAccess实现,以及对于资源的抽象实现。

针对JPA的实现在测试环境中使用Hibernate-EntityManager-4.0,在HSQLDBMySQL中测试通过。

如果你下载了源码,则可以直接运行测试用例,HSQLDB将在内存运行

依赖于

  • daf4j-api:0.1.1-RELEASE
  • slf4j-api:1.7.12
  • hibernate-jpa-2.0-api:1.0.1.Final

当然,日志输出还需要额外的日志系统,比如slf4j-simple/log4j+slf4j-log4j等,JPA实现也需要引入依赖。

您可以自行添加依赖或者使用maven

<dependencies>
	<dependency>
		<groupId>net.cassite</groupId>
		<artifactId>daf4j-ds-jpa</artifactId>
		<version>0.1.1-RELEASE</version>
	</dependency>
</dependencies>

###JPQLDataAccess/JPQLDataSource JPA是 JavaEE 体系中的一部分,是数据持久化的标准。所以对其进行了实现。JPA提供了JPQLCriteria两种查询方式,但是Criteria方式限制很大,所以采用JPQL查询方式作实现。

关系型数据库可以进行join,group by,having,然而daf4j-api中并没有这些元素。这起源于我曾经针对SQL简化的思考

  1. SQL中,若出现聚合函数,那么出现在select子句中却没有出现在聚合函数中的字段必需出现在group by子句中。
  2. 若聚合函数作为条件的一部分,那么必需放在having子句中。

也就是说,大部分情况下,group by和having可以推导出来。

而使用JPA时,join关系已经在实体中定义好了。即使写JPQL时也只会这么写

select u from User u join u.roles r ...

完全可以自动生成。

不过此处还有一些使用规则。

例如User(用户)和Role(角色),如果需要查询用户,也需要以角色为依据。比如说:查询出所有角色为admin的用户,那么你必需这么写。

User user=new User();
Role role=new Role();
user.getRoles().add(role);
	
query
	.from(user)
	.stream()
	.filter(role.name.$eq("admin"))
.list();

换句话说,你需要指定join的字段。

如果是多对一关系,则需要通过setter填入。

####ResourceDataAccess 在0.1.1版本中,api提供了一个新的DataAccess实现。对于所有的“资源”进行抽象。

使用Resource对象表示资源,Source接口实现表示来源。

API中自带本地文件源LocalFileSource实现,使用方式类似如下:

Query query=new Query(new ResourceDataAccess(new LocalFileSource));
Resource r = new Resource();
query
	.from(r)
	.where(r.location.$like("/Volumes/PROJECTS/openSource/DAF4J"))
.list();

where子句支持$eq,$ne,$lt,$gt,$le,$ge,like。其中必须出现对location$eqlike
(like表示从该资源下的子资源中寻找)

select子句只支持count

update子句支持locationbuffer的set。其中buffer需要一个InputStream。location需要字符串或者concat

若事务开启,则会在commit和rollback时把使用过的InputStream关闭。

##Scala特殊用法 这里不作过多叙述,如下代码一目了然:

import entity._

query from entity where age > 18 & id <> 1 param (orderBy(name.desc) top 1) list()

query from entity where age > 18 & id <> 1 param (orderBy(name.desc) top 1) select id ~(name, "a")

query from entity stream() filter age > 18 & id <> 1 sorted name.desc limit 5 list()

query from entity stream() filter age > 18 & id <> 1 sorted name.desc limit 5 map id ~(name, "a") list()

##DataSource DataSource用于将DataAccess中的逻辑分离,以减少编码失误,也有利于单元测试的进行。

DataSource模块分为以下几个主要类/接口

  • DataSource 入口,规定模块中的其它实现
  • ConditionParser 条件解释器
  • ExpressionParser 表达式解释器
  • AndOrParser 与,或解释器
  • AroundParser 全局解释器
  • QueryParameterParser 查询参数解释器
  • EntityDataParser 实体字段解释器(也包括子查询的解释)
  • UpdateEntryParser 更新目标解释器

详情参考文档以及JPQLDataSource实现

The MIT License (MIT) Copyright (c) 2015 wkgcass Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

Data Access Facade For Java,更简单的持久层结构,为数据访问提供统一接口,以及更简单可读的DSL 展开 收起
Java
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/wkgcass/DAF4J.git
git@gitee.com:wkgcass/DAF4J.git
wkgcass
DAF4J
DAF4J
master

搜索帮助