@CrudMapping#

com.palmyralabs.palmyra.base.annotations.CrudMapping

Binds a handler component to one or more URL paths and the entity type it operates on — Palmyra’s equivalent of Spring’s @RequestMapping for CRUD surfaces. Target: TYPE. Retention: RUNTIME.

Attributes#

Attribute Signature
mapping String[] mapping() — collection-level path; consumed by QueryHandler, CreateHandler, SaveHandler
secondaryMapping String secondaryMapping() default "" — single-record path (usually {id}-templated); consumed by UpdateHandler, DeleteHandler
type Class<?> type() — entity type handled
queryType Class<?> queryType() default Object.class — type used for query projections

Which handler hits which URL#

Palmyra routes HTTP methods to handler interfaces based on which mapping the request matched:

Handler HTTP method Matches
QueryHandler GET mapping (collection list / filter / paginate)
ReadHandler GET mapping
CreateHandler POST mapping
SaveHandler POST mapping (upsert by body keys)
UpdateHandler POST (or PUT) secondaryMapping (targets a single row by {id})
DeleteHandler DELETE secondaryMapping

Keep mapping for collection-level concerns (list, create, upsert) and secondaryMapping for per-record concerns (update, delete).

Example#

Following the clinic-sample convention:

@Component
@CrudMapping(
    mapping          = "/masterdata/industrySector",
    type             = IndustrySectorModel.class,
    secondaryMapping = "/masterdata/industrySector/{id}"
)
public class IndustrySectorHandler extends AbstractHandler
        implements QueryHandler, ReadHandler, SaveHandler, UpdateHandler, DeleteHandler {
}

That single handler answers:

  • GET /api/masterdata/industrySector?...QueryHandler
  • GET /api/masterdata/industrySector/{id}ReadHandler
  • POST /api/masterdata/industrySectorSaveHandler (or CreateHandler if you compose it instead)
  • POST /api/masterdata/industrySector/{id}UpdateHandler
  • DELETE /api/masterdata/industrySector/{id}DeleteHandler

The {id} placeholder#

{id} in a secondaryMapping URL resolves to the primary key of the mapped table — the table Palmyra derived from the class declared in type (via @PalmyraType and the primary-key field on the POJO, flagged with @PalmyraField(primaryKey = true)).

So for the IndustrySectorHandler above, {id} is the primary key of the IndustrySectorModel / industry_sector table. A request to DELETE /api/masterdata/industrySector/42 deletes the row where the primary key equals 42; POST /api/masterdata/industrySector/42 updates it.

If the primary key is composite or spans multiple fields, use a multi-segment path template (e.g. /masterdata/industrySector/{tenantId}/{id}) and Palmyra will match each placeholder against the corresponding primary-key component in declaration order.

When secondaryMapping is omitted#

If you don’t declare secondaryMapping, update and delete routes are not published — the handler will only accept the collection-level verbs. A read-only list endpoint looks like:

@Component
@CrudMapping(mapping = "/reports/daily-sales", type = DailySalesModel.class)
public class DailySalesHandler implements QueryHandler { }