Bahasanya berganti, tetapi posisi kita tetap sama: di sisi aplikasi yang harus membuka koneksi, mengirim query, dan menerima baris. Python adalah bahasa yang paling akrab dengan data, dan ekosistemnya menyediakan dua driver MySQL resmi. Kabar baik untuk bab ini: salah satunya — PyMySQL — sudah terpasang di lingkungan latihan course ini, sehingga untuk pertama kalinya dalam section ini kita menjalankan kode aplikasi sungguhan, bukan ilustratif. Kita akan menulis satu file contoh.py yang lengkap, menjalankannya lewat terminal, membedah isinya baris per baris, lalu menutupnya dengan pola with yang membuat koneksi tertutup sendiri.

Dua Driver Resmi: PyMySQL dan mysql-connector-python #

Python mengakses MySQL lewat dua paket yang sama-sama mengikuti standar API database Python (PEP 249, lazim disebut DB-API 2.0). mysql-connector-python adalah driver resmi dari Oracle — ditulis dengan ekstensi C yang cepat dan terintegrasi dengan tooling MySQL. PyMySQL adalah driver murni Python: tanpa dependensi kompilasi, mudah dipasang di mana saja, dan kode sumbernya bisa dibaca langsung untuk belajar protokol MySQL. Keduanya menyediakan nama yang sama — connect(), cursor(), execute(), fetchall() — sehingga kode yang kita tulis di chapter ini mudah dipindahkan antar keduanya.

Untuk latihan, kita memakai PyMySQL:

bash
pip install pymysql

Menyiapkan Data Latihan #

File Python kita akan membaca tabel produk dari database belajar_app. Seperti biasa, kita bangun dulu skemanya lewat klien mysql supaya chapter ini mandiri:

sql
DROP DATABASE IF EXISTS belajar_app;
CREATE DATABASE belajar_app;
USE belajar_app;

CREATE TABLE produk (
  id    INT PRIMARY KEY,
  nama  VARCHAR(100) NOT NULL,
  harga INT NOT NULL
);

INSERT INTO produk (id, nama, harga) VALUES
  (1, 'Kopi Toraja', 52000),
  (2, 'Teh Melati', 28000),
  (3, 'Gula Aren', 32000);

Satu File Lengkap: contoh.py #

Berikut isi lengkap contoh.py — file ini tersimpan di dalam bundle chapter (bagian Berkas Sumber di akhir halaman), dan akan kita jalankan beberapa saat lagi:

python
"""contoh.py — tiga pola dasar akses MySQL dari Python dengan PyMySQL.

Cara menjalankan dari folder chapter:
    python3 contoh.py

Koneksi memakai port 3306 secara default; timpa lewat variabel
lingkungan DB_PORT bila server MySQL Anda berjalan di port lain:
    DB_PORT=3307 python3 contoh.py
"""

import os

import pymysql

port = int(os.environ.get("DB_PORT", "3306"))

with pymysql.connect(
    host="127.0.0.1",
    user="root",
    password="",
    database="belajar_app",
    port=port,
) as koneksi:
    with koneksi.cursor() as kursor:
        kursor.execute("SELECT id, nama, harga FROM produk ORDER BY id")
        print("Daftar produk:")
        for id_produk, nama, harga in kursor.fetchall():
            print(f"  {id_produk} | {nama} | {harga}")

    with koneksi.cursor() as kursor:
        kursor.execute(
            "SELECT id, nama FROM produk WHERE harga < %s ORDER BY id",
            (40000,),
        )
        print("Produk di bawah 40000:")
        for id_produk, nama in kursor.fetchall():
            print(f"  {id_produk} | {nama}")

print("Koneksi ditutup.")

Menjalankan contoh.py dari Terminal #

Eksekusi program dari terminal — di komputer Anda, cukup python3 contoh.py dari dalam folder chapter; server MySQL standar berjalan di port 3306 dan program memakai nilai itu secara default:

bash
-- @test-bash
DB_PORT=3306 python3 content/mysql/integrasi-aplikasi/mysql-dan-python/contoh.py

Output yang Dihasilkan:

text
Daftar produk:
  1 | Kopi Toraja | 52000
  2 | Teh Melati | 28000
  3 | Gula Aren | 32000
Produk di bawah 40000:
  2 | Teh Melati
  3 | Gula Aren
Koneksi ditutup.

