More Into External Conversions

Apr 2, 2018
More Into External Conversions
  • As we learnt before, we can add external converters to an SQLObjectConverter for conversion supports for classes that are not implementing this API. However, if you are smart enough, you may found that the TypeConverter does not limit your output type to any primitive types or String. This will open the external conversions into a deeper topic. In this page, you are taught two advanced topics about external conversions:
    • Multiple external conversions within a serialization or deserialization
    • Path of external conversions
    Multiple External Conversions

    Multiple external conversions is one of the unique features of the SQLObjectConverter API among all the Java binding APIs. The benefit is that, take a look at the example below.

    Let's say we want to save an org.bukkit.OfflinePlayer and an com.rcextract.minecord.Server. Suppose we can refer a Server using UUID even after server restart. In this situation, we can save UUID of the player and server into the database for further reference. But before saving a UUID, we need to convert it to String. So, in conclusion, we need to let SQLObjectConverter know how to convert both player and server into UUIDs, and know how to convert UUIDs into Strings:
    Code (Java):
    new TypeConverter<Server, UUID>(Server.class, UUID.class) {

        @Override
        public UUID serialize(Server server) {
            return server.getIdentifier();
        }

        @Override
        public Server deserialize(UUID uuid) {
            return Minecord.getServer(uuid);
            //This will not work in the real Minecord, but as what we supposed in this example, we can always refer to a server by UUID.
        }

    }
    new TypeConverter<OfflinePlayer, UUID>(OfflinePlayer.class, UUID.class) {

        @Override
        public UUID serialize(OfflinePlayer player) {
            return player.getUniqueId();
        }

        @Override
        public OfflinePlayer deserialize(UUID uuid) {
            return Bukkit.getOfflinePlayer(uuid);
        }

    }
    new TypeConverter<UUID, String>(UUID.class, String.class) {

        @Override
        public String serialize(UUID uuid) {
            return uuid.toString();
        }

        @Override
        public UUID deserialize(String string) {
            return UUID.fromString(string);
        }

    }
    instead of using the method below:
    Code (Java):
    new TypeConverter<OfflinePlayer, String>(OfflinePlayer.class, String.class) {

        @Override
        public String serialize(OfflinePlayer player) {
            return player.getUniqueId().toString();
        }

        @Override
        public OfflinePlayer deserialize(String string) {
            return Bukkit.getOfflinePlayer(UUID.fromString(string));
        }

    }
    new TypeConverter<Server, String>(Server.class, String.class) {

        @Override
        public String serialize(Server server) {
            return server.getIdentifier().toString();
        }

        @Override
        public Server deserialize(String string) {
            return Minecord.getServer(UUID.fromString(string));
        }

    }
    There aren't significant differences between two methods in this example. But think about it. Suppose there are types D, C, which should be converted to type B, then to type A. If the conversion between type B and A is complicated and expensive, the first method helps the programmers very much. Also, if type B belongs to an enterprise or a non-open-source application, the conversion between type B and type A can be also non-open-source if the first method is supported. Otherwise, programmers will need to know the direct conversions between type D or C and A, which forces the conversion between type B and A to be open source so they can correctly do to conversions.

    Path of External Conversions

    The SQLObjectConverter only blocks adding type converters which its input and output type is the same as another one added. So, what if it has the following converters?
    • D -> C
    • C -> B
    • B -> A
    • D -> E
    • E -> A
    • D -> F
    Suppose -> means serialization and we are saving a D, and A is a primitive type or String. The conversions are listed below:
    • D -> C -> B -> A
    • D -> E -> A
    • D -> F
    As you can see, the third conversion path, D -> F, will not work, because F may not be a primitive type or String. So, the SQLObjectConverter will try out all converters randomly and see is further conversions is possible. If not, the SQLObjectConverter will restore the non-converted object and try another converter. This is how the SQLObjectConverter converts an object to a primitive type or String, randomly.

    Random Conversion Path

    It is way complicated to tell SQLObjectConverter to use the correct converter, so it will randomly create a conversion path for an object. Let's take the example in the previous section. SQLObjectConverter has no idea about the converting algorithms of -> C -> B -> and -> E ->, so different results after serialization or deserialization may be produced after using different conversion paths. Therefore, after saving the object, SQLObjectConverter will also save the conversion path for deserialization.
  • Loading...
  • Loading...