Spring Cloud和远程关联
远程关联是Jimmer对微服务架构的首次思考。
通过垂直切分,实体类型可以被划分到不同的微服务,也可以说被划分到不同的数据库中。隶属于不同的微服务实体类型之间关联 (包括一对一、多对一、一对多和多对多) 就是远程关联。
Jimmer自动实现远程关联数据的自动查询。
即,如果对象抓取器中包含任意深度的远程关联属性,就会跨越多个微服务查询数据的不同部分,最后将不同部分的查询结果合并成对象抓取器所期望的数据结构作为一个整体返回。无需开发人员自己实现远程查询和数据拼接。
数据库
在本文中,各实体如此划分
-
BOOK_AUTHOR_MAPPING
作为多对多关联中间表,既可以划分给book-service
服务,也可以划分给author-service
服务。本例将之划分给book-service
服务,原因会在后问阐述。 -
实线箭头表示同一个数据库中的外键引用,真伪可以由开发人员自由选择。
-
虚线箭头表示跨越数据库边界的外键引用,只能是伪外键。
为了简化讨论,三个服务的数据库初始化脚本如下
语言 | 例子 | 服务(子项目) | 数据库初始化SQL |
---|---|---|---|
Java | jimmer-examples/java/jimmer-cloud | store-service | jimmer-cloud/store-service/src/main/resources/store.sql |
book-service | jimmer-cloud/book-service/src/main/resources/book.sql | ||
author-service | jimmer-cloud/author-service/src/main/resources/author.sql | ||
Kotlin | jimmer-examples/kotlin/jimmer-cloud-kt | store-service | jimmer-cloud-kt/store-service/src/main/resources/store.sql |
book-service | jimmer-cloud-kt/book-service/src/main/resources/book.sql | ||
author-service | jimmer-cloud-kt/author-service/src/main/resources/author.sql |
附带的例子jimmer-examples/java/jimmer-cloud 或jimmer-examples/kotlin/jimmer-cloud-kt 使用H2,各服务启动后会自动初始化各自的数据库。
实体定义
在一个独立的子项目中 (附带例子为 jimmer-examples/java/jimmer-cloud/model或 jimmer-examples/kotlin/jimmer-cloud-kt/model ),定义所有实体类型。
这些实体类型构成了全局模型,实体类型隶属于不同的微服务。隶属于不同微服务中的实体之间构成了远程关联,其实可以理解成定义在不同微服务之间的实体彼此交互契约。
- Java
- Kotlin
@Entity(microServiceName = "store-service")
public interface BookStore {
@OneToMany(mappedBy = "store")
List<Book> books();
...省略其他代码...
}
@Entity(microServiceName = "store-service")
interface BookStore {
@OneToMany(mappedBy = "store")
val books: List<Book>
...省略其他代码...
}
- Java
- Kotlin
@Entity(microServiceName = "book-service")
public interface Book {
@Nullable
@ManyToOne
BookStore store();
@ManyToMany
List<Author> authors();
...省略其他代码...
}
@Entity(microServiceName = "book-service")
interface Book {
@ManyToOne
val store: BookStore?
@ManyToMany
val authors: List<Author>
...省略其他代码...
}
- Java
- Kotlin
@Entity(microServiceName = "author-service")
public interface Book {
@ManyToMany(mappedBy = "authors")
List<Book> books();
...省略其他代码...
}
@Entity(microServiceName = "author-service")
interface Author {
@ManyToMany(mappedBy = "authors")
val books: List<Book>
...省略其他代码...
}
-
实体类的
@Entity
注解的microServiceName
被指定,这样,说明每个实体属于那个微服务,即属于那个数据库。在本例中,实体类型
BookStore
、Book
和Author
分别隶属于为服务store-service
、book-service
和author-service
。这里的微服务,就是注册中心中每个服务的名称,对于spring-cloud而言,就是全局配置
spring.application.name
。 -
隶属于不同微服务中的实体之间的关联,就是远程关联。
-
BookStore.books
和Book.store
:BookStore
隶属于store-service
,而Book
隶属于book-service
-
Book.authors
和Author.books
:Book
隶属于book-service
,而Author
隶属于author-service
-
-
多对一关联
Book.store
必须可null
,因为远程关联表示其外键必然引用另外一个数据库中的数据,也就是说必然为伪外键。而Jimmer中伪外键所对应的字段必须可null
。请参考真假外键
-
对于书籍和作者之间的双向多对多关联而言,
Book.authors
是主动方,而Author.books
是从动方 (@ManyToMany
的mappedBy
被配置)。信息Jimmer规定远程关联的中间表必须隶属于主动方实体所属的微服务。
这里回答了前文留下的问题,为何在本例中中间表
BOOK_AUTHOR_MAPPING
定义在微服务book-service
的数据库中,而不定义在author-service
的数据库中。
注册中心
微服务应用需要一个注册中心,这是微服务架构的基本特征,本文无需做任何阐述。
附带例子中,为jimmer-examples/java/jimmer-cloud/registry-center 或jimmer-examples/kotlin/jimmer-cloud-kt/registry-center
实现各服务
由于微服务store-service
、book-service
和author-service
的实现方式高度雷同,所以本文档仅讲解如何实现store-service
。
虽然本文只讲解store-service
,但要运行起来体验远程关联的效果,需要把三个服务都实现完。
完整的代码可参见附带的例子jimmer-examples/java/jimmer-cloud 或jimmer-examples/kotlin/jimmer-cloud-kt。
让Jimmer支持微服务
任何一个微服务,都必须让Jimmer支持微服务。有两种做法:
-
使用Spring Boot Starter
修改spring配置文件
application.yml
(或application.properties
)spring:
application:
name: store-service
jimmer:
micro-service-name: ${spring.application.name}
...省略其他配置...
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7000/eureka/这里,"store-service"既作为当前服务在spring-cloud注册中心的名称,又作为Jimmer的微服务配置。
-
使用底层API时
- Java
- Kotlin
JSqlClient sqlClient = JSqlClient
.newBuilder()
.setMicroServiceName("...")
...省略其他配置...
.build();val sqlClient = newKSqlClient {
setMicroServiceName("...")
...省略其他配置...
}
一旦配置了jimmer.micro-service-name
, Jimmer就明白了哪些实体类型隶属于当前服务 (这里是BookStore
)。
-
使用Jimmer的ORM API操作当前微服务的实体类型,将会正确执行,如同之前所有文档所讲那样。
-
使用Jimmer的ORM API操作其他微服务的实体类型,将会抛出异常。
只要每个服务都保证spring.application.name
和jimmer.micro-service-name
相同,Jimmer就会自动实现远程关联数据的自动查询,无需开发人员编写任何代码。