避免对Hibernate / Spring JPA的原始痴迷

vx6bjr1n  于 2024-01-09  发布在  Spring
关注(0)|答案(1)|浏览(292)

在我们的Sping Boot 3(Kotlin)项目中,我们应用了域驱动设计,并希望在实体和存储库中avoid primitive obsession(尤其是ID,通常是类型为x1的,很容易混淆)。使用@Embeddable / @EmbeddedId将原始ID类型 Package 在一个类中是个好主意吗?像这样:

  1. @Embeddable
  2. data class OrderId(var orderId: String = "") : Serializable
  3. @Entity
  4. data class Order(
  5. @EmbeddedId
  6. var id: OrderId = OrderId(),
  7. )

字符串
(We我们只见过这种方法用于复合键。)或者其他一些机制是推荐的方法吗?我们担心微妙的陷阱,例如,它可能不完全兼容JPQL原生查询。

0tdrvxhp

0tdrvxhp1#

我的项目使用了一个自定义的UUID(https://github.com/ulid/spec的Kotlin派生),我们使用它作为所有实体的标识符。实际上,我们并没有为每个实体使用不同的UUID,但这很容易实现。
我们不使用@EmbeddedId,而只是配置到Spring Data序列化过程中的一个东西。
这意味着实体类在正常情况下不需要注解任何东西:

  1. data class Channel(
  2. val id: RULID,
  3. ...
  4. }

字符串
因为,至少在Mongo中,id是预期的主键(当它到达Mongo DB时被翻译为_id)。
下面是相关的代码片段(假设RULID是我们的自定义ID)-正如您所看到的,我们在数据库端将其序列化为String。

  1. @org.springframework.data.convert.ReadingConverter
  2. class DBObjectToRULIDConverter : Converter<String, RULID> {
  3. override fun convert(source: String): RULID {
  4. if (!RULID.isValid(source)) {
  5. return RULID.INVALID
  6. }
  7. return RULID.from(source)
  8. }
  9. }
  10. @org.springframework.data.convert.WritingConverter
  11. class RULIDToDBObjectConverter : Converter<RULID, String> {
  12. override fun convert(source: RULID): String {
  13. return source.toString()
  14. }
  15. }


考虑到我们使用的是Mongo,我们这样声明转换器:

  1. @Configuration
  2. @EnableMongoRepositories(basePackages = ["xxxxxxx.yyyyyy"])
  3. class MongoConfig {
  4. @Bean
  5. fun customConversions(): MongoCustomConversions {
  6. return MongoCustomConversions(
  7. listOf(
  8. DBObjectToRULIDConverter(),
  9. RULIDToDBObjectConverter(),
  10. // others...
  11. )
  12. )
  13. }
  14. }


Spring Repository接口魔术在许多情况下无需任何配置即可工作:

  1. @Repository
  2. interface ProfileRepository : MongoRepository<Profile, RULID>


没有任何额外的东西,因此findById(id: RULID): Profile无需进一步配置即可工作。
和自定义查询,再次工作,没有任何进一步的序列化处理:

  1. @Query("{'member._id': ?0}")
  2. fun findChannelsByUserId(memberId: RULID?): List<Channel>


或编写较低级别的查询语法:

  1. val criteria = Criteria.where("_id").`is`(channel.id)

展开查看全部

相关问题