Bedah Kode Baris-per-Baris #

  1. Docstring pembuka ("""contoh.py — ..."""): dokumentasi kecil yang menjelaskan cara menjalankan file — kebiasaan lama yang menyelamatkan kita (dan rekan tim) enam bulan kemudian.
  2. port = int(os.environ.get("DB_PORT", "3306")): membaca port dari lingkungan dengan nilai bawaan "3306". os.environ.get mengembalikan string, sehingga int() mengubahnya menjadi angka yang diminta parameter port.
  3. pymysql.connect(host=..., user=..., password=..., database=..., port=...): membuka koneksi TCP ke server. Password kosong ditulis sebagai string "" — di produksi, baca kredensial dari variabel lingkungan, bukan hardcode. Objek koneksi diikat sebagai koneksi oleh klausa as.
  4. with ... as koneksi:: PyMySQL mendukung context manager pada objek koneksi — saat blok with berakhir (secara normal maupun lewat exception), metode close() dipanggil otomatis. Kita tidak pernah memanggil koneksi.close() sendiri di file ini.
  5. with koneksi.cursor() as kursor:: cursor adalah objek yang mengirim statement dan menampung hasil; with di sini juga menutup cursor secara otomatis. Satu koneksi boleh membuat banyak cursor bergantian.
  6. kursor.execute("SELECT ..."): mengirim satu statement. Hasilnya tidak langsung berupa daftar — cursor menyimpannya di sisi klien dan kita mengambilnya dengan metode fetch.
  7. kursor.fetchall(): mengambil seluruh baris sebagai tuple of tuples — (1, 'Kopi Toraja', 52000); perulangan for membongkarnya langsung menjadi tiga variabel, teknik yang disebut tuple unpacking.
  8. kursor.execute("... WHERE harga < %s ...", (40000,)): parameterized query. Placeholder %s ditulis di teks SQL, nilai dikirim sebagai tuple (40000,) — perhatikan koma: tuple satu elemen wajib memakai koma. Nilai tidak pernah digabung ke string SQL; driver yang menanganinya sebagai data.
  9. print(f" {id_produk} | {nama}"): f-string memformat baris menjadi output yang rapi.
  10. print("Koneksi ditutup.") di luar blok with: bukti urutan — baris ini hanya tercetak setelah blok koneksi selesai, artinya setelah close() otomatis dijalankan.

Placeholder %s dan Kenapa Aman #

Kedua query di file kita memakai dua bentuk yang berbeda dengan sengaja: query pertama tanpa parameter (aman karena tanpa input), query kedua dengan %s. Tidak ada versi Python yang merakit string SQL dengan + nama + " — sejak awal kita membiasakan pola parameterized, karena pola rakit-rakit itulah akar masalah SQL injection. Driver mengirim nilai terpisah dari teks statement, sehingga tanda kutip, titik koma, atau komentar dalam input pengguna diperlakukan sebagai data biasa. Chapter Prepared Statements dalam Aplikasi menguji perbandingan pola rentan versus pola aman ini secara nyata.

Context Manager dan Commit #

Pola with yang kita pakai punya satu tugas: menutup. Untuk statement yang mengubah data (INSERT/UPDATE), PyMySQL memulai transaksi implisit dan menunggu kita memutuskan — koneksi.commit() untuk menyimpan, koneksi.rollback() untuk membatalkan. Bila blok with berakhir tanpa commit(), penutupan koneksi membatalkan seluruh perubahan yang belum di-commit — perilaku yang aman secara default, tetapi mengejutkan bagi yang mengira with otomatis commit. Semantik transaksi ini sudah kita bahas tuntas di section transaksi; di sini kita cukup mengingat satu kalimat: commit tetap eksplisit.

Membersihkan Latihan #

Program Python kita hanya membaca, jadi tabel masih utuh setelah contoh.py berjalan. Kita jatuhkan database latihan lewat terminal — dan karena program tadi sudah selesai, urutan pembersihannya tepat:

bash
-- @test-bash
mysql -h 127.0.0.1 -P 3306 -u root -e "DROP DATABASE IF EXISTS belajar_app;"

Referensi #

Ekosistem Python memilih gaya API sinkron dan eksplisit; JavaScript di Node.js memilih gaya asinkron dengan Promise dan await. Cara keduanya menampung baris hasil pun berbeda — array objek siap pakai di Node.js versus tuple mentah di Python. Mari kita bandingkan langsung di chapter MySQL dan Node.js.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.py

Unduh berkas
python
"""contoh.py — tiga pola dasar akses MySQL dari Python dengan PyMySQL.

Cara menjalankan dari folder chapter:
    python3 contoh.py

Koneksi memakai port 3306 secara default; timpa lewat variabel
lingkungan DB_PORT bila server MySQL Anda berjalan di port lain:
    DB_PORT=3307 python3 contoh.py
"""

import os

import pymysql

port = int(os.environ.get("DB_PORT", "3306"))

with pymysql.connect(
    host="127.0.0.1",
    user="root",
    password="",
    database="belajar_app",
    port=port,
) as koneksi:
    with koneksi.cursor() as kursor:
        kursor.execute("SELECT id, nama, harga FROM produk ORDER BY id")
        print("Daftar produk:")
        for id_produk, nama, harga in kursor.fetchall():
            print(f"  {id_produk} | {nama} | {harga}")

    with koneksi.cursor() as kursor:
        kursor.execute(
            "SELECT id, nama FROM produk WHERE harga < %s ORDER BY id",
            (40000,),
        )
        print("Produk di bawah 40000:")
        for id_produk, nama in kursor.fetchall():
            print(f"  {id_produk} | {nama}")

print("Koneksi ditutup.")