Wykonywanie operacji CRUD
Opanuj tworzenie, odczytywanie, aktualizowanie i usuwanie danych w relacyjnych bazach danych z poziomu Clojure.
Wykonywanie operacji CRUD to bezpłatna lekcja Clojure Functional Programming & JVM Backend Development na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Clojure Functional Programming & JVM Backend Development, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Clojure Functional Programming & JVM Backend Development zawiera 4 lekcji w sumie.
Części tej lekcji nie zostały jeszcze przetłumaczone i są wyświetlane po angielsku.
What is CRUD?
When you build applications, you'll constantly interact with databases. The fundamental operations for managing data are often summarized by the acronym CRUD.
- Create: Adding new data.
- Read: Retrieving existing data.
- Update: Modifying existing data.
- Delete: Removing data.
In this lesson, we'll master how to perform these essential operations using Clojure's next.jdbc library.
Database Connection Setup
Before we perform CRUD operations, we need a database connection. We'll use next.jdbc with an in-memory SQLite database for our examples.
Remember from the previous lesson, we define a database specification (db-spec) and obtain a data source (ds) or a direct connection.
We'll create a simple users table for our examples.
Create: Inserting New Records
To add new data, we use the next.jdbc.sql/insert! function. It takes a connection, table name, and a map of column-value pairs.
Try running this example to create a users table and insert your first user:
(ns coddykit.core
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]))
(defn -main
"Insert a new user into the database."
[& args]
(let [db-spec {:dbtype "sqlite" :dbname ":memory:"}]
(with-open [conn (jdbc/get-connection db-spec)]
(jdbc/execute! conn ["DROP TABLE IF EXISTS users;"])
(jdbc/execute! conn ["CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"])
(println "Inserting new user...")
(let [new-user {:name "Alice" :email "alice@example.com"}
result (sql/insert! conn :users new-user)]
(println "Insert result:" result))
(println "\nUsers in database:")
(doseq [user (sql/select! conn ["SELECT * FROM users"])]
(println user)))))Create: Inserting Multiple Records
What if you need to add many records at once? The next.jdbc.sql/insert-multi! function is perfect for this. It takes a sequence of maps, each representing a row to insert.
It's more efficient than calling insert! repeatedly in a loop.
(ns coddykit.core
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]))
(defn -main
"Insert multiple users into the database."
[& args]
(let [db-spec {:dbtype "sqlite" :dbname ":memory:"}]
(with-open [conn (jdbc/get-connection db-spec)]
(jdbc/execute! conn ["DROP TABLE IF EXISTS users;"])
(jdbc/execute! conn ["CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"])
(println "Inserting multiple users...")
(let [new-users [{:name "Bob" :email "bob@example.com"}
{:name "Charlie" :email "charlie@example.com"}]
result (sql/insert-multi! conn :users new-users)]
(println "Insert result:" result))
(println "\nUsers in database:")
(doseq [user (sql/select! conn ["SELECT * FROM users"])]
(println user)))))Read: Fetching All Data
To retrieve all records from a table, use next.jdbc.sql/select-all!. It takes a connection and the table name (as a keyword or string).
The function returns a sequence of maps, where each map represents a row.
(ns coddykit.core
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]))
(defn -main
"Select all users from the database."
[& args]
(let [db-spec {:dbtype "sqlite" :dbname ":memory:"}]
(with-open [conn (jdbc/get-connection db-spec)]
(jdbc/execute! conn ["DROP TABLE IF EXISTS users;"])
(jdbc/execute! conn ["CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"])
(sql/insert! conn :users {:name "Alice" :email "alice@example.com"})
(sql/insert! conn :users {:name "Bob" :email "bob@example.com"})
(println "Fetching all users...")
(let [all-users (sql/select-all! conn :users)]
(doseq [user all-users]
(println user))))))Read: Filtering Data with `WHERE`
Often, you only want specific records. Use next.jdbc.sql/select! with a WHERE clause to filter results. The WHERE clause is provided as a map.
For example, to find users with a specific name or email:
(ns coddykit.core
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]))
(defn -main
"Select users with a specific name."
[& args]
(let [db-spec {:dbtype "sqlite" :dbname ":memory:"}]
(with-open [conn (jdbc/get-connection db-spec)]
(jdbc/execute! conn ["DROP TABLE IF EXISTS users;"])
(jdbc/execute! conn ["CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"])
(sql/insert! conn :users {:name "Alice" :email "alice@example.com"})
(sql/insert! conn :users {:name "Bob" :email "bob@example.com"})
(sql/insert! conn :users {:name "Alice" :email "alice2@example.com"})
(println "Fetching users named Alice...")
(let [alices (sql/select! conn :users {:name "Alice"})]
(doseq [user alices]
(println user))))))Update: Modifying Existing Data
To change existing records, use next.jdbc.sql/update!. It takes a connection, table name, a map of columns to update, and a WHERE clause map to specify which records to modify.
Always be careful with your WHERE clause when updating!
(ns coddykit.core
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]))
(defn -main
"Update a user's email address."
[& args]
(let [db-spec {:dbtype "sqlite" :dbname ":memory:"}]
(with-open [conn (jdbc/get-connection db-spec)]
(jdbc/execute! conn ["DROP TABLE IF EXISTS users;"])
(jdbc/execute! conn ["CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"])
(sql/insert! conn :users {:name "Alice" :email "alice@example.com"})
(println "Original user:")
(println (first (sql/select! conn :users {:name "Alice"})))
(println "\nUpdating Alice's email...")
(let [result (sql/update! conn :users {:email "alice.new@example.com"} {:name "Alice"})]
(println "Update result:" result))
(println "\nUpdated user:")
(println (first (sql/select! conn :users {:name "Alice"})))))Delete: Removing Data
To remove records from a table, use next.jdbc.sql/delete!. Similar to update!, it requires a connection, table name, and a WHERE clause map to specify which records to remove.
A missing or empty WHERE clause can delete ALL records, so use with extreme caution!
(ns coddykit.core
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]))
(defn -main
"Delete a user from the database."
[& args]
(let [db-spec {:dbtype "sqlite" :dbname ":memory:"}]
(with-open [conn (jdbc/get-connection db-spec)]
(jdbc/execute! conn ["DROP TABLE IF EXISTS users;"])
(jdbc/execute! conn ["CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"])
(sql/insert! conn :users {:name "Alice" :email "alice@example.com"})
(sql/insert! conn :users {:name "Bob" :email "bob@example.com"})
(println "Users before delete:")
(doseq [user (sql/select-all! conn :users)] (println user))
(println "\nDeleting Bob...")
(let [result (sql/delete! conn :users {:name "Bob"})]
(println "Delete result:" result))
(println "\nUsers after delete:")
(doseq [user (sql/select-all! conn :users)] (println user)))))Atomic Operations with Transactions
Sometimes you need multiple CRUD operations to succeed or fail as a single, atomic unit. This is where transactions come in.
If any operation within a transaction fails, all changes are rolled back, ensuring data consistency. Use next.jdbc/with-transaction for this.
(ns coddykit.core
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]))
(defn -main
"Demonstrate a database transaction."
[& args]
(let [db-spec {:dbtype "sqlite" :dbname ":memory:"}]
(with-open [conn (jdbc/get-connection db-spec)]
(jdbc/execute! conn ["DROP TABLE IF EXISTS accounts;"])
(jdbc/execute! conn ["CREATE TABLE accounts (id INTEGER PRIMARY KEY, name TEXT, balance INTEGER)"])
(sql/insert! conn :accounts {:name "Alice" :balance 100})
(sql/insert! conn :accounts {:name "Bob" :balance 50})
(println "Balances before transaction:")
(doseq [acc (sql/select-all! conn :accounts)] (println acc))
(println "\nAttempting to transfer $20 from Alice to Bob...")
(try
(jdbc/with-transaction [tx conn]
(sql/update! tx :accounts {:balance [- :balance 20]} {:name "Alice"})
(sql/update! tx :accounts {:balance [+ :balance 20]} {:name "Bob"})
(println "Transfer successful!"))
(catch Exception e
(println "Transfer failed:" (.getMessage e))))
(println "\nBalances after transaction:")
(doseq [acc (sql/select-all! conn :accounts)] (println acc)))))CRUD Quick Check
You've learned the core functions for interacting with databases. Let's test your understanding!
Recap: Mastering CRUD
Congratulations! You've mastered the essential CRUD operations for database interaction in Clojure using next.jdbc:
- Create:
insert!andinsert-multi!to add data. - Read:
select!andselect-all!to retrieve data. - Update:
update!to modify existing data. - Delete:
delete!to remove data.
You also learned about using transactions with with-transaction for atomic operations, ensuring data integrity. These are the building blocks for almost any data-driven application!
Często zadawane pytania
Czy lekcja „Wykonywanie operacji CRUD” jest bezpłatna?
Tak — pełny tekst „Wykonywanie operacji CRUD” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Clojure Functional Programming & JVM Backend Development, przejdź na CoddyKit PRO. Kurs Clojure Functional Programming & JVM Backend Development zawiera 4 lekcji w sumie.
Co nauczysz się w „Wykonywanie operacji CRUD”?
Opanuj tworzenie, odczytywanie, aktualizowanie i usuwanie danych w relacyjnych bazach danych z poziomu Clojure. Ćwiczysz Clojure Functional Programming & JVM Backend Development z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Clojure Functional Programming & JVM Backend Development?
Nie wymagamy żadnego doświadczenia. Clojure Functional Programming & JVM Backend Development w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Wykonywanie operacji CRUD”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Clojure Functional Programming & JVM Backend Development?
Tak. Każda lekcja Clojure Functional Programming & JVM Backend Development zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Łączenie z bazami danych za pomocą next.jdbc
- Wykonywanie operacji CRUD
- Migracje baz danych i zarządzanie schematem
- Pule połączeń i transakcje