预赛
在使用任何开发工具之前,有许多事情需要完成:获取,安装,配置,获取依赖(以及解决依赖冲突)等。在Java世界中,Maven已经处理了对我们来说很多,当然,你可以通过Maven得到Hrorm。当然,您也可以下载jar文件并使用您希望的任何方式将其放入类路径中。
设置Hrorm最困难的部分是设置JDBC提供程序。这超出了本文的范围,因为它因数据库而异。但是一旦你完成了,并且可以java.sql.Connection
为你的数据存储创建一个,Hrorm就可以了。Hrorm是一个jar,它只需要在你的类路径中。Hrorm没有依赖关系:它只使用Java标准库。Hrorm不需要容器,也不需要配置,并且因为它使用JDBC接口和ANSI SQL,所以没有每个数据库的自定义。此外,Hrorm使用普通Java对象:它不要求对象模型包含任何Hrorm代码,注释或实现其任何接口。
示例模型
在本文的其余部分,我将讨论一个简单的实体模型。这里是Java,省略了getter和setter(虽然需要一些访问器,Hrorm不使用反射,尤其不使用反射sun.misc.Unsafe
):
class Product {
私人 长 身份 ;
私有 字符串 名称 ;
私有 ProductCategory 类别 ;
私人 BigDecimal 价格 ;
私人 长 sku ;
private boolean 停止 ;
private LocalDateTime firstAvailable ;
}
枚举 ProductCategory {
厨房,服装,电子,杂项
}
这是SQL模式。
create sequence products_sequence;
创造 餐桌产品(
id integer PRIMARY KEY,
姓名文字,
类别文字,
价格小数,
sku 整数,
停止布尔值,
first_available 时间戳
);
通常情况下这种类型的例子,它有点愚蠢和人为,但它将突出Hrorm的一些基本功能。
在Hrorm中定义实体
要使用Hrorm管理实体的持久性,我们首先创建一个DaoBuilder
。A DaoBuilder
使用流畅的声明性接口来描述实体的数据库模式和Java对象模型,从而允许Hrorm执行持久性任务。
我们需要的大部分映射都很简单,但有一个细节需要一些预备。因为Java对象模型包含自定义枚举类型并且数据库包含字符串,所以我们需要实现一个简单的接口,org.hrorm.Converter<CLASS, CODE>
它将处理与a String
和枚举类型之间的转换。实施是微不足道的,如下所示。
class CategoryConverter 实现 Converter < ProductCategory,String > {
@覆盖
来自(ProductCategory item)的public String {
返回 项目。toString();
}
@覆盖
public ProductCategory to(String aString){
返回 ProductCategory。valueOf(aString);
}
}
其他类型是直接支持的。在DaoBuilder
这个样子的。
DaoBuilder < 产品> productDaoBuilder = 新 DaoBuilder <>(“产品”,产品 :: 新)
。withPrimaryKey(“id”,“products_sequence”,Product :: getId,Product :: setId)
。withStringColumn(“name”,Product :: getName,Product :: setName)
。withConvertingStringColumn(“category”,Product :: getCategory,Product :: setCategory,new CategoryConverter())
。withBigDecimalColumn(“price”,Product :: getPrice,Product :: setPrice)
。withIntegerColumn(“sku”,Product :: getSku,Product :: setSku)
。withBooleanColumn(“已停止”,Product :: isDiscontinued,Product :: setDiscontinued)
。withLocalDateTimeColumn(“first_available”,Product :: getFirstAvailable,Product :: setFirstAvailable);
它DaoBuilder
由底层表的名称和Java对象的构造函数的函数引用构成(Hrorm还支持不带参数构造函数的不可变对象的机制,您可以在hrorm文档中阅读更多相关内容)。
下一行定义对象的主键,包括列的名称,将填充它的序列的名称,以及主键的getter和setter。其余行定义了哪些列名与Java对象上的getter和setter对应。必须使用正确的类型。在ProductCategory
还包括以上所示的转换器的一个实例。
使用Hrorm
这是Hrorm管理Product
对象持久性所需的所有设置。顾名思义,a DaoBuilder
用于构建实际Dao
对象。为此,我们需要另外一件事:一个java.sql.Connection
对象,由所使用的任何数据库的JDBC实现提供。到目前为止,我们一直在为Hrorm工作,但现在Hrorm将开始为我们工作。
连接 连接 = //不知怎的,我们从JDBC获得连接
Dao < 产品> productDao = productDaoBuilder。buildDao(连接);
我们的第一份工作是坚持产品的实例。我们创建Product
对象的新实例,而不设置主键id
字段。我们将其传递给该Dao.insert()
方法。insert方法将做几件事。
它将从
products_sequence
数据库中选择一个新值。它将在数据库中的预准备语句上运行SQL
INSERT INTO PRODUCTS (ID, NAME, ...) VALUES ( ... )
。它会将新主键的值设置到
Product
对象上。
产品 产品 = 新 产品();
产品。setName(“厨师刀”);
产品。setCategory(产品分类。厨房);
产品。setPrice(new BigDecimal(“99.95”));
产品。setSku(12345L);
产品。setDiscontinued(false);
产品。setFirstAvailable(LocalDateTime。的(2017年,6,15,0,0));
长 厨师知道 = dao。插入(产品);
连接。commit();
请注意,Hrorm不对何时提交负责。由于应用程序知道什么是事务,所以它由你决定,但Hrorm没有。
稍后,当产品开始销售时,我们可以加载它,设置新价格并发布更新。
产品 chefKnife = productDao。选择(chefKnifeId);
chefKnife。setPrice(new BigDecimal(“59.95”));
产品道。更新(chefKnife);
连接。commit();
Hrorm不执行任何状态跟踪或尝试发出优化的SQL。Dao.update()
调用该方法时,它将使用与对象匹配的主键更新数据库中记录的所有字段。
Hrorm还支持为特定记录delete
发出DELETE
SQL 的方法。
各种选择
插入,更新和删除记录的方法非常简单,几乎没有添加到上面说的内容。选择的情况有点复杂。select
当通过主键从数据库中提取单个记录时,我们已经看到了上面的一种方法。但是Hrorm支持各种选择机制。
首先,一种为某些模板对象选择匹配记录的方法。这里的想法是创建实体类的实例,填充一些值,然后指示Hrorm通过表列的某个子集查找匹配的记录。例如,如果我们想要找到所有已停产的电子产品,我们可以编写这样的代码。
Dao < 产品> productDao = productDaoBuilder。buildDao(连接);
产品 模板 = 新 产品();
模板。setCategory(产品分类。电子);
模板。setDiscontinued(true);
列出< 产品> 产品 = productDao。selectManyByColumns(模板,“类别”,“已停止”);
当您有一个想要匹配的对象实例时,这是一种有用的机制,但它不是Hrorm提供的最通用的方法。按列选择仅允许检查值的相等性。使用Hrorm的Where
对象可以编写更多通用查询。
假设我们想要找到价格低于100美元的所有“杂项”产品,并在2018年推出。使用Where
对象将允许我们构建我们需要的查询,如下所示。
列出< 产品> 产品 = productDao。选择(
新 在哪里(“类别”,操作员。EQUALS,产品分类,杂项。的toString())
。和(“价格”,操作员。LESS_THAN,新 的BigDecimal(“100.00” ))
。和(“first_available” ,操作员。GREATER_THAN_OR_EQUALS,LocalDateTime。的(2018,1,1,0,0))
。和(“first_available” ,操作员。LESS_THAN_OR_EQUALS,LocalDateTime。的(2018,12,31,23,59)));
Hrorm Where
对象是列上的(可能是嵌套的)谓词集合。使用org.hrorm.Operator
该类,我们描述了对给定值使用什么测试。使用or
或and
方法连接谓词。
上面显示的机制将始终导致构造一个等于结果集大小的对象列表。如果您希望运行查询并对结果进行计算而无需在一个大列表中实例化所有结果,则可以使用该Dao.foldingSelect()
方法。如果您不熟悉折叠,请考虑这些java.util.Stream.reduce()
方法。我们的想法是提供一个充当累加器的函数,它将依次传递结果集中的每个值,但只使用结果进行计算,而不是将其保留在内存中。
下面的例子将计算所有停产商品的所有价格的总和(不可否认,这个例子很荒谬,为什么你会这样做?但重点是以最简单的方式显示API)。
BigDecimal accumulationPrice = productDao。折叠选择(
新的 BigDecimal(0),
(cumulativeCost,产品)- > 累积成本。添加(产品。用getPrice()),
新 在哪里(“停产”,操作员。EQUALS,假));
上面的例子实际上是愚蠢的,但它也很愚蠢,因为它可以通过在SQL中运行一个函数来更简单地完成。
BigDecimal accumulationPrice = productDao。runBigDecimalFunction(
SqlFunction。SUM,
“价格”,
新 在哪里(“停产”,操作员。EQUALS,假));
您可以通过阅读Hrorm文档和Javadocs了解其他几个细节,但上面的示例可以让您对Hrorm提供的功能有所了解。在定义了DaoBuilder
大约8行代码之后,Hrorm为执行CRUD操作提供了一个不错的工具包,并且从不要求我们编写SQL或解析java.sql.ResultSet
。一切都以流利,富有表现力的成语完成。