ElementCachingManager - wysohn/GeneralLib GitHub Wiki

ElementCachingManager

PluginManager -> ElementCachingManager

The abstract class that helps you to easily store data in permanent data storage and vice versa. It caches the data to boost up the performance and support automatic data serialization.

ElementCachingManager is a child class of PluginManager, so you can use it just like other PluginManagers.

This class keep data in memory, so if you want to build a very sophisticated memory efficient database, this manager might not be a great option for you. Also, it saves data only when the update is required and continuously update caches from the database. This can be useful when you have shared database such as MySql, and want each managers to have up-to-date information.

Creating Data Class

Before even working with the ElementCachingManager, you need to define a data class that will be responsible only to store the data.

public class User implements ElementCachingManager.NamedElement{
    private final String name;        

    private int level = 0;
    private int kills = 0;

    private transient Player player;

    public User(String name){
        this.name = name;
    }

    @Override
    public String getName(){
        return name;
    }
}

This is the typical data class that will store some information about the player. Notice that the User class implements ElementCachingManager.NamedElement. This is required to use ElementCachingManager.

By implementing NamedElement interface, you are going to implement getName() method, and I will explain what this will do later in this WIKI.

Also notice that the 'player' field is declared as 'transient,' and this is required if you want that field to be ignored when saving/loading the data.

Creating Manager Class

public class MyUserManager extends ElementCachingManager<UUID, User>{
    public MyUserManager(MyPlugin base, int priority){
        super(base, priority);
    }

    @Override
    protected String getTableName(){
        return "UserData";
    }

    @Override
    protected Type getType(){
        return User.class;
    }

    @Override
    protected UUID createKeyFromString(String keyValue){
        return UUID.fromString(keyValue);
    }

    @Override
    protected CacheUpdateHandle<UUID, User> getUpdateHandle(){
        return null;
    }

    @Override
    protected CacheDeleteHandle<UUID, User> getDeleteHandle(){
        return null;
    }
}

Let's go one by one again.

First, the constructor there is just like PluginBase. If you don't know what I mean, I strongly suggest you to read PluginBase WIKI before proceeding.

The GenericTypes -- You might noticed there is two generic types provided: UUID and User. The ElementCachingManager use those as key and value pair, so when you save the data, you should provide both key and value. You can think about the Map interface. For our case, the key will be UUID, and User will be the data to be saved.

  1. getTableName() -- This method is used when the ElementCachingManager first have to create a storage to save the data. For file database, this name will be used as the folder to save the data, and if it were MySql, it will create a table with the given name. Currently, ElementCachingManager has file and mysql database, but more might be added in future. It's never your job to worry about as long as using ElementCachingManager class though.

  2. getType() -- This is crucial component of the ElementCachingManager. ElementCachingManager uses the GeneralLib's Database system, and this database automatically serialize/deserialize data using GSON internally. In order to serialize/deserialize the data, you must let the database to know what kind of class it is. For simple data class like User class we made, you can just return User.class; however, if it's more complex type like List or Map<String, String>, you will have to use some special trick.

The trick to save those data type is using TypeToken class of gson.

return new TypeToken<ArrayList<User>>() {}.getType();
return new TypeToken<Map<String, String>>() {}.getType();

Be aware! When you use TypeToken, you have to import the copy version of gson, not the one in the spigot.jar. I manually added the copy version of latest gson as the old gson in spigot.jar is too outdated, and I assume md-5 will not update them in short time. So even though the suggested import is com.google.gson.reflect.TypeToken, you should use copy.com.google.gson.reflect.TypeToken instead.

  1. createKeyFromString() -- When ElementCachingManager saving the data, the key, UUID in our case, need to be translated into String. To do so, ElementCachingManager simply uses the toString() method. Fortunately, the UUID class already implement toString() method! However, when we are loading the data from the database, we need to convert the String back to UUID. But because the value type is generic, the ElementCachingManager has no idea how to convert it back. So this method, createKeyFromString, will convert that String into the value instance. And amazingly, the UUID class has static method fromString() that does exactly what we want to do.

  2. getUpdateHandle() -- This is an optional method that you can provide null if you don't need it. The UpdateHandle is needed when the ElementCachingManager is updating the cache from the database, and you need to do something with the loaded data. For example, the transient field in the value will not be deserialized upon updating the cache; you might want to manually insert appropriate value into it. For example, we usually don't want to serialize/deserialize Player object, so we just make it transient. And in UpdateHandle, you insert this Player object using Bukkit.getPlayer(uuid).

  3. getDeleteHandle() -- Optional method just other way around. Unlike UpdateHandle, DeleteHandle is used when you have to do something when cache will be deleted. The cache is deleted means that it was deleted from database completely, so you can safely assume that this data is already gone from database upon DeleteHandle was used.

Save/Load data

In ElementCachingManager, you have several methods that can be used. However, there are two mostly used methods: the get() and save() method.

get() -- the get() method accepts two inputs: the key you used to save the data, and boolean value.

  1. args0 -- The first argument is simply the key you used when you saved the data. In our example, this should be UUID.

  2. args1 -- The second argument let you synchronize database operation or not. For example, if you set this to be 'false,' the database operation will be queued and it's not blocking. But if you set it to 'true,' this method will block the thread until the previously scheduled operations to be end. This is useful when you have async thread that has to deal with the database (AsyncPlayerPreLoginEvent for example).

save() -- simply, save method just save the value paired with given key. For example) save(someUuid, someUser);

⚠️ **GitHub.com Fallback** ⚠️