Bonus Materi - Migrasi SQLite ke MySQL

Bonus Materi - Migrasi SQLite ke MySQL

Materi pengayaan opsional untuk memindahkan aplikasi penjualan dari SQLite ke MySQL, lengkap dengan langkah dan contoh kode.

Tentang Materi Ini

Sifat: OPSIONAL (Pengayaan). Materi ini tidak diujikan dan bukan bagian dari praktikum wajib. Dikerjakan bagi mahasiswa yang ingin tantangan tambahan, dan bernilai poin bonus.

Prasyarat: sudah menyelesaikan Pertemuan 10 (SQLite Database).

Mengapa materi ini disediakan? Di dunia kerja dan pada penyusunan skripsi, MySQL jauh lebih sering dipakai daripada SQLite. Kabar baiknya, hampir semua yang sudah dipelajari langsung terpakai — yang berubah terutama cara koneksi dan sedikit penulisan tipe data.

Tujuan

Setelah menyelesaikan materi bonus ini, mahasiswa mampu:

  1. Menjelaskan perbedaan mendasar SQLite dan MySQL.
  2. Memasang MySQL (melalui XAMPP) dan membuat database.
  3. Menghubungkan Python dengan MySQL memakai mysql-connector-python.
  4. Membuat ulang keempat tabel aplikasi penjualan di MySQL.
  5. Memindahkan data dari toko.db (SQLite) ke MySQL.

Konsep Singkat

SQLite vs MySQL

Aspek SQLite MySQL
Bentuk Satu file (toko.db) Server (berjalan sebagai layanan)
Instalasi Tidak perlu (bawaan Python) Perlu dipasang (mis. lewat XAMPP)
Pemakaian Aplikasi kecil, belajar, prototipe Aplikasi banyak pengguna, web produksi
Akses bersamaan Terbatas Kuat, banyak pengguna sekaligus
Login Tidak ada Ada (user & password)
Modul Python sqlite3 (bawaan) mysql-connector-python (dipasang)

Yang Berubah dan Yang Tetap Sama

TETAP SAMA (inilah alasan ilmunya transferable):

  • Perintah SQL: CREATE TABLE, INSERT, SELECT, UPDATE, DELETE, WHERE, ORDER BY, JOIN.
  • Konsep Primary Key, Foreign Key, dan relasi master–detail.
  • Pola Python: cursor.execute(), fetchall(), commit(), close().

BERUBAH (hanya tiga hal):

Hal SQLite MySQL
Cara koneksi sqlite3.connect("file.db") mysql.connector.connect(host, user, password, database)
Auto increment INTEGER PRIMARY KEY AUTOINCREMENT INT AUTO_INCREMENT PRIMARY KEY
Penanda parameter ? %s
Tiga perbedaan di atas adalah inti dari seluruh materi bonus ini. Sisanya sama persis.

Langkah 1 - Memasang MySQL dengan XAMPP

Cara termudah memasang MySQL adalah lewat XAMPP (paket yang berisi MySQL dan phpMyAdmin).

  1. Unduh XAMPP dari https://www.apachefriends.org, lalu pasang.
  2. Buka XAMPP Control Panel.
  3. Klik tombol Start pada baris MySQL (Apache juga di-start bila ingin memakai phpMyAdmin).
  4. Pastikan statusnya berubah menjadi hijau (berjalan).
Catatan: pengaturan bawaan XAMPP adalah user root dengan password kosong. Kita pakai itu untuk latihan.

Langkah 2 - Membuat Database

Cara A - Lewat phpMyAdmin (visual)

  1. Buka browser, kunjungi http://localhost/phpmyadmin.
  2. Klik menu Databases.
  3. Ketik nama database: toko_elektronik, lalu klik Create.

Cara B - Lewat Python

Database juga bisa dibuat dari Python (perhatikan: koneksi awal tanpa menyebut nama database):

python
import mysql.connector

conn = mysql.connector.connect(
    host="localhost",
    user="root",
    password="",
)
cursor = conn.cursor()
cursor.execute("CREATE DATABASE IF NOT EXISTS toko_elektronik")
print("Database berhasil dibuat.")

conn.close()

Langkah 3 - Memasang Konektor Python

