javamysqlhibernatemariadbhibernate-mapping

Map MariaDB geometry Point to custom Hibernate type


I want to map a MariaDB Point field to a custom Vector2 type with Hibernate.

I have the following table:

CREATE TABLE `ships` (
    `accounts_id` int   NOT NULL,
    `maps_id`     int   NOT NULL,
    `position`    point NOT NULL
) ENGINE InnoDB CHARACTER SET utf8;

And I want to map it to a class like this:

class Ship {
    public Account account;
    public Map map;
    public Vector2 position;
}

The problem comes with the position field, how do I map it to an already existing type?

The solutions I've found implied using hibernate-spatial in order to use its Point class, however I want to use my Vector2 class instead of that one


Solution

  • After some hours of reading I figured it out.

    First I need a simple way to get the X and Y coordinates of a point column. According to the manual they are stored in WKB, HOWEVER, when I tried myself to retrieve the raw bytes something wasn't right:

    ResultSet rs = statement.executeQuery("SELECT * FROM accounts_ships");
    rs.next();
    
    byte[] position = rs.getBytes("position");
    // position ==> byte[25] { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 80, -44, 64, 0, 0, 0, 0, 0, 0, -55, 64 }
    

    There were 4 additional bytes to the beginning of the array. A part of this, everything was right so I proceeded to parse the result while keeping in mind those bytes:

    var in = new ByteArrayInputStream(rs.getBytes(names[0]));
    if (in.available() == 25) {
        in.skip(4);
    }
    
    var order = ByteOrder.BIG_ENDIAN;
    if (in.read() == 1) {
        order = ByteOrder.LITTLE_ENDIAN;
    }
    
    var typeBytes = new byte[4];
    var xBytes    = new byte[8];
    var yBytes    = new byte[8];
    
    try {
        in.read(typeBytes);
        in.read(xBytes);
        in.read(yBytes);
    } catch (Exception e) {
        throw new HibernateException("Can't parse point column!", e);
    }
    
    var type = ByteBuffer.wrap(typeBytes)
                         .order(order);
    
    if (type.getInt() != 1) {
        throw new HibernateException("Not a point!");
    }
    
    var x = ByteBuffer.wrap(xBytes)
                      .order(order);
    var y = ByteBuffer.wrap(yBytes)
                      .order(order);
    
    return new Vector2((float) x.getDouble(), (float) y.getDouble());
    

    And the only thing left to do was to make a custom type so hibernate had something to parse it.