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: