Georm

Trait Georm 

Source
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 table
  • find - Retrieve a single entity by primary key
  • delete_by_id - Delete an entity by primary key

§Instance Methods (Mutation Operations)

  • create - Insert a new entity into the database
  • update - Update an existing entity in the database
  • create_or_update - Upsert (insert or update) an entity
  • delete - Delete this entity from the database
  • get_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§

Source

fn find_all(pool: &PgPool) -> impl Future<Output = Result<Vec<Self>>> + Send
where 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.

Source

fn find( pool: &PgPool, id: &Id, ) -> impl Future<Output = Result<Option<Self>>> + Send
where 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 pool
  • id - Primary key value (simple type or composite key struct)
§Returns
  • Ok(Some(Self)) - Entity found and returned
  • Ok(None) - No entity with the given ID exists
  • Err(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).
Source

fn create(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Send
where 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 insertion
  • Err(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
Source

fn update(&self, pool: &PgPool) -> impl Future<Output = Result<Self>> + Send
where 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 update
  • Err(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
Source

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 CONFLICT for 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
Source

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
Source

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 pool
  • id - 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
Source

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.

Implementors§