Aktifkan virtual environment project, lalu pasang konektor MySQL:

bash
venv\Scripts\activate
pip install mysql-connector-python
pip freeze > requirements.txt
macOS / Linux: aktifkan dengan source venv/bin/activate.

Langkah 4 - Menghubungkan Python dengan MySQL

Buat file koneksi_mysql.py. Bandingkan dengan versi SQLite di Pertemuan 10 — hanya bagian koneksinya yang berbeda.

python
import mysql.connector

conn = mysql.connector.connect(
    host="localhost",
    user="root",
    password="",
    database="toko_elektronik",
)
cursor = conn.cursor()

print("Koneksi MySQL berhasil.")

conn.close()

Jalankan:

bash
python koneksi_mysql.py

Output:

text
Koneksi MySQL berhasil.

Menyimpan Pengaturan Koneksi

Agar rapi, simpan pengaturan koneksi dalam satu fungsi (nanti dipakai berulang):

python
import mysql.connector


def get_connection():
    return mysql.connector.connect(
        host="localhost",
        user="root",
        password="",
        database="toko_elektronik",
    )

Langkah 5 - Membuat Tabel di MySQL

Buat file setup_mysql.py. Struktur tabelnya sama dengan Pertemuan 10; yang berubah hanya penulisan tipe data dan auto increment.

python
import mysql.connector

conn = mysql.connector.connect(
    host="localhost", user="root", password="", database="toko_elektronik"
)
cursor = conn.cursor()

# --- Tabel produk ---
cursor.execute("""
CREATE TABLE IF NOT EXISTS produk (
    id_produk   INT AUTO_INCREMENT PRIMARY KEY,
    nama_produk VARCHAR(100) NOT NULL,
    kategori    VARCHAR(50)  NOT NULL,
    harga       INT          NOT NULL,
    stok        INT          NOT NULL DEFAULT 0
)
""")

# --- Tabel pelanggan ---
cursor.execute("""
CREATE TABLE IF NOT EXISTS pelanggan (
    id_pelanggan INT AUTO_INCREMENT PRIMARY KEY,
    nama         VARCHAR(100) NOT NULL,
    telepon      VARCHAR(20),
    alamat       VARCHAR(255)
)
""")

# --- Tabel penjualan (master) ---
cursor.execute("""
CREATE TABLE IF NOT EXISTS penjualan (
    id_penjualan INT AUTO_INCREMENT PRIMARY KEY,
    tanggal      DATE NOT NULL,
    id_pelanggan INT  NOT NULL,
    total        INT  NOT NULL DEFAULT 0,
    FOREIGN KEY (id_pelanggan) REFERENCES pelanggan(id_pelanggan)
)
""")

# --- Tabel detail_penjualan (detail) ---
cursor.execute("""
CREATE TABLE IF NOT EXISTS detail_penjualan (
    id_detail    INT AUTO_INCREMENT PRIMARY KEY,
    id_penjualan INT NOT NULL,
    id_produk    INT NOT NULL,
    qty          INT NOT NULL,
    subtotal     INT NOT NULL,
    FOREIGN KEY (id_penjualan) REFERENCES penjualan(id_penjualan),
    FOREIGN KEY (id_produk)    REFERENCES produk(id_produk)
)
""")

conn.commit()
conn.close()
print("Empat tabel MySQL berhasil dibuat.")

Perbandingan Tipe Data

SQLite MySQL Keterangan
INTEGER PRIMARY KEY AUTOINCREMENT INT AUTO_INCREMENT PRIMARY KEY Kunci utama otomatis
TEXT VARCHAR(100) Teks — MySQL perlu batas panjang
TEXT (untuk tanggal) DATE MySQL punya tipe tanggal khusus
INTEGER INT Bilangan bulat
REAL DECIMAL(10,2) Bilangan desimal (mis. uang)
Perbedaan penting: MySQL mewajibkan panjang teks (VARCHAR(100)), sedangkan SQLite tidak. MySQL juga punya tipe DATE sungguhan, sementara di SQLite tanggal disimpan sebagai teks.

Langkah 6 - Mengisi Data (Perhatikan Tanda %s)

Inilah perbedaan ketiga: penanda parameter berubah dari ? menjadi %s.

