JMolecules究竟是啥,竟然Spring Data JDBC都为它单独给了example
文章目录
前两天在选择Demo工程的框架的时候,选了从未用过的Spring Data JDBC。在官方给出的Examples里,发现有个单独的模块jmolecules-example。
我感觉挺惊讶的,Spring官方竟然给一个在Github上star不超过400(截止到2021-4-22)的项目给了单独的使用示例。
但是,我看到这个项目的第一眼就star上了。用注解和接口表达领域驱动设计中的概念,实在是太coooool了。
所以,我就决定将这个示例jmolecules-example,clone下来研究一下。
为啥测试用例通过不了?
所有Spring Data示例在一个repository里简直太折磨人了,clone下来下载jar包都得需要好长时间。
下载完之后,运行测试用例就报错了。
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ’example.springdata.jdbc.jmolecules.customer.Customers’ available
一脸懵逼,官方示例不应该有问题。
但是,这个错误很明显,没找到Customers这个Repository。
让我们来看看Customers的代码。
| |
用过Spring Data JPA的都知道,我们自定义一个接口然后继承org.springframework.data.repository.Repository就行了。但是这里,继承的是org.jmolecules.ddd.types.Repository。所以上面的错误,肯定跟这个有关系。所以,就必须了解这个jMolecules了。
jMolecules
A set of libraries to help developers work with architectural concepts in Java. Goals:
- Express that a piece of code (package, class, method…) implements an architectural concept.
- Make it easy for the human reader to determine what kind of architectural concepts a given piece of code is.
- Allow tool integration (to do interesting stuff like generating persistence or static architecture analysis to check for validations of the architectural rules.)
从Github上的介绍以及源码可以看出来,jMolecules只是提供了一些标志性的接口和注解而已。
比如: 使用@ValueObject来标志领域驱动设计中的值对象
如果这个jMolecules只有标识作用,是不是显得没有那么实用?因此,还有另一个叫jmolecules-integrations的项目。
jMolecules — Technology integrations
正是这个项目,可以将jMolecules转换成Spring Data中的接口及注解。
从示例的pom.xml中可以看到就有这些依赖。
而重点就是下面这个。
| |
使用这个插件在target目录下生成class文件,测试用例就可以通过了。
生成的Customers代码如下:
| |
可以,看到生成的class文件里多了org.springframework.data.repository.Repository接口。
Spring Data JDBC示例分析
示例里,更多的还是关于Spring Data JDBC的内容,而jMolecules相当于起了一个辅助的作用。
下面,分析一下这个示例的代码。
| |
Customers和Orders很简单,分别是聚合根Customer和Order的Repository。
所以重点还是Customer和Order。
一对一
Customer是一个典型的一对一关系示例。
| |
| |
这里有Lombok的注解中的@PersistenceConstructor,其实意思就是在构造方法上加上这个@PersistenceConstructor注解,它的作用可以在Spring Data JDBC文档上了解到。
| |
虽然,代码里使用的是List,但是这里Customer和Address确实是一对一的。这里jMolecules通过AggregateRoot接口,让我们不再需要加上@Id注解(org.springframework.data.annotation.@Id)。
而且,对于Spring Data JDBC只需要@Id注解就够了,甚至不需要setter和getter方法。Spring Data不愧是贯彻领域驱动设计概念的框架。有时候,有些框架强制使用setter和getter方法就让我觉得很别扭。
Customer和Address的建表语句可就有点意思了。
| |
Address是一个值对象,它随customer产生也随之消亡。
我们不需要做任何操作,在customer保存的时候,会将customer的id保存到address的customer字段。
而customer_key,以我的理解对应的是customer中List的index。
customer保存后的数据如下:
| ID | FIRSTNAME | LASTNAME |
|---|---|---|
| 52eb4e05-4371-4009-a03e-2fbdc3a5963d | Carter | Matthews |
address的数据如下:
| STREET | CITY | ZIP_CODE | CUSTOMER | CUSTPMER_KEY |
|---|---|---|---|---|
| 41 Greystreet | Dreaming Tree | 2731 | 52eb4e05-4371-4009-a03e-2fbdc3a5963d | 0 |
一对多
Order与LineItem就是一对多的关系了。
| |
这其实跟一对一是非常类似的。唯一的点就是使用了@Table指定了表名和@Column指定了列名,而且是line_item表的列名。
| |
示例执行后my_order表的数据如下:
| ID | CUSTOMER |
|---|---|
| 1a02ef53-db71-47ef-8985-88f907800dc8 | 52eb4e05-4371-4009-a03e-2fbdc3a5963d |
示例执行后line_item表的数据如下:
| MY_ORDER_KEY | DESCRIPTION | FOO |
|---|---|---|
| 0 | Foo | 1a02ef53-db71-47ef-8985-88f907800dc8 |
| 1 | Bar | 1a02ef53-db71-47ef-8985-88f907800dc8 |
多对一
Customer与Order就是多对一。
上面的一对一,一对多。都是聚合根被删除了,其包含的值对象也应该被删除,这是合理的。但是Customer与Order都是聚合根。一个顾客可以有多个订单,订单删除了,顾客不能删除。
这种情况在Spring Data JDBC该怎么办呢。
在Spring官网的博客中有篇文章中讲到过处理方式。Spring Data JDBC, References, and Aggregates
对应于,这个示例,就是让Order持有Customer的ID,而非对象。
所以,这里jMolecules就发挥作用了。使用Association来封装这个ID。
如果是这样,Spring Data JDBC怎么知道jMolecules的封装,又该怎么解析Association呢?
那就是,JMolecules Spring integration的自定义转换器PrimitivesToIdentifierConverter和PrimitivesToAssociationConverter。在Spring Data JDBC的Custom Conversions有自定义转换器的介绍。
H2
示例中使用的H2数据库。所以除了只需要一个schema.sql外,不需要什么配置信息。
如果需要,查看H2数据库的数据。需要添加application.properties配置
| |
另外,需要加入web的依赖。
然后在项目启动的时候,可以看到访问路径为/h2-console。
类似下面的这种连接的url也可以看到
jdbc:h2:mem:0dbb7bce-452c-4d12-b9e3-1ca04479f6f5;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false
总结
不管是Spring Data JDBC, 还是jMolecules都是非常优秀的项目了。就连官方的给示例项目的很经典,可谓是以最少的代码表达出了你想知道的内容。
之前在还在网上看到有博客的作者说,Spring Data JDBC很鸡肋,我不清楚当时的作者为什么会有这样的言论,是不是真的了解过Spring Data JDBC。
我只想说,Spring Data JDBC, yyds(永远滴神)。