diff --git a/build.gradle b/build.gradle
index 09c11776..d79e928d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,3 +1,13 @@
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
+    }
+}
+
 plugins {
     id 'java-library'
     id 'com.github.johnrengelman.shadow' version '7.0.0'
@@ -14,6 +24,7 @@ allprojects {
     apply plugin: 'java'
     apply plugin: 'maven-publish'
     apply plugin: 'com.github.johnrengelman.shadow'
+    apply plugin: 'kotlin'
 
     repositories {
         mavenCentral()
@@ -53,6 +64,7 @@ allprojects {
         compileOnly 'com.willfp:eco:6.9.4'
 
         compileOnly 'org.jetbrains:annotations:19.0.0'
+        compileOnly 'org.jetbrains.kotlin:kotlin-stdlib:1.5.21'
 
         compileOnly 'org.projectlombok:lombok:1.18.20'
         annotationProcessor 'org.projectlombok:lombok:1.18.20'
diff --git a/eco-core/core-plugin/build.gradle b/eco-core/core-plugin/build.gradle
index be5052be..2a12f2eb 100644
--- a/eco-core/core-plugin/build.gradle
+++ b/eco-core/core-plugin/build.gradle
@@ -9,4 +9,8 @@ dependencies {
     compileOnly 'net.essentialsx:EssentialsX:2.19.0'
     compileOnly 'io.papermc.paper:paper-api:1.17.1-R0.1-SNAPSHOT'
     compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.4-SNAPSHOT'
+    compileOnly 'org.jetbrains.exposed:exposed-core:0.34.1'
+    compileOnly 'org.jetbrains.exposed:exposed-dao:0.34.1'
+    compileOnly 'org.jetbrains.exposed:exposed-jdbc:0.34.1'
+    compileOnly 'mysql:mysql-connector-java:5.1.48'
 }
\ No newline at end of file
diff --git a/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java b/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java
index 21cca9d2..16261677 100644
--- a/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java
+++ b/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java
@@ -11,6 +11,9 @@ import com.willfp.ecoenchants.command.CommandEnchantinfo;
 import com.willfp.ecoenchants.config.RarityYml;
 import com.willfp.ecoenchants.config.TargetYml;
 import com.willfp.ecoenchants.config.VanillaEnchantsYml;
+import com.willfp.ecoenchants.data.storage.DataHandler;
+import com.willfp.ecoenchants.data.storage.MySQLDataHandler;
+import com.willfp.ecoenchants.data.storage.YamlDataHandler;
 import com.willfp.ecoenchants.display.EnchantDisplay;
 import com.willfp.ecoenchants.enchantments.EcoEnchant;
 import com.willfp.ecoenchants.enchantments.EcoEnchants;
@@ -60,6 +63,12 @@ public class EcoEnchantsPlugin extends EcoPlugin {
     @Getter
     private final VanillaEnchantsYml vanillaEnchantsYml;
 
+    /**
+     * The data handler.
+     */
+    @Getter
+    private final DataHandler dataHandler;
+
     /**
      * Internal constructor called by bukkit on plugin load.
      */
@@ -70,6 +79,8 @@ public class EcoEnchantsPlugin extends EcoPlugin {
         rarityYml = new RarityYml(this);
         targetYml = new TargetYml(this);
         vanillaEnchantsYml = new VanillaEnchantsYml(this);
+        dataHandler = this.getConfigYml().getBool("mysql.enabled")
+                ? new MySQLDataHandler(this) : new YamlDataHandler(this);
     }
 
     @Override
diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/SaveHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/SaveHandler.kt
new file mode 100644
index 00000000..ccbce855
--- /dev/null
+++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/SaveHandler.kt
@@ -0,0 +1,30 @@
+package com.willfp.ecoenchants.data
+
+import com.willfp.ecoenchants.EcoEnchantsPlugin
+import com.willfp.ecoenchants.data.storage.PlayerProfile
+import org.bukkit.Bukkit
+
+class SaveHandler {
+    companion object {
+        fun save(plugin: EcoEnchantsPlugin) {
+            if (Bukkit.getOnlinePlayers().isEmpty()) {
+                return
+            }
+            if (plugin.configYml.getBool("autosave.log")) {
+                plugin.logger.info("Auto-Saving player data!")
+            }
+            PlayerProfile.saveAll(plugin.configYml.getBool("autosave.async"))
+            if (plugin.configYml.getBool("autosave.log")) {
+                plugin.logger.info("Saved data!")
+            }
+        }
+    }
+
+    class Runnable(
+        private val plugin: EcoEnchantsPlugin
+    ) : java.lang.Runnable {
+        override fun run() {
+            save(plugin)
+        }
+    }
+}
\ No newline at end of file
diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/DataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/DataHandler.kt
new file mode 100644
index 00000000..272ea939
--- /dev/null
+++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/DataHandler.kt
@@ -0,0 +1,13 @@
+package com.willfp.ecoenchants.data.storage
+
+import java.util.*
+
+interface DataHandler {
+    fun save()
+
+    fun <T> write(uuid: UUID, key: String, value: T)
+    fun <T> read(uuid: UUID, key: String): T?
+    fun <T : Any> read(uuid: UUID, key: String, default: T): T {
+        return read<T>(uuid, key) ?: default
+    }
+}
\ No newline at end of file
diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/MySQLDataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/MySQLDataHandler.kt
new file mode 100644
index 00000000..fb8f4fb6
--- /dev/null
+++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/MySQLDataHandler.kt
@@ -0,0 +1,69 @@
+package com.willfp.ecoenchants.data.storage
+
+import com.willfp.ecoenchants.EcoEnchantsPlugin
+import org.jetbrains.exposed.dao.id.EntityID
+import org.jetbrains.exposed.dao.id.UUIDTable
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.transactions.transaction
+import java.util.*
+
+@Suppress("UNCHECKED_CAST")
+class MySQLDataHandler(
+    plugin: EcoEnchantsPlugin
+) : DataHandler {
+    init {
+        Database.connect(
+            "jdbc:mysql://" +
+                    "${plugin.configYml.getString("mysql.host")}:" +
+                    "${plugin.configYml.getString("mysql.port")}/" +
+                    plugin.configYml.getString("mysql.database"),
+            driver = "com.mysql.cj.jdbc.Driver",
+            user = plugin.configYml.getString("mysql.user"),
+            password = plugin.configYml.getString("mysql.password")
+        )
+
+        transaction {
+            Players.apply {
+                /*
+                Optional for auto-add fields
+                 */
+            }
+
+            SchemaUtils.create(Players)
+        }
+    }
+
+    override fun save() {
+        // Do nothing
+    }
+
+    override fun <T> write(uuid: UUID, key: String, value: T) {
+        transaction {
+            Players.select { Players.id eq uuid }.firstOrNull() ?: run {
+                Players.insert {
+                    it[this.id] = uuid
+                }
+            }
+            val column: Column<T> = Players.columns.stream().filter { it.name == key }.findFirst().get() as Column<T>
+            Players.update({ Players.id eq uuid }) {
+                it[column] = value
+            }
+        }
+    }
+
+    override fun <T> read(uuid: UUID, key: String): T? {
+        var value: T? = null
+        transaction {
+            val player = Players.select { Players.id eq uuid }.firstOrNull() ?: return@transaction
+            value = player[Players.columns.stream().filter { it.name == key }.findFirst().get()] as T?
+        }
+        return value
+    }
+
+    object Players : UUIDTable("EcoEnchants_Players") {
+        override val id: Column<EntityID<UUID>> = uuid("uuid")
+            .entityId()
+        val descriptions = bool("descriptions")
+            .default(true)
+    }
+}
\ No newline at end of file
diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/PlayerProfile.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/PlayerProfile.kt
new file mode 100644
index 00000000..6dc28943
--- /dev/null
+++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/PlayerProfile.kt
@@ -0,0 +1,85 @@
+package com.willfp.ecoenchants.data.storage
+
+import com.willfp.ecoenchants.EcoEnchantsPlugin
+import org.bukkit.OfflinePlayer
+import java.util.*
+
+@Suppress("UNCHECKED_CAST")
+class PlayerProfile private constructor(
+    private val data: MutableMap<String, Any>
+) {
+    fun <T : Any> write(key: String, value: T) {
+        data[key] = value
+    }
+
+    fun <T : Any> read(key: String, default: T): T {
+        return data[key] as T? ?: default
+    }
+
+    companion object {
+        private val handler = EcoEnchantsPlugin.getInstance().dataHandler
+        private val loaded = mutableMapOf<UUID, PlayerProfile>()
+        private val keys = mutableMapOf<String, Type>()
+
+        private fun load(uuid: UUID): PlayerProfile {
+            val found = loaded[uuid]
+            if (found != null) {
+                return found
+            }
+
+            val data = mutableMapOf<String, Any>()
+            for ((key, type) in keys) {
+                when (type) {
+                    Type.INT -> data[key] = handler.read(uuid, key, 0)
+                    Type.DOUBLE -> data[key] = handler.read(uuid, key, 0.0)
+                    Type.STRING -> data[key] = handler.read(uuid, key, "Unknown")
+                    Type.BOOLEAN -> data[key] = handler.read(uuid, key, false)
+                }
+            }
+
+            val profile = PlayerProfile(data)
+            loaded[uuid] = profile
+            return profile
+        }
+
+        fun saveAll(async: Boolean) {
+            val saver = {
+                for ((uuid, profile) in loaded) {
+                    for ((key, type) in keys) {
+                        when (type) {
+                            Type.INT -> handler.write(uuid, key, profile.read(key, 0))
+                            Type.DOUBLE -> handler.write(uuid, key, profile.read(key, 0.0))
+                            Type.STRING -> handler.write(uuid, key, profile.read(key, "Unknown Value"))
+                            Type.BOOLEAN -> handler.write(uuid, key, profile.read(key, false))
+                        }
+                    }
+                }
+
+                handler.save()
+            }
+
+            if (async) {
+                EcoEnchantsPlugin.getInstance().scheduler.runAsync(saver)
+            } else {
+                saver.invoke()
+            }
+        }
+
+        @JvmStatic
+        val OfflinePlayer.profile: PlayerProfile
+            get() {
+                return load(this.uniqueId)
+            }
+
+        init {
+            keys["descriptions"] = Type.BOOLEAN
+        }
+    }
+
+    private enum class Type {
+        STRING,
+        DOUBLE,
+        BOOLEAN,
+        INT
+    }
+}
\ No newline at end of file
diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/YamlDataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/YamlDataHandler.kt
new file mode 100644
index 00000000..ccde1a13
--- /dev/null
+++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/YamlDataHandler.kt
@@ -0,0 +1,32 @@
+package com.willfp.ecoenchants.data.storage
+
+import com.willfp.eco.core.config.yaml.YamlBaseConfig
+import com.willfp.ecoenchants.EcoEnchantsPlugin
+import java.util.*
+
+@Suppress("UNCHECKED_CAST")
+class YamlDataHandler(
+    plugin: EcoEnchantsPlugin
+) : DataHandler {
+    private val dataYml = DataYml(plugin)
+
+    override fun save() {
+        dataYml.save()
+    }
+
+    override fun <T> write(uuid: UUID, key: String, value: T) {
+        dataYml.set("player.$uuid.$key", value)
+    }
+
+    override fun <T> read(uuid: UUID, key: String): T? {
+        return dataYml.get("player.$uuid.$key") as T?
+    }
+
+    class DataYml(
+        plugin: EcoEnchantsPlugin
+    ) : YamlBaseConfig(
+        "data",
+        false,
+        plugin
+    )
+}
\ No newline at end of file
diff --git a/eco-core/core-plugin/src/main/resources/config.yml b/eco-core/core-plugin/src/main/resources/config.yml
index 66e29fcc..4fd8d60b 100644
--- a/eco-core/core-plugin/src/main/resources/config.yml
+++ b/eco-core/core-plugin/src/main/resources/config.yml
@@ -3,6 +3,18 @@
 # by Auxilor
 #
 
+mysql:
+  enabled: false # Set to false, data.yml will be used instead.
+  host: localhost
+  port: 3306
+  database: database
+  user: username
+  password: passy
+
+autosave:
+  log: true # If auto-save messages should be sent to console
+  async: false # If saves should be performed asynchronously. May cause bugs without MySQL
+
 allow-on-npc: false # If enchantments should activate against NPCs.
 
 commands:
diff --git a/eco-core/core-plugin/src/main/resources/data.yml b/eco-core/core-plugin/src/main/resources/data.yml
new file mode 100644
index 00000000..305b7f45
--- /dev/null
+++ b/eco-core/core-plugin/src/main/resources/data.yml
@@ -0,0 +1 @@
+# Do not modify this file, it is purely for internal storage.
\ No newline at end of file
diff --git a/eco-core/core-plugin/src/main/resources/plugin.yml b/eco-core/core-plugin/src/main/resources/plugin.yml
index fae8dec4..123619df 100644
--- a/eco-core/core-plugin/src/main/resources/plugin.yml
+++ b/eco-core/core-plugin/src/main/resources/plugin.yml
@@ -22,6 +22,12 @@ softdepend:
   - Essentials
   - PlaceholderAPI
   - mcMMO
+libraries:
+  - 'org.jetbrains.kotlin:kotlin-stdlib:1.5.21'
+  - 'org.jetbrains.exposed:exposed-core:0.34.1'
+  - 'org.jetbrains.exposed:exposed-dao:0.34.1'
+  - 'org.jetbrains.exposed:exposed-jdbc:0.34.1'
+  - 'mysql:mysql-connector-java:5.1.48'
 
 commands:
   enchantinfo: