Here is my code if you can optimize it further or if you have any comments let me know:

internal sealed class SqLiteDatabase
{
    private readonly string _connectionString;

    public SqLiteDatabase(string databaseName)
    {
        _connectionString = $"Data Source={databaseName}.db";
        InitializeDatabase();
    }

    private void InitializeDatabase()
    {
        using var connection = new SQLiteConnection(_connectionString);
        connection.Open();

        var createUsersTableCommand = connection.CreateCommand();
        createUsersTableCommand.CommandText =
        @"
            CREATE TABLE IF NOT EXISTS Users (
                Id TEXT PRIMARY KEY
            );
        ";

        var createCardsTableCommand = connection.CreateCommand();
        createCardsTableCommand.CommandText =
        @"
            CREATE TABLE IF NOT EXISTS Cards (
                Id TEXT PRIMARY KEY,
                Units REAL,
                Remaining INTEGER,
                Timestamp INTEGER
            );
        ";

        var createUserCardsTableCommand = connection.CreateCommand();
        createUserCardsTableCommand.CommandText =
        @"
            CREATE TABLE IF NOT EXISTS UserCards (
                UserId TEXT,
                CardId TEXT,
    
                Timestamp INTEGER,
                PRIMARY KEY(UserId, CardId),
                FOREIGN KEY(UserId) REFERENCES Users(Id),
                FOREIGN KEY(CardId) REFERENCES Cards(Id)
            );
        ";

        createUsersTableCommand.ExecuteNonQuery();
        createCardsTableCommand.ExecuteNonQuery();
        createUserCardsTableCommand.ExecuteNonQuery();
    }
    public User GetOrAddUser(string userId)
    {
        var user = GetUser(userId);
        if (user == null)
        {
            user = new User(userId);
            AddOrUpdateUser(user);
        }

        return user;
    }
    public void AddOrUpdateUser(User user)
    {
        using var connection = new SQLiteConnection(_connectionString);
        connection.Open();

        using var transaction = connection.BeginTransaction();

        var command = connection.CreateCommand();
        command.Transaction = transaction;
        command.CommandText =
        @"
            INSERT OR REPLACE INTO Users (Id)
            VALUES (@Id);
        ";

        command.Parameters.AddWithValue("@Id", user.Id);
        command.ExecuteNonQuery();

        foreach (var card in user.Cards)
        {
            AddOrUpdateCard(card);
            AssignCardToUser(user.Id, card.Id, card.Timestamp);
        }

        transaction.Commit();
    }

    public User? GetUser(string userId)
    {
        using var connection = new SQLiteConnection(_connectionString);
        connection.Open();

        var command = connection.CreateCommand();
        command.CommandText =
        @"
            SELECT Id FROM Users WHERE Id = @UserId;
        ";

        command.Parameters.AddWithValue("@UserId", userId);

        using var reader = command.ExecuteReader();
        if (!reader.Read()) return null;

        var id = reader.GetString(0);
        var user = new User(id, GetUserCards(id));
        return user;
    }
    private IEnumerable<Card> GetUserCards(string userId)
    {
        using var connection = new SQLiteConnection(_connectionString);
        connection.Open();

        var command = connection.CreateCommand();
        command.CommandText =
            @"
                SELECT Cards.Id, Units, Remaining, Cards.Timestamp
                FROM Cards
                JOIN UserCards ON Cards.Id = UserCards.CardId
                WHERE UserCards.UserId = @UserId;
            ";

        command.Parameters.AddWithValue("@UserId", userId);

        using var reader = command.ExecuteReader();
        while (reader.Read())
        {
            yield return new Card(
                reader.GetString(0),
                reader.GetDouble(1),
                reader.GetInt32(2),
                reader.GetInt64(3)
            );
        }
    }

    public void AddOrUpdateCard(Card card)
    {
        using var connection = new SQLiteConnection(_connectionString);
        connection.Open();

        using var transaction = connection.BeginTransaction();

        var command = connection.CreateCommand();
        command.Transaction = transaction;
        command.CommandText =
        @"
            INSERT OR REPLACE INTO Cards (Id, Units, Remaining, Timestamp)
            VALUES (@Id, @Units, @Remaining, @Timestamp);
        ";

        command.Parameters.AddWithValue("@Id", card.Id);
        command.Parameters.AddWithValue("@Units", card.Units);
        command.Parameters.AddWithValue("@Remaining", card.Remaining);
        command.Parameters.AddWithValue("@Timestamp", card.Timestamp);
        command.ExecuteNonQuery();

        transaction.Commit();
    }

    public Card? GetCard(string cardId)
    {
        using var connection = new SQLiteConnection(_connectionString);
        connection.Open();

        var command = connection.CreateCommand();
        command.CommandText =
        @"
            SELECT Id, Units, Remaining, Timestamp FROM Cards WHERE Id = @CardId;
        ";

        command.Parameters.AddWithValue("@CardId", cardId);

        using var reader = command.ExecuteReader();
        if (reader.Read())
        {
            return new Card(
                reader.GetString(0),
                reader.GetDouble(1),
                reader.GetInt32(2),
                reader.GetInt64(3)
            );
        }

        return null;
    }

    public void AssignCardToUser(string userId, string cardId, long timestamp)
    {
        using var connection = new SQLiteConnection(_connectionString);
        connection.Open();

        using var transaction = connection.BeginTransaction();

        var command = connection.CreateCommand();
        command.Transaction = transaction;
        command.CommandText =
        @"
            INSERT OR REPLACE INTO UserCards (UserId, CardId, Timestamp)
            VALUES (@UserId, @CardId, @Timestamp);
        ";

        command.Parameters.AddWithValue("@UserId", userId);
        command.Parameters.AddWithValue("@CardId", cardId);
        command.Parameters.AddWithValue("@Timestamp", timestamp);
        command.ExecuteNonQuery();

        transaction.Commit();
    }
}