Skip to content

AutoCrud<T>

AutoCrud<T> uses a single type parameter T for filters, rows, view, and edit forms. It is the fastest way to get a working CRUD on screen: it derives every capability — search, filters, navigation, editing, creation, deletion — from the entity and its CrudStore. Capability annotations let you strip them one by one — turning the same class into a listing, a read-only catalogue, or any combination in between.


public abstract class AutoCrud<T extends Identifiable>
extends FilteredAutoCrud<T, T>
MethodPurpose
store()Return the CrudStore<T> used for all operations

@Service
@UI("/products")
public class ProductCrud extends AutoCrud<Product> {
private final ProductStore repository;
public ProductCrud(ProductStore repository) {
this.repository = repository;
}
@Override
public CrudStore<Product> store() {
return repository;
}
}
public record Product(
@EditableOnlyWhenCreating @NotEmpty String id,
@NotEmpty String name,
BigDecimal price,
ProductStatus status
) implements Identifiable {}

This gives you:

RouteScreen
/productsListing with New and Delete buttons
/products/:idRead-only detail view with Edit button
/products/:id/editEdit form
/products/newCreate form

Add any combination of these class-level annotations to restrict what users can do:

AnnotationEffect
@ReadOnlyShorthand for @NotCreatable @NotEditable @NotDeletable
@NotCreatableHides the New button in the list
@NotEditableHides the Edit button in the detail view
@NotDeletableHides the Delete button in the list
@NotNavigableHides the View button column — rows are not clickable

These combine freely. A few common patterns:

IntentAnnotations
Full CRUD(nothing)
Read-only with detail view@ReadOnly
Simple read-only list (no detail)@ReadOnly @NotNavigable
List you can add to, but not click into@NotNavigable
List you can edit but not create@NotCreatable
List you can edit but not delete@NotDeletable
@Service
@UI("/audit-log")
@ReadOnly
@NotNavigable
public class AuditLog extends AutoCrud<AuditEntry> {
private final AuditRepository repository;
public AuditLog(AuditRepository repository) {
this.repository = repository;
}
@Override
public CrudStore<AuditEntry> store() {
return repository;
}
}

The write operations are simply never invoked for a read-only AutoCrud.


OperationBehaviour
searchDelegates to store().find(searchText, filters, pageable) → Page<T> (default: filters by SearchableText.searchableText()/toString(), sorts by pageable.sort(), paginates in memory)
getViewLoads the entity by id
getEditorSame as view — entity fields become editable inputs
getCreationFormInstantiates a new T
deleteAllByIdDelegates to store().deleteAllById()

For most use cases store() is the only method you need to implement. To push search, filtering, sorting and pagination to the database, override CrudStore.find(...) — AutoCrud calls it automatically. To customise other operations (pre-populated creation forms, etc.) override the protected hooks fetchRows(), buildNamedView(), or buildCreationForm() directly in your subclass — see Customising AutoCrud behaviour.


AutoCrud<T> uses T for every screen, which means:

  • The filter bar shows the same fields as the grid rows.
  • The edit form shows the same fields as the detail view.
  • The create form is identical to the edit form.

When this is too restrictive, move to: