Building Tables
Define 4.0 table metadata, field types, backend mappings, and relationships.
Define the table
This fragment creates metadata for a person table. Imports are shown so the classes can be resolved in a Java 21 project using qqq-backend-core.
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
QTableMetaData person = new QTableMetaData()
.withName("person")
.withLabel("People")
.withBackendName("rdbmsBackend")
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("name", QFieldType.STRING)
.withIsRequired(true));Add it with qInstance.addTable(person). The instance must contain the named backend. For RDBMS, use RDBMSTableBackendDetails to map the SQL table and QFieldMetaData.withBackendName to map differently named columns. Manage SQL schema migrations separately.
Field types
| QFieldType | Java value |
|---|---|
| STRING, TEXT, HTML, PASSWORD | String |
| INTEGER | Integer |
| LONG | Long |
| DECIMAL | BigDecimal |
| BOOLEAN | Boolean |
| DATE | LocalDate |
| TIME | LocalTime |
| DATE_TIME | Instant |
| BLOB | byte array |
Use withIsRequired, withIsEditable, withMaxLength, and field behaviors where appropriate. A display format changes presentation, not the stored type. Match primary-key types and database nullability to the metadata.
Relationships
Use joins for table relationships, possible-value sources for selectable referenced values, and associations for related record sections. Their names must resolve in the same QInstance. Validate the complete instance with QInstanceValidator before serving requests.
Association sample
The 4.0 sample uses Person → pets → Pet → notes → Pet Note. pets and notes are exact association group names; pets.notes identifies the nested path when selecting associations. Join names and widget names do not replace those group names.
An expanded read returns notes: [] for a known empty group. Unrequested or personalized-away groups remain absent; a failed read must not be treated as an empty group. On replacement UPDATE, omitting a group preserves it, while an explicit empty array requests removal of its stored children subject to DELETE permission. Existing children need EDIT and new children need INSERT.
Replacement uses the stored old relationship to find omitted children and the normalized prospective relationship for retained/new children. A null prospective relationship permits an empty replacement but rejects child writes. Omitted children are deleted before the parent changes; cascades delete descendants before ancestors. A rejected or incomplete child deletion stops that parent mutation, but earlier successful sibling or descendant writes may remain. Use a supported encompassing caller transaction and roll it back when the operation must succeed together; recursive writes do not create an automatic transaction.
The Next Copy preview uses this tree to distinguish copying one Person from copying the Person with its pets and notes. Run it from the tagged Quickstart checkout.
The tagged table reference and the sample metadata describe the 4.0.0 source.