#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.txt
和SetterGeneratorForIDEA.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
,然后用它初始化JPQLDataAccess
和Query
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,在HSQLDB
和MySQL
中测试通过。
如果你下载了源码,则可以直接运行测试用例,
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提供了JPQL
和Criteria
两种查询方式,但是Criteria方式限制很大,所以采用JPQL查询方式作实现。
关系型数据库可以进行join,group by,having,然而daf4j-api中并没有这些元素。这起源于我曾经针对SQL简化的思考。
也就是说,大部分情况下,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
的$eq
或like
。
(like表示从该资源下的子资源中寻找)
select
子句只支持count
update
子句支持location
和buffer
的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
模块分为以下几个主要类/接口
详情参考文档以及JPQLDataSource实现
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型