无法理解Fetcher.rel逻辑,附组织与事件Scala案例类定义
Fetcher.rel with Your Scala Domain Models Got it, let's break down how to work with Fetcher.rel using your Organization/Event relationship. First, let's clarify what Fetcher.rel does at its core: it's a tool to define batch-fetching logic for related entities, solving the dreaded N+1 query problem by grouping all required related entity IDs and fetching them in one go instead of querying individually.
I'll use examples aligned with common Scala libraries (like ZIO Query, where Fetcher is widely used) but the core logic translates to any framework with similar fetcher abstractions.
1. First: Define Base Fetchers for Individual Entities
Before creating relationships, you need basic fetchers that can retrieve single entities by their IDs in bulk. These are the building blocks for Fetcher.rel.
import zio._ import zio.query._ import java.time.LocalDateTime // Your existing case classes case class OrganizationId(value: Long) extends AnyVal case class Organization(id: OrganizationId, name: String, iban: Option[String], bic: Option[String], updatedAt: LocalDateTime, insertedAt: LocalDateTime) case class EventId(value: Long) extends AnyVal case class Event(id: EventId, organizationId: OrganizationId, name: String, description: Option[String], updatedAt: LocalDateTime, insertedAt: LocalDateTime) // Fetcher to batch-fetch Organizations by their IDs val organizationById: Fetcher[OrganizationId, Organization] = Fetcher((ids: Set[OrganizationId]) => // Replace this with your actual database query logic ZIO.succeed( ids.map(id => id -> Organization( id, s"Organization ${id.value}", Some(s"DE89370400440532013000"), Some("COBADEFFXXX"), LocalDateTime.now(), LocalDateTime.now() ) ).toMap ) ) // Fetcher to batch-fetch Events by their IDs val eventById: Fetcher[EventId, Event] = Fetcher((ids: Set[EventId]) => // Replace this with your actual database query logic ZIO.succeed( ids.map(id => id -> Event( id, OrganizationId(id.value / 10), // Example: map event ID to org ID s"Event ${id.value}", Some("A sample event description"), LocalDateTime.now(), LocalDateTime.now() ) ).toMap ) )
2. Use Fetcher.rel to Define Event → Organization Relationship
Since every Event has an organizationId, we can use Fetcher.rel to link Events to their parent Organizations. This fetcher will automatically collect all unique organizationIds from a set of Events, batch-fetch the corresponding Organizations, and map them back to the Events.
// Define a fetcher that gets the Organization for a given Event val eventToOrganization: Fetcher[Event, Organization] = Fetcher.rel( sourceFetcher = eventById, // Fetcher for the source entity (Event) targetFetcher = organizationById, // Fetcher for the related entity (Organization) getId = (event: Event) => event.organizationId // Extract the related ID from the source )
How to Use This Relationship Fetcher
Here's how to fetch a set of Events and their corresponding Organizations in one optimized query:
val fetchEventsWithOrgs: ZIO[Any, Nothing, Map[EventId, (Event, Organization)]] = for { // First fetch the Events we care about events <- ZIOQuery.run(eventById.fetch(Set(EventId(1), EventId(11), EventId(21)))) // Batch-fetch all related Organizations in one go relatedOrgs <- ZIOQuery.run(eventToOrganization.fetch(events.values.toSet)) // Pair each Event with its Organization pairedResults = events.map { case (eventId, event) => eventId -> (event, relatedOrgs(event)) } } yield pairedResults
3. Reverse Relationship: Organization → Events (One-to-Many)
Fetcher.rel also works for one-to-many relationships. First, define a fetcher that can retrieve all Events for a set of Organization IDs, then link it to the Organization fetcher.
// Fetcher to batch-fetch all Events for a set of Organization IDs val eventsByOrganizationId: Fetcher[OrganizationId, List[Event]] = Fetcher((orgIds: Set[OrganizationId]) => // Replace with your actual database query (e.g., SELECT * FROM events WHERE organization_id IN (...) grouped by org ID) ZIO.succeed( orgIds.map { orgId => orgId -> List( Event(EventId(orgId.value * 10 + 1), orgId, s"Event ${orgId.value}-1", None, LocalDateTime.now(), LocalDateTime.now()), Event(EventId(orgId.value * 10 + 2), orgId, s"Event ${orgId.value}-2", None, LocalDateTime.now(), LocalDateTime.now()) ) }.toMap ) ) // Define a fetcher that gets all Events for a given Organization val organizationToEvents: Fetcher[Organization, List[Event]] = Fetcher.rel( sourceFetcher = organizationById, targetFetcher = eventsByOrganizationId, getId = (org: Organization) => org.id // Extract the Organization's ID to find its Events )
Key Takeaways
Fetcher.relis all about composing fetchers for related entities to avoid N+1 queries.- It handles the heavy lifting of:
- Collecting all unique related IDs from a set of source entities
- Batch-fetching the related entities
- Mapping the results back to the original source entities
- The pattern works the same for one-to-one, one-to-many, and even many-to-many relationships (you'd just adjust the target fetcher to return collections or mapped pairs).
内容的提问来源于stack exchange,提问作者mravey

