A lightweight ORM framework designed for AWS Lambda environments. featherORM eliminates the boilerplate code and manual mapping required when using pure JDBC connections, while remaining lightweight compared to full-featured frameworks like Spring Cloud for AWS Lambda.
featherORM provides a minimal, annotation-driven approach to database operations in AWS Lambda functions. It manages database connections, transactions, and result set mapping automatically, allowing developers to focus on business logic rather than database plumbing.
- Lightweight: Minimal dependencies, optimized for AWS Lambda cold starts
- Annotation-driven: Simple annotations for DAO classes, models, and transactions
- Automatic transaction management: Declarative transaction handling via
@Transactionannotation - Flexible mapping: Support for both entity models and DTOs
- Named parameter queries: Type-safe query execution with named parameters
- Automatic save/update: Entity persistence with automatic INSERT/UPDATE detection based on primary keys
- Java 11 or higher
- PostgreSQL database
- AWS Lambda environment (or compatible JDBC environment)
Add featherORM to your project dependencies:
<dependency>
<groupId>io.sarfraz</groupId>
<artifactId>featherorm</artifactId>
<version>1.0</version>
</dependency>Annotate your DAO class with @DBUtilScan to enable proxy creation and transaction management:
@DBUtilScan
public class UserDAO {
@Transaction
public List<UserDTO> findUsersByStatus(String status) {
String query = "SELECT * FROM users WHERE status = :status";
return QueryExecutor.createQuery(query)
.setParameter("status", status)
.execute(UserDTO.class);
}
@Transaction
public int updateUserStatus(Long userId, String status) {
String query = "UPDATE users SET status = :status WHERE user_id = :userId";
return QueryExecutor.createQuery(query)
.setParameter("status", status)
.setParameter("userId", userId)
.executeUpdate();
}
}Create a proxy instance using DBUtil.getProxy():
UserDAO userDAO = DBUtil.getProxy(UserDAO.class);
List<UserDTO> users = userDAO.findUsersByStatus("ACTIVE");Methods annotated with @Transaction are automatically wrapped with transaction begin, commit, and rollback logic. If an exception occurs, the transaction is rolled back automatically.
@DBUtilScan
public class OrderDAO {
@Transaction
public void createOrder(Order order) {
QueryExecutor.save(order);
}
@Transaction
public void processPayment(Long orderId, BigDecimal amount) {
String query = "UPDATE orders SET payment_amount = :amount WHERE order_id = :orderId";
QueryExecutor.createQuery(query)
.setParameter("amount", amount)
.setParameter("orderId", orderId)
.executeUpdate();
}
}For classes used to map query results (DTOs), use @DBUtilModel and @Column annotations:
@DBUtilModel
public class UserDTO {
@Column("user_id")
private Long userId;
@Column("username")
private String username;
@Column("email")
private String email;
// Getters and setters
}For true entity models that support save operations, use @Table, @DBUtilModel, @Column, and @PrimaryKey:
@Table(schema = "public", table = "users")
@DBUtilModel
public class User {
@PrimaryKey
@Column("user_id")
private Long userId;
@Column("username")
private String username;
@Column("email")
private String email;
// Getters and setters
}Use QueryExecutor.execute() to execute SELECT queries and map results to a list of DTOs or models:
String query = "SELECT * FROM users WHERE status = :status";
List<UserDTO> users = QueryExecutor.createQuery(query)
.setParameter("status", "ACTIVE")
.execute(UserDTO.class);Use QueryExecutor.executeUpdate() for INSERT, UPDATE, or DELETE operations:
String query = "UPDATE users SET status = :status WHERE user_id = :userId";
int rowsAffected = QueryExecutor.createQuery(query)
.setParameter("status", "INACTIVE")
.setParameter("userId", 123L)
.executeUpdate();For entity models annotated with @Table and @PrimaryKey, use QueryExecutor.save() to automatically insert or update based on the primary key value:
User user = new User();
user.setUsername("john_doe");
user.setEmail("john@example.com");
// Insert (primary key is null or 0)
user = QueryExecutor.save(user);
// Update (primary key has a value)
user.setEmail("newemail@example.com");
user = QueryExecutor.save(user);The save() method performs:
- INSERT when the primary key is null, 0, or empty
- UPDATE when the primary key has a non-empty value
featherORM uses a proxy-based approach to intercept method calls and manage transactions. The framework is intentionally lightweight with no IoC container or automatic scanning—developers maintain full control over object creation and lifecycle.
The framework:
- Creates proxy instances on-demand when
DBUtil.getProxy()is explicitly called - Intercepts method calls on proxied DAO instances
- Manages database connections per Lambda execution context
- Automatically handles transaction boundaries for
@Transactionannotated methods - Maps ResultSet data to annotated model/DTO classes
featherORM focuses solely on removing boilerplate code and manual mapping, giving developers complete control over when and how DAO instances are created.
featherORM manages database connections automatically:
- A single connection is maintained per Lambda execution
- Connections are automatically closed when the Lambda execution completes
- Connection configuration is read from environment variables and AWS SSM Parameter Store
See LICENSE file for details.