Skip to content

Commit 45cc66e

Browse files
committed
Update docs.
1 parent 21d2278 commit 45cc66e

8 files changed

Lines changed: 734 additions & 282 deletions

File tree

en/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [Database Integration](database.md)
1010
* [Advanced Features](advanced-features.md)
1111
* [Best Practices](best-practices.md)
12+
* [What's New in 1.7.23](whats-new-1.7.23.md)
1213

1314
## API Reference
1415
* [Action API](api/action.md)

en/cli-applications.md

Lines changed: 20 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -221,152 +221,31 @@ public String exportUsers() {
221221

222222
## Creating Custom Commands
223223

224-
### POJO Generator Example
224+
### Built-in POJO Generator
225225

226-
```java
227-
@Action(
228-
value = "generate-pojo",
229-
description = "Generate POJO class from database table",
230-
options = {
231-
@Argument(name = "table", description = "Database table name"),
232-
@Argument(name = "package", description = "Java package name"),
233-
@Argument(name = "output", description = "Output directory")
234-
},
235-
mode = Action.Mode.CLI
236-
)
237-
public String generatePojo() {
238-
String table = getContext().getAttribute("--table");
239-
String packageName = getContext().getAttribute("--package", "com.example.model");
240-
String output = getContext().getAttribute("--output", "./src/main/java");
241-
242-
if (table == null) {
243-
return "Please provide a table name with --table";
244-
}
245-
246-
try {
247-
Repository repository = Type.MySQL.createRepository();
248-
repository.connect(getConfiguration());
249-
250-
// Get table metadata
251-
List<Row> columns = repository.query(
252-
"SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE " +
253-
"FROM INFORMATION_SCHEMA.COLUMNS " +
254-
"WHERE TABLE_NAME = ?", table);
255-
256-
if (columns.isEmpty()) {
257-
return "Table not found or has no columns: " + table;
258-
}
259-
260-
// Generate class name (convert snake_case to CamelCase)
261-
String className = Arrays.stream(table.split("_"))
262-
.map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase())
263-
.collect(Collectors.joining());
264-
265-
// Generate Java code
266-
StringBuilder code = new StringBuilder();
267-
code.append("package ").append(packageName).append(";\n\n");
268-
code.append("public class ").append(className).append(" {\n\n");
269-
270-
// Generate fields
271-
for (Row column : columns) {
272-
String columnName = column.getString("COLUMN_NAME");
273-
String dataType = column.getString("DATA_TYPE");
274-
String javaType = mapSqlTypeToJava(dataType);
275-
String fieldName = toCamelCase(columnName);
276-
277-
code.append(" private ").append(javaType).append(" ").append(fieldName).append(";\n");
278-
}
279-
280-
code.append("\n");
281-
282-
// Generate getters and setters
283-
for (Row column : columns) {
284-
String columnName = column.getString("COLUMN_NAME");
285-
String dataType = column.getString("DATA_TYPE");
286-
String javaType = mapSqlTypeToJava(dataType);
287-
String fieldName = toCamelCase(columnName);
288-
String capitalizedFieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
289-
290-
// Getter
291-
code.append(" public ").append(javaType).append(" get").append(capitalizedFieldName).append("() {\n");
292-
code.append(" return ").append(fieldName).append(";\n");
293-
code.append(" }\n\n");
294-
295-
// Setter
296-
code.append(" public void set").append(capitalizedFieldName).append("(").append(javaType).append(" ").append(fieldName).append(") {\n");
297-
code.append(" this.").append(fieldName).append(" = ").append(fieldName).append(";\n");
298-
code.append(" }\n\n");
299-
}
300-
301-
code.append("}");
302-
303-
// Write to file
304-
String packagePath = packageName.replace('.', '/');
305-
Path outputPath = Paths.get(output, packagePath, className + ".java");
306-
Files.createDirectories(outputPath.getParent());
307-
Files.write(outputPath, code.toString().getBytes());
308-
309-
return "Generated " + className + ".java in " + outputPath;
310-
} catch (Exception e) {
311-
return "Error generating POJO: " + e.getMessage();
312-
}
313-
}
226+
Tinystruct includes a built-in `generate` command that creates POJO classes and XML mapping files directly from your database schema. It automatically detects Java types from column definitions and adds the required import statements — no manual import specification is needed.
314227

315-
private String toCamelCase(String snakeCase) {
316-
StringBuilder result = new StringBuilder();
317-
boolean nextUpper = false;
318-
319-
for (char c : snakeCase.toCharArray()) {
320-
if (c == '_') {
321-
nextUpper = true;
322-
} else {
323-
if (nextUpper) {
324-
result.append(Character.toUpperCase(c));
325-
nextUpper = false;
326-
} else {
327-
result.append(Character.toLowerCase(c));
328-
}
329-
}
330-
}
331-
332-
return result.toString();
333-
}
228+
```bash
229+
# Interactive mode — prompts for table names and output path
230+
bin/dispatcher generate
334231

335-
private String mapSqlTypeToJava(String sqlType) {
336-
switch (sqlType.toUpperCase()) {
337-
case "VARCHAR":
338-
case "CHAR":
339-
case "TEXT":
340-
return "String";
341-
case "INT":
342-
case "SMALLINT":
343-
case "TINYINT":
344-
return "Integer";
345-
case "BIGINT":
346-
return "Long";
347-
case "DECIMAL":
348-
case "NUMERIC":
349-
return "BigDecimal";
350-
case "FLOAT":
351-
return "Float";
352-
case "DOUBLE":
353-
return "Double";
354-
case "BOOLEAN":
355-
case "BIT":
356-
return "Boolean";
357-
case "DATE":
358-
return "LocalDate";
359-
case "TIME":
360-
return "LocalTime";
361-
case "DATETIME":
362-
case "TIMESTAMP":
363-
return "LocalDateTime";
364-
default:
365-
return "Object";
366-
}
367-
}
232+
# Non-interactive — specify tables directly
233+
bin/dispatcher generate --tables users
234+
235+
# Multiple tables (semicolon-delimited)
236+
bin/dispatcher generate --tables "users;orders;products"
368237
```
369238

239+
The generator:
240+
- Supports **MySQL**, **MSSQL**, **SQLite**, and **H2** databases
241+
- **Automatically imports** `java.time.LocalDateTime`, `java.util.Date`, `java.sql.Timestamp`, and `java.sql.Time` based on column types
242+
- Generates both the **Java POJO** and the **XML mapping file**
243+
- Auto-detects the project's base package from your source tree
244+
- Singularizes table names for class names (e.g. `users``User`)
245+
246+
For full details and generated code examples, see the [Database Integration — Built-in POJO Generator](database.md#built-in-pojo-generator) section.
247+
248+
370249
## Best Practices
371250

372251
1. **Provide Clear Help**: Always include descriptive help text for your commands.

en/database.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,166 @@ In the tinystruct framework, there are distinct methods for different database o
576576

577577
For clarity and precise control, it's recommended to use `append()` for inserts and `update()` for updates rather than relying on `save()`.
578578

579+
## Built-in POJO Generator
580+
581+
Tinystruct includes a built-in code generator that creates POJO classes and XML mapping files directly from your database schema. The generator supports **MySQL**, **MSSQL**, **SQLite**, and **H2** databases.
582+
583+
### Running the Generator
584+
585+
Use the `generate` CLI command to invoke the generator:
586+
587+
```bash
588+
# Interactive mode — the generator will prompt for table names and output path
589+
bin/dispatcher generate
590+
591+
# Non-interactive — specify tables directly
592+
bin/dispatcher generate --tables users
593+
594+
# Multiple tables at once (semicolon-delimited)
595+
bin/dispatcher generate --tables "users;orders;products"
596+
```
597+
598+
The interactive prompts will ask for:
599+
1. **Table name(s)** — semicolon-delimited (e.g. `users;orders`)
600+
2. **Base path** — where to place Java files (default: auto-detected from your project's package structure)
601+
602+
### Automatic Package Imports
603+
604+
The generator **automatically detects** the Java types required by your table columns and adds the correct import statements to the generated POJO. You do not need to specify any packages manually.
605+
606+
The following type mappings are handled automatically:
607+
608+
| SQL Column Type | Java Type | Import |
609+
|----------------------------------------|-------------------|-----------------------------|
610+
| `DATETIME`, `TIMESTAMP`, `DATETIME2` | `LocalDateTime` | `java.time.LocalDateTime` |
611+
| `DATE` | `Date` | `java.util.Date` |
612+
| `TIMESTAMP` (explicit) | `Timestamp` | `java.sql.Timestamp` |
613+
| `TIME` | `Time` | `java.sql.Time` |
614+
| `VARCHAR`, `CHAR`, `TEXT` | `String` | *(built-in)* |
615+
| `INT`, `SMALLINT`, `TINYINT` | `int` | *(built-in)* |
616+
| `BIGINT` | `long` | *(built-in)* |
617+
| `FLOAT` | `float` | *(built-in)* |
618+
| `DOUBLE` | `double` | *(built-in)* |
619+
| `BLOB`, `BINARY`, `VARBINARY` | `byte[]` | *(built-in)* |
620+
621+
### Generated Output
622+
623+
For each table, the generator produces two files:
624+
625+
1. **Java POJO** — e.g. `src/main/java/com/example/objects/User.java`
626+
2. **XML Mapping** — e.g. `src/main/resources/com/example/objects/User.map.xml`
627+
628+
#### Example: Generated POJO
629+
630+
For a table `users` with columns `id INT AUTO_INCREMENT`, `username VARCHAR(50)`, `email VARCHAR(100)`, `created_at DATETIME`:
631+
632+
```java
633+
package com.example.objects;
634+
635+
import java.io.Serializable;
636+
import java.time.LocalDateTime;
637+
import org.tinystruct.data.component.AbstractData;
638+
import org.tinystruct.data.component.Row;
639+
640+
public class User extends AbstractData implements Serializable {
641+
private static final long serialVersionUID = ...L;
642+
private String username;
643+
private String email;
644+
private LocalDateTime createdAt;
645+
646+
public Integer getId() {
647+
return Integer.parseInt(this.Id.toString());
648+
}
649+
650+
public void setUsername(String username) {
651+
this.username = this.setFieldAsString("username", username);
652+
}
653+
654+
public String getUsername() {
655+
return this.username;
656+
}
657+
658+
public void setEmail(String email) {
659+
this.email = this.setFieldAsString("email", email);
660+
}
661+
662+
public String getEmail() {
663+
return this.email;
664+
}
665+
666+
public void setCreatedAt(LocalDateTime createdAt) {
667+
this.createdAt = this.setFieldAsLocalDateTime("createdAt", createdAt);
668+
}
669+
670+
public LocalDateTime getCreatedAt() {
671+
return this.createdAt;
672+
}
673+
674+
@Override
675+
public void setData(Row row) {
676+
if(row.getFieldInfo("id") != null)
677+
this.setId(row.getFieldInfo("id").intValue());
678+
if(row.getFieldInfo("username") != null)
679+
this.setUsername(row.getFieldInfo("username").stringValue());
680+
if(row.getFieldInfo("email") != null)
681+
this.setEmail(row.getFieldInfo("email").stringValue());
682+
if(row.getFieldInfo("created_at") != null)
683+
this.setCreatedAt(row.getFieldInfo("created_at").localDateTimeValue());
684+
}
685+
686+
@Override
687+
public String toString() {
688+
StringBuilder buffer = new StringBuilder();
689+
buffer.append("{");
690+
buffer.append("\"Id\":" + this.getId());
691+
buffer.append(",\"username\":\"" + this.getUsername() + "\"");
692+
buffer.append(",\"email\":\"" + this.getEmail() + "\"");
693+
buffer.append(",\"createdAt\":\"" + this.getCreatedAt() + "\"");
694+
buffer.append("}");
695+
return buffer.toString();
696+
}
697+
}
698+
```
699+
700+
#### Example: Generated XML Mapping
701+
702+
```xml
703+
<?xml version="1.0" encoding="UTF-8"?>
704+
<mapping>
705+
<class name="User" table="users">
706+
<id name="Id" column="id" increment="true" generate="false" length="11" type="INT"/>
707+
<property name="username" column="username" length="50" type="VARCHAR"/>
708+
<property name="email" column="email" length="100" type="VARCHAR"/>
709+
<property name="createdAt" column="created_at" length="0" type="DATETIME"/>
710+
</class>
711+
</mapping>
712+
```
713+
714+
### Using the Generated Code
715+
716+
The generated classes extend `AbstractData`, so they integrate directly with tinystruct's ORM:
717+
718+
```java
719+
// Create
720+
User user = new User();
721+
user.setUsername("john");
722+
user.setEmail("john@example.com");
723+
user.setCreatedAt(LocalDateTime.now());
724+
user.append();
725+
726+
// Read
727+
User found = new User();
728+
found.setId(1);
729+
found.findOneById();
730+
731+
// Update
732+
found.setEmail("newemail@example.com");
733+
found.update();
734+
735+
// Delete
736+
found.delete();
737+
```
738+
579739
## Best Practices
580740

581741
1. **Connection Management**: Always close your database connections when done.

0 commit comments

Comments
 (0)