Blog App Tutorial Part 5

Part 5 closes the tutorial with transactional workflows and reporting queries.

Transaction with commit

ballerina
transaction {
    UserRow newUser = check (check db.'from(User).create({
        email: "transaction@example.com",
        name: "Transaction User",
        status: ACTIVE
    })).cloneWithType();

    UserProfileRow _ = check (check db.'from(UserProfile).create({
        userId: newUser.id,
        bio: "Created within a transaction"
    })).cloneWithType();

    check commit;
}

Transaction with rollback

ballerina
transaction {
    UserRow _ = check (check db.'from(User).create({
        email: "rollback@example.com",
        name: "Rollback User",
        status: ACTIVE
    })).cloneWithType();

    check error("Simulated error for rollback");
    check commit;
} on fail {
    // rollback handled automatically
}

Verify rollback behavior

ballerina
record {}? rolledBackRow = check db.'from(User)
    .'where({email: {'equals: "rollback@example.com"}})
    .findUnique();

if rolledBackRow is () {
    // expected: user not persisted
}

Raw SQL cross-table report

ballerina
stream<record {}, error?> resultStream = check db.rawQuery(
    "SELECT u.name, COUNT(p.id) as post_count" +
    " FROM users u" +
    " LEFT JOIN posts p ON u.id = p.author_id" +
    " GROUP BY u.id, u.name" +
    " HAVING COUNT(p.id) > 0" +
    " ORDER BY COUNT(p.id) DESC"
);

record {}[] results = check from var row in resultStream select row;

Raw SQL update operation

ballerina
_ = check db.rawExecute(
    "UPDATE users" +
    " SET updated_at = NOW()" +
    " WHERE id IN (SELECT DISTINCT author_id FROM posts WHERE status = 'PUBLISHED')"
);

Cleanup and next steps

  • close the database client: check db.close();
  • revisit Filter Operators Reference for advanced conditions
  • adapt this tutorial into your own domain (inventory, CRM, analytics)