python
import mysql.connector

conn = mysql.connector.connect(
    host="localhost", user="root", password="", database="toko_elektronik"
)
cursor = conn.cursor()

daftar_produk = [
    ("Laptop", "Elektronik", 8500000, 10),
    ("Monitor", "Elektronik", 2000000, 15),
    ("Printer", "Elektronik", 1500000, 8),
    ("Mouse", "Aksesoris", 150000, 50),
    ("Keyboard", "Aksesoris", 250000, 40),
]

# SQLite : VALUES (?, ?, ?, ?)
# MySQL  : VALUES (%s, %s, %s, %s)
cursor.executemany(
    "INSERT INTO produk (nama_produk, kategori, harga, stok) VALUES (%s, %s, %s, %s)",
    daftar_produk,
)

cursor.executemany(
    "INSERT INTO pelanggan (nama, telepon, alamat) VALUES (%s, %s, %s)",
    [("Budi Santoso", "08123456789", "Jakarta"),
     ("Siti Aminah", "08234567890", "Tangerang")],
)

conn.commit()
print(cursor.rowcount, "baris terakhir dimasukkan.")
conn.close()

Membaca data — perintah SELECT sama persis seperti SQLite:

python
cursor.execute("SELECT * FROM produk")
for baris in cursor.fetchall():
    print(baris)

# dengan syarat (perhatikan %s)
cursor.execute("SELECT * FROM produk WHERE kategori = %s", ("Elektronik",))
print(cursor.fetchall())
Jebakan paling sering: lupa mengganti ? menjadi %s. Gejalanya muncul error sintaks SQL. Ganti semuanya, maka kode langsung jalan.

Langkah 7 - Memindahkan Data dari SQLite ke MySQL

Inilah inti "migrasi": membaca data lama dari toko.db, lalu menuliskannya ke MySQL. Buat file migrasi.py.

python
import sqlite3
import mysql.connector

# --- sumber: SQLite ---
lite_conn = sqlite3.connect("database/toko.db")
lite_cur = lite_conn.cursor()

# --- tujuan: MySQL ---
my_conn = mysql.connector.connect(
    host="localhost", user="root", password="", database="toko_elektronik"
)
my_cur = my_conn.cursor()

# urutan penting: tabel induk lebih dulu, agar Foreign Key tidak gagal
urutan_tabel = ["pelanggan", "produk", "penjualan", "detail_penjualan"]

for tabel in urutan_tabel:
    lite_cur.execute(f"SELECT * FROM {tabel}")
    baris_data = lite_cur.fetchall()

    if not baris_data:
        print(f"{tabel}: kosong, dilewati.")
        continue

    # ambil nama kolom dari hasil query
    kolom = [d[0] for d in lite_cur.description]
    nama_kolom = ", ".join(kolom)
    penanda = ", ".join(["%s"] * len(kolom))     # -> %s, %s, %s, ...

    sql = f"INSERT INTO {tabel} ({nama_kolom}) VALUES ({penanda})"
    my_cur.executemany(sql, baris_data)
    print(f"{tabel}: {len(baris_data)} baris dipindahkan.")

my_conn.commit()
lite_conn.close()
my_conn.close()
print("Migrasi selesai.")

Output:

text
pelanggan: 2 baris dipindahkan.
produk: 5 baris dipindahkan.
penjualan: 1 baris dipindahkan.
detail_penjualan: 2 baris dipindahkan.
Migrasi selesai.
Mengapa urutan tabel penting? Karena penjualan memiliki Foreign Key ke pelanggan. Bila penjualan dimasukkan lebih dulu sementara pelanggan masih kosong, MySQL akan menolak datanya. Aturannya: tabel induk dulu, tabel anak kemudian.

Langkah 8 - Membuktikan Relasi Bekerja di MySQL

Uji dengan JOIN — perintahnya identik dengan Pertemuan 10.

python
import mysql.connector

conn = mysql.connector.connect(
    host="localhost", user="root", password="", database="toko_elektronik"
)
cursor = conn.cursor()

query = """
SELECT p.id_penjualan, p.tanggal, c.nama, pr.nama_produk, d.qty, d.subtotal
FROM penjualan p
JOIN pelanggan c        ON p.id_pelanggan = c.id_pelanggan
JOIN detail_penjualan d ON p.id_penjualan = d.id_penjualan
JOIN produk pr          ON d.id_produk = pr.id_produk
"""

cursor.execute(query)
for baris in cursor.fetchall():
    print(baris)

conn.close()

Output:

text
(1, datetime.date(2025, 3, 1), 'Budi Santoso', 'Laptop', 1, 8500000)
(1, datetime.date(2025, 3, 1), 'Budi Santoso', 'Mouse', 2, 300000)

Hasilnya sama seperti SQLite. Perbedaannya hanya kolom tanggal kini bertipe date sungguhan (bukan teks), karena MySQL memiliki tipe DATE.

Referensi Perintah Penting

Perbandingan Kode SQLite vs MySQL

Kegiatan SQLite MySQL
Import import sqlite3 import mysql.connector
Koneksi sqlite3.connect("toko.db") mysql.connector.connect(host=..., user=..., password=..., database=...)
Cursor conn.cursor() conn.cursor()
Parameter VALUES (?, ?) VALUES (%s, %s)
Auto increment INTEGER PRIMARY KEY AUTOINCREMENT INT AUTO_INCREMENT PRIMARY KEY
Teks TEXT VARCHAR(100)
Id terakhir cursor.lastrowid cursor.lastrowid
Simpan conn.commit() conn.commit()
Tutup conn.close() conn.close()

Menangani Error Koneksi

python
import mysql.connector

try:
    conn = mysql.connector.connect(
        host="localhost", user="root", password="", database="toko_elektronik"
    )
    print("Terhubung.")
    conn.close()
except mysql.connector.Error as e:
    print("Gagal terhubung:", e)

Troubleshooting

Masalah Penyebab Solusi
Can't connect to MySQL server MySQL belum dijalankan Buka XAMPP, klik Start pada MySQL
Access denied for user 'root' Password salah Bawaan XAMPP: password kosong (password="")
Unknown database 'toko_elektronik' Database belum dibuat Buat lewat phpMyAdmin atau Langkah 2
You have an error in your SQL syntax Masih memakai ? Ganti seluruh ? menjadi %s
Cannot add or update a child row Urutan pengisian salah (FK gagal) Isi tabel induk (pelanggan, produk) lebih dulu
Duplicate entry for key PRIMARY Data sudah pernah dimasukkan Kosongkan tabel, atau jangan jalankan skrip dua kali
No module named 'mysql' Konektor belum dipasang pip install mysql-connector-python
Port 3306 bentrok Ada MySQL lain berjalan Matikan layanan MySQL lain, atau ubah port di XAMPP

Tugas Bonus

Kerjakan bila ingin memperoleh nilai tambahan.

  1. Pasang XAMPP, jalankan MySQL, dan buat database toko_elektronik.
  2. Buat keempat tabel di MySQL (produk, pelanggan, penjualan, detail_penjualan) beserta relasinya.
  3. Pindahkan seluruh data dari toko.db ke MySQL menggunakan skrip migrasi.
  4. Buktikan migrasi berhasil dengan menampilkan:
  • Jumlah baris tiap tabel di MySQL.
  • Hasil query JOIN (isi nota) — harus sama dengan hasil di SQLite.
  1. Tantangan: buat satu transaksi baru langsung di MySQL (satu nota berisi 2 barang, stok berkurang, total benar).

Kumpulkan: file .py yang dibuat, tangkapan layar phpMyAdmin yang memperlihatkan tabel beserta isinya, dan tangkapan layar hasil menjalankan program.

Kesimpulan

Materi bonus ini memperlihatkan bahwa berpindah dari SQLite ke MySQL jauh lebih mudah daripada yang dibayangkan. Struktur database, perintah SQL, konsep Primary Key dan Foreign Key, serta pola kerja cursor seluruhnya tetap sama. Yang benar-benar berubah hanya tiga: cara koneksi, penulisan auto increment dan tipe data, serta penanda parameter (? menjadi %s). Dengan memahami ini, keterampilan database yang dipelajari di mata kuliah ini dapat langsung diterapkan pada aplikasi nyata maupun tugas akhir yang umumnya memakai MySQL.