Skip to content

Commit 2afa768

Browse files
refactor(docs): Merge Typed API docs into Java Client page
1 parent cf425a0 commit 2afa768

File tree

2 files changed

+170
-174
lines changed

2 files changed

+170
-174
lines changed

website/docs/apis/java-client.md

Lines changed: 170 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ List<User> users = List.of(
170170
);
171171
```
172172

173-
**Note:** Currently data in Fluss is written in the form of `rows`, so we need to convert our POJO to `GenericRow`. For a more user-friendly API for writing data, please refer to the [Java Typed API](./java-typed-api.md) documentation.
173+
**Note:** Currently data in Fluss is written in the form of `rows`, so we need to convert our POJO to `GenericRow`. For a more user-friendly API for writing data, please refer to the [Java Typed API](#java-typed-api) section below.
174174
```java
175175
Table table = connection.getTable(tablePath);
176176

@@ -243,4 +243,172 @@ LookupResult prefixLookup = table.newLookup()
243243
.createLookuper()
244244
.lookup(rowKey)
245245
.get();
246-
```
246+
```
247+
248+
## Java Typed API
249+
250+
Fluss provides a Typed API that allows you to work directly with Java POJOs (Plain Old Java Objects) instead of `InternalRow` objects. This simplifies development by automatically mapping your Java classes to Fluss table schemas.
251+
252+
:::info
253+
The Typed API provides a more user-friendly experience but comes with a performance cost due to the overhead of converting between POJOs and internal row formats. For high-performance use cases, consider using the lower-level `InternalRow` API.
254+
:::
255+
256+
### Defining POJOs
257+
258+
To use the Typed API, define a Java class where the field names and types match your Fluss table schema.
259+
260+
```java
261+
public class User {
262+
public Integer id;
263+
public String name;
264+
public Integer age;
265+
266+
public User() {}
267+
268+
public User(Integer id, String name, Integer age) {
269+
this.id = id;
270+
this.name = name;
271+
this.age = age;
272+
}
273+
274+
// Getters, setters, equals, hashCode, toString...
275+
}
276+
```
277+
278+
The supported type mappings are:
279+
280+
| Fluss Type | Java Type |
281+
|---|---|
282+
| INT | Integer |
283+
| BIGINT | Long |
284+
| STRING | String |
285+
| BOOLEAN | Boolean |
286+
| FLOAT | Float |
287+
| DOUBLE | Double |
288+
| DECIMAL | BigDecimal |
289+
| DATE | LocalDate |
290+
| TIME | LocalTime |
291+
| TIMESTAMP | LocalDateTime |
292+
| TIMESTAMP_LTZ | Instant |
293+
| BINARY / BYTES | byte[] |
294+
295+
### Writing Data
296+
297+
#### Append Writer
298+
299+
For append-only tables (Log tables), use `TypedAppendWriter`.
300+
301+
```java
302+
TablePath path = TablePath.of("my_db", "users_log");
303+
try (Table table = conn.getTable(path)) {
304+
TypedAppendWriter<User> writer = table.newAppend().createTypedWriter(User.class);
305+
306+
writer.append(new User(1, "Alice", 30));
307+
writer.append(new User(2, "Bob", 25));
308+
309+
writer.flush();
310+
}
311+
```
312+
313+
#### Upsert Writer
314+
315+
For primary key tables, use `TypedUpsertWriter`.
316+
317+
```java
318+
TablePath path = TablePath.of("my_db", "users_pk");
319+
try (Table table = conn.getTable(path)) {
320+
TypedUpsertWriter<User> writer = table.newUpsert().createTypedWriter(User.class);
321+
322+
// Insert or Update
323+
writer.upsert(new User(1, "Alice", 31));
324+
325+
// Delete
326+
writer.delete(new User(1, null, null)); // Only PK fields are needed for delete
327+
328+
writer.flush();
329+
}
330+
```
331+
332+
#### Partial Updates
333+
334+
You can perform partial updates by specifying the columns to update.
335+
336+
```java
337+
// Update only 'name' and 'age' for the user with id 1
338+
Upsert upsert = table.newUpsert().partialUpdate("name", "age");
339+
TypedUpsertWriter<User> writer = upsert.createTypedWriter(User.class);
340+
341+
User partialUser = new User();
342+
partialUser.id = 1;
343+
partialUser.name = "Alice Updated";
344+
partialUser.age = 32;
345+
346+
writer.upsert(partialUser);
347+
writer.flush();
348+
```
349+
350+
### Reading Data
351+
352+
Use `TypedLogScanner` to read data as POJOs.
353+
354+
```java
355+
Scan scan = table.newScan();
356+
TypedLogScanner<User> scanner = scan.createTypedLogScanner(User.class);
357+
358+
try (CloseableIterator<TypedScanRecord<User>> iterator = scanner.subscribeFromBeginning()) {
359+
while (iterator.hasNext()) {
360+
TypedScanRecord<User> record = iterator.next();
361+
ChangeType changeType = record.getChangeType();
362+
User user = record.getValue();
363+
364+
System.out.println(changeType + ": " + user);
365+
}
366+
}
367+
```
368+
369+
#### Projections
370+
371+
You can also use projections with the Typed API. The POJO fields that are not part of the projection will be null.
372+
373+
```java
374+
// Only read 'id' and 'name'
375+
TypedLogScanner<User> scanner = table.newScan()
376+
.project("id", "name")
377+
.createTypedLogScanner(User.class);
378+
```
379+
380+
### Lookups
381+
382+
For primary key tables, you can perform lookups using a POJO that represents the primary key.
383+
384+
```java
385+
// Define a POJO for the key
386+
public class UserId {
387+
public Integer id;
388+
389+
public UserId(Integer id) { this.id = id; }
390+
}
391+
392+
// Create a TypedLookuper
393+
TypedLookuper<UserId> lookuper = table.newLookup().createTypedLookuper(UserId.class);
394+
395+
// Perform lookup
396+
CompletableFuture<LookupResult> resultFuture = lookuper.lookup(new UserId(1));
397+
LookupResult result = resultFuture.get();
398+
399+
if (result != null) {
400+
// Convert the result row back to a User POJO if needed
401+
// Note: You might need a RowToPojoConverter for this part if you want the full User object
402+
// or you can access fields from the InternalRow directly.
403+
}
404+
```
405+
406+
### Performance Considerations
407+
408+
While the Typed API offers convenience and type safety, it involves an additional layer of conversion between your POJOs and Fluss's internal binary row format (`InternalRow`). This conversion process (serialization and deserialization) introduces CPU overhead.
409+
410+
Benchmarks indicate that using the Typed API can be roughly **2x slower** than using the `InternalRow` API directly for both writing and reading operations.
411+
412+
**Recommendation:**
413+
* Use the **Typed API** for ease of use, rapid development, and when type safety is preferred over raw performance.
414+
* Use the **InternalRow API** for high-throughput, latency-sensitive applications where performance is critical.

website/docs/apis/java-typed-api.md

Lines changed: 0 additions & 172 deletions
This file was deleted.

0 commit comments

Comments
 (0)