pub trait Georm<Id> {
// Required methods
fn find_all(pool: &PgPool) -> impl Future<Output = Result<Vec<Self>>> + Send
where Self: Sized;
fn find(
pool: &PgPool,
id: &Id,
) -> impl Future<Output = Result<Option<Self>>> + Send
where Self: Sized;
fn create(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Send
where Self: Sized;
fn update(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Send
where Self: Sized;
fn create_or_update(
&self,
pool: &PgPool,
) -> impl Future<Output = Result<Self>>
where Self: Sized;
fn delete(&self, pool: &PgPool) -> impl Future<Output = Result<u64>> + Send;
fn delete_by_id(
pool: &PgPool,
id: &Id,
) -> impl Future<Output = Result<u64>> + Send;
fn get_id(&self) -> Id;
}Expand description
Core database operations trait for Georm entities.
This trait is automatically implemented by the #[derive(Georm)] macro and provides
all essential CRUD operations for database entities. The trait is generic over the
primary key type Id, which can be a simple type (e.g., i32) or a generated
composite key struct (e.g., UserRoleId).
§Generated Implementation
When you derive Georm on a struct, this trait is automatically implemented with
PostgreSQL-optimized queries that use:
- Prepared statements for security and performance
- RETURNING clause to capture database-generated values
- ON CONFLICT for efficient upsert operations
- Compile-time verification via SQLx macros
§Method Categories
§Static Methods (Query Operations)
find_all- Retrieve all entities from the tablefind- Retrieve a single entity by primary keydelete_by_id- Delete an entity by primary key
§Instance Methods (Mutation Operations)
create- Insert a new entity into the databaseupdate- Update an existing entity in the databasecreate_or_update- Upsert (insert or update) an entitydelete- Delete this entity from the databaseget_id- Get the primary key of this entity
§Usage Examples
use georm::Georm;
#[derive(Georm)]
#[georm(table = "users")]
struct User {
#[georm(id)]
id: i32,
username: String,
email: String,
}
// Static methods
let all_users = User::find_all(&pool).await?;
let user = User::find(&pool, &1).await?;
let deleted_count = User::delete_by_id(&pool, &1).await?;
// Instance methods
let new_user = User { id: 0, username: "alice".into(), email: "alice@example.com".into() };
let created = new_user.create(&pool).await?;
let updated = created.update(&pool).await?;
let id = updated.get_id();
let deleted_count = updated.delete(&pool).await?;§Composite Key Support
For entities with composite primary keys, the Id type parameter becomes a generated
struct following the pattern {EntityName}Id:
#[derive(Georm)]
#[georm(table = "user_roles")]
struct UserRole {
#[georm(id)]
user_id: i32,
#[georm(id)]
role_id: i32,
assigned_at: chrono::DateTime<chrono::Utc>,
}
// Generated: pub struct UserRoleId { pub user_id: i32, pub role_id: i32 }
// Trait: impl Georm<UserRoleId> for UserRole
let id = UserRoleId { user_id: 1, role_id: 2 };
let user_role = UserRole::find(&pool, &id).await?;§Error Handling
All methods return sqlx::Result<T> and may fail due to:
- Database connection issues
- Constraint violations (unique, foreign key, etc.)
- Invalid queries (though most are caught at compile time)
- Missing records (for operations expecting existing data)
Required Methods§
Sourcefn find_all(pool: &PgPool) -> impl Future<Output = Result<Vec<Self>>> + Sendwhere
Self: Sized,
fn find_all(pool: &PgPool) -> impl Future<Output = Result<Vec<Self>>> + Sendwhere
Self: Sized,
Retrieve all entities from the database table.
This method executes a SELECT * FROM table_name query and returns all records
as a vector of entities. The results are not paginated or filtered.
§Returns
Ok(Vec<Self>)- All entities in the table (may be empty)Err(sqlx::Error)- Database connection or query execution errors
§Performance Notes
- Returns all records in memory - consider pagination for large tables
- Uses prepared statements for optimal performance
- No built-in ordering - results may vary between calls
§Examples
let all_users = User::find_all(&pool).await?;
println!("Found {} users", all_users.len());§Errors
Returns sqlx::Error for database connection issues, permission problems,
or if the table doesn’t exist.
Sourcefn find(
pool: &PgPool,
id: &Id,
) -> impl Future<Output = Result<Option<Self>>> + Sendwhere
Self: Sized,
fn find(
pool: &PgPool,
id: &Id,
) -> impl Future<Output = Result<Option<Self>>> + Sendwhere
Self: Sized,
Find a single entity by its primary key.
This method executes a SELECT * FROM table_name WHERE primary_key = $1 query
(or equivalent for composite keys) and returns the matching entity if found.
§Parameters
pool- Database connection poolid- Primary key value (simple type or composite key struct)
§Returns
Ok(Some(Self))- Entity found and returnedOk(None)- No entity with the given ID existsErr(sqlx::Error)- Database connection or query execution errors
§Examples
// Simple primary key
let user = User::find(&pool, &1).await?;
// Composite primary key
let id = UserRoleId { user_id: 1, role_id: 2 };
let user_role = UserRole::find(&pool, &id).await?;§Errors
Returns sqlx::Error for database connection issues, type conversion errors,
or query execution problems. Note that not finding a record is not an error
- it returns
Ok(None).
Sourcefn create(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Sendwhere
Self: Sized,
fn create(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Sendwhere
Self: Sized,
Insert this entity as a new record in the database.
This method executes an INSERT INTO table_name (...) VALUES (...) RETURNING *
query and returns the newly created entity with any database-generated values
(such as auto-increment IDs, default timestamps, etc.).
§Parameters
pool- Database connection pool
§Returns
Ok(Self)- The entity as it exists in the database after insertionErr(sqlx::Error)- Database constraint violations or connection errors
§Database Behavior
- Uses
RETURNING *to capture database-generated values - Respects database defaults for fields marked
#[georm(defaultable)] - Triggers and database-side modifications are reflected in the returned entity
§Examples
let new_user = User { id: 0, username: "alice".into(), email: "alice@example.com".into() };
let created_user = new_user.create(&pool).await?;
println!("Created user with ID: {}", created_user.id);§Errors
Returns sqlx::Error for:
- Unique constraint violations
- Foreign key constraint violations
- NOT NULL constraint violations
- Database connection issues
- Permission problems
Sourcefn update(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Sendwhere
Self: Sized,
fn update(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Sendwhere
Self: Sized,
Update an existing entity in the database.
This method executes an UPDATE table_name SET ... WHERE primary_key = ... RETURNING *
query using the entity’s current primary key to locate the record to update.
§Parameters
pool- Database connection pool
§Returns
Ok(Self)- The entity as it exists in the database after the updateErr(sqlx::Error)- Database errors or if no matching record exists
§Database Behavior
- Uses
RETURNING *to capture any database-side changes - Updates all fields, not just changed ones
- Triggers and database-side modifications are reflected in the returned entity
- Fails if no record with the current primary key exists
§Examples
let mut user = User::find(&pool, &1).await?.unwrap();
user.email = "newemail@example.com".into();
let updated_user = user.update(&pool).await?;§Errors
Returns sqlx::Error for:
- No matching record found (record was deleted by another process)
- Constraint violations (unique, foreign key, etc.)
- Database connection issues
- Permission problems
Sourcefn create_or_update(&self, pool: &PgPool) -> impl Future<Output = Result<Self>>where
Self: Sized,
fn create_or_update(&self, pool: &PgPool) -> impl Future<Output = Result<Self>>where
Self: Sized,
Insert or update this entity using PostgreSQL’s upsert functionality.
This method executes an INSERT ... ON CONFLICT (...) DO UPDATE SET ... RETURNING *
query that atomically inserts the entity if it doesn’t exist, or updates it if
a record with the same primary key already exists.
§Parameters
pool- Database connection pool
§Returns
Ok(Self)- The final entity state in the database (inserted or updated)Err(sqlx::Error)- Database connection or constraint violation errors
§Database Behavior
- Uses PostgreSQL’s
ON CONFLICTfor true atomic upsert - More efficient than separate find-then-create-or-update logic
- Uses
RETURNING *to capture the final state - Conflict resolution is based on the primary key constraint
§Examples
let user = User { id: 1, username: "alice".into(), email: "alice@example.com".into() };
let final_user = user.create_or_update(&pool).await?;
// Will insert if ID 1 doesn't exist, update if it does§Errors
Returns sqlx::Error for:
- Non-primary-key constraint violations
- Database connection issues
- Permission problems
Sourcefn delete(&self, pool: &PgPool) -> impl Future<Output = Result<u64>> + Send
fn delete(&self, pool: &PgPool) -> impl Future<Output = Result<u64>> + Send
Delete this entity from the database.
This method executes a DELETE FROM table_name WHERE primary_key = ... query
using this entity’s primary key to identify the record to delete.
§Parameters
pool- Database connection pool
§Returns
Ok(u64)- Number of rows affected (0 if entity didn’t exist, 1 if deleted)Err(sqlx::Error)- Database connection or constraint violation errors
§Database Behavior
- Uses the entity’s current primary key for deletion
- Returns 0 if no matching record exists (not an error)
- May fail due to foreign key constraints if other records reference this entity
§Examples
let user = User::find(&pool, &1).await?.unwrap();
let deleted_count = user.delete(&pool).await?;
assert_eq!(deleted_count, 1);§Errors
Returns sqlx::Error for:
- Foreign key constraint violations (referenced by other tables)
- Database connection issues
- Permission problems
Sourcefn delete_by_id(
pool: &PgPool,
id: &Id,
) -> impl Future<Output = Result<u64>> + Send
fn delete_by_id( pool: &PgPool, id: &Id, ) -> impl Future<Output = Result<u64>> + Send
Delete an entity by its primary key without needing an entity instance.
This method executes a DELETE FROM table_name WHERE primary_key = ... query
using the provided ID to identify the record to delete.
§Parameters
pool- Database connection poolid- Primary key value (simple type or composite key struct)
§Returns
Ok(u64)- Number of rows affected (0 if entity didn’t exist, 1 if deleted)Err(sqlx::Error)- Database connection or constraint violation errors
§Database Behavior
- More efficient than
find().delete()when you only have the ID - Returns 0 if no matching record exists (not an error)
- May fail due to foreign key constraints if other records reference this entity
§Examples
// Simple primary key
let deleted_count = User::delete_by_id(&pool, &1).await?;
// Composite primary key
let id = UserRoleId { user_id: 1, role_id: 2 };
let deleted_count = UserRole::delete_by_id(&pool, &id).await?;§Errors
Returns sqlx::Error for:
- Foreign key constraint violations (referenced by other tables)
- Database connection issues
- Permission problems
Sourcefn get_id(&self) -> Id
fn get_id(&self) -> Id
Get the primary key of this entity.
For entities with simple primary keys, this returns the ID value directly.
For entities with composite primary keys, this returns an owned instance of
the generated {EntityName}Id struct.
§Returns
- Simple keys: The primary key value (e.g.,
i32,String) - Composite keys: Generated ID struct (e.g.,
UserRoleId)
§Examples
// Simple primary key
let user = User { id: 42, username: "alice".into(), email: "alice@example.com".into() };
let id = user.get_id(); // Returns 42
// Composite primary key
let user_role = UserRole { user_id: 1, role_id: 2, assigned_at: now };
let id = user_role.get_id(); // Returns UserRoleId { user_id: 1, role_id: 2 }Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.