|
| 1 | + |
| 2 | +# TeaQL Java CRUD Guide |
| 3 | + |
| 4 | +Generated for `com.teaql.runtimeexampleconformanceservice`. Use this guide when adding controllers, services, jobs, or integration code in this workspace. |
| 5 | + |
| 6 | +## Setup |
| 7 | + |
| 8 | +```java |
| 9 | +import com.teaql.runtimeexampleconformanceservice.Q; |
| 10 | +import com.teaql.runtimeexampleconformanceservice.RuntimeExampleConformanceServiceUserContext; |
| 11 | +import io.teaql.core.web.WebResponse; |
| 12 | +``` |
| 13 | + |
| 14 | +Most workspace code receives a TeaQL context from Spring: |
| 15 | + |
| 16 | +```java |
| 17 | +public WebResponse handle(@TQLContext UserContext userContext) { |
| 18 | + // use Q, WebResponse, and entity.auditAs("comment").save(userContext) |
| 19 | +} |
| 20 | +``` |
| 21 | + |
| 22 | +## Non-Negotiable Database Rule |
| 23 | + |
| 24 | +Never use SQL to operate on the database from workspace business code. This includes select, insert, update, delete, schema changes, JDBC templates, native queries, direct repositories, and ad hoc SQL helpers. |
| 25 | + |
| 26 | +If the generated TeaQL API does not provide a path for the requested change, stop and report the missing API. Do not implement a SQL workaround. |
| 27 | + |
| 28 | +## Mandatory Update Method Rule |
| 29 | + |
| 30 | +Use generated `updateXxx(...)` methods for all entity field changes. Do not use `setXxx(...)` in new code. Setters are deprecated even when the IDE or generated class makes them visible. |
| 31 | + |
| 32 | +```java |
| 33 | +entity.updateName("new value"); |
| 34 | +``` |
| 35 | + |
| 36 | +Do not write: |
| 37 | + |
| 38 | +```java |
| 39 | +entity.setName("new value"); |
| 40 | +``` |
| 41 | + |
| 42 | +## Read One Entity |
| 43 | + |
| 44 | +Prefer typed generated query entry points. Controller methods that return one object should wrap the result in `WebResponse.of(...)`: |
| 45 | + |
| 46 | +```java |
| 47 | +public WebResponse getOne(@TQLContext UserContext userContext, Long id) { |
| 48 | + try { |
| 49 | + var entity = Q.platforms() |
| 50 | + .filterById(id) |
| 51 | + .selectSelf() |
| 52 | + .comment("load detail by id") |
| 53 | + .purpose("get task detail") |
| 54 | + .executeForOne(userContext); |
| 55 | + return WebResponse.of(entity); |
| 56 | + } catch (Exception e) { |
| 57 | + return WebResponse.fail(e.getMessage()); |
| 58 | + } |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +## Read A Page |
| 63 | + |
| 64 | +Controller methods that return multiple objects should wrap the list in `WebResponse.of(...)`: |
| 65 | + |
| 66 | +```java |
| 67 | +public WebResponse list(@TQLContext UserContext userContext) { |
| 68 | + try { |
| 69 | + var list = Q.platforms() |
| 70 | + .selectSelf() |
| 71 | + .page(1, 20) |
| 72 | + .comment("list tasks") |
| 73 | + .purpose("render dashboard list") |
| 74 | + .executeForList(userContext); |
| 75 | + return WebResponse.of(list); |
| 76 | + } catch (Exception e) { |
| 77 | + return WebResponse.fail(e.getMessage()); |
| 78 | + } |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +## Create |
| 83 | + |
| 84 | +```java |
| 85 | +var entity = new Platform(); |
| 86 | +// Fill fields with generated updateXxx(...) methods, not setXxx(...). |
| 87 | +entity.auditAs("Create new item").save(userContext); |
| 88 | +``` |
| 89 | + |
| 90 | +## Update |
| 91 | + |
| 92 | +```java |
| 93 | +var entity = Q.platforms() |
| 94 | + .filterById(id) |
| 95 | + .selectSelf() |
| 96 | + .comment("load for update") |
| 97 | + .purpose("edit entity") |
| 98 | + .executeForOne(userContext); |
| 99 | + |
| 100 | +// Use updateXxx(...) methods for state changes. |
| 101 | +// entity.updateName("new value"); |
| 102 | +entity.auditAs("Update entity properties").save(userContext); |
| 103 | +``` |
| 104 | + |
| 105 | +## Load Relations |
| 106 | + |
| 107 | +Use generated selectors. Do not write a loop that queries children one row at a time. The examples below focus on the entities with the highest reverse relation counts because they are stronger aggregate-root candidates. |
| 108 | + |
| 109 | +```java |
| 110 | +var list = Q.platforms() |
| 111 | + .selectWorkItemListWith(Q.workItems().selectSelf()) |
| 112 | + .comment("load with workItemList") |
| 113 | + .purpose("fetch child workItemList") |
| 114 | + .executeForList(userContext); |
| 115 | +``` |
| 116 | + |
| 117 | + |
| 118 | +## Delete |
| 119 | + |
| 120 | +Use the generated TeaQL soft-delete API available on the entity/request class in this domain. If unsure, inspect the generated request class for the target entity and use the soft-delete operation it exposes. |
| 121 | + |
| 122 | +Do not hard-delete rows. Do not write SQL `DELETE` or `UPDATE` statements. If the soft-delete API is not visible, stop and report that blocker instead of changing data through SQL. |
| 123 | + |
| 124 | +## Common Mistakes |
| 125 | + |
| 126 | +- Do not instantiate repositories directly in workspace business code. |
| 127 | +- Do not use SQL for any database operation. |
| 128 | +- Do not use deprecated `setXxx(...)` methods for updates. Use `updateXxx(...)`. |
| 129 | +- Do not return raw entities or lists from controller query methods. Return `WebResponse.of(entity)` or `WebResponse.of(list)`. |
| 130 | +- Do not forget `.executeForOne(userContext)` or `.executeForList(userContext)`. |
| 131 | +- Do not forget `.comment("...")` and `.purpose("...")` on queries. |
| 132 | +- Do not forget `.auditAs("...")` before calling `.save(context)` on entities. |
| 133 | +- Do not assume relations are loaded unless the query selected them. |
| 134 | +- Do not bypass `UserContext`; context carries logging, tenant, security, and repository resolution behavior. |
| 135 | + |
| 136 | +## Entity Cheat Sheet |
| 137 | + |
| 138 | +These entities are selected by reverse relation count, not by model declaration order. |
| 139 | + |
| 140 | +- `Platform`: reverse relations `1`, query `Q.platforms()`, save `new Platform().auditAs("comment").save(userContext)`, request `com.teaql.runtimeexampleconformanceservice.platform.PlatformRequest` |
0 commit comments