• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

DAF4J: Data Access Facade For Java,更简单的持久层结构,为数据访问提供统一接口, ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

DAF4J

开源软件地址:

https://gitee.com/wkgcass/DAF4J

开源软件介绍:

#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

@Entityclass 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);// 将所有用户的年龄+1query	.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/JPQLDataSourceJPA是 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()

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

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

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

详情参考文档以及JPQLDataSource实现


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap