You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Hibernate将MariaDB Point字段映射至自定义Vector2类型?

解决Hibernate映射MariaDB Point到自定义Vector2类型的问题

我来帮你搞定这个MariaDB Point字段到自定义Vector2类的映射需求,下面给你两种实用的方案,你可以根据自己的项目场景来选择:

方案一:使用JPA AttributeConverter(简单易实现)

这种方案是最直接的,不需要深入Hibernate的底层API,只需要实现一个转换器类,告诉JPA怎么把Vector2和数据库的Point类型互相转换。

首先,假设你的Vector2类结构是这样的(如果你的类不一样,调整转换器里的字段即可):

public class Vector2 {
    private double x;
    private double y;

    // 无参构造器(JPA必须要有,用来实例化对象)
    public Vector2() {}

    public Vector2(double x, double y) {
        this.x = x;
        this.y = y;
    }

    // Getter和Setter
    public double getX() { return x; }
    public void setX(double x) { this.x = x; }
    public double getY() { return y; }
    public void setY(double y) { this.y = y; }
}

接下来实现转换器,这里推荐用MariaDB JDBC驱动提供的Geometry类来处理,比字符串解析更严谨:

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import org.mariadb.jdbc.Geometry;

@Converter(autoApply = false) // 不要自动应用到所有字段,只在需要的地方指定
public class Vector2ToPointConverter implements AttributeConverter<Vector2, Geometry> {

    // 把Vector2转成数据库能识别的Point类型
    @Override
    public Geometry convertToDatabaseColumn(Vector2 vector2) {
        if (vector2 == null) {
            return null;
        }
        return Geometry.createPoint(vector2.getX(), vector2.getY());
    }

    // 把数据库的Point转成自定义的Vector2
    @Override
    public Vector2 convertToEntityAttribute(Geometry geometry) {
        if (geometry == null || geometry.getType() != Geometry.GeometryType.POINT) {
            return null;
        }
        double[] coords = geometry.getCoordinates();
        return new Vector2(coords[0], coords[1]);
    }
}

然后在你的Ship实体类的position字段上加上注解,指定使用这个转换器:

import jakarta.persistence.*;

@Entity
@Table(name = "ships")
public class Ship {

    @ManyToOne
    @JoinColumn(name = "accounts_id", nullable = false)
    public Account account;

    @ManyToOne
    @JoinColumn(name = "maps_id", nullable = false)
    public Map map;

    @Column(name = "position", nullable = false)
    @Convert(converter = Vector2ToPointConverter.class)
    public Vector2 position;

    // 记得加上构造器、Getter/Setter等方法
}

注意:这个方案需要你引入MariaDB的JDBC驱动依赖,比如Maven:

<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>3.3.3</version> <!-- 替换成最新稳定版 -->
</dependency>

方案二:自定义Hibernate BasicType(适合复杂场景)

如果你需要在HQL查询中直接使用Vector2类型,或者需要更深度的Hibernate集成(比如自定义类型的缓存、SQL类型处理),可以用Hibernate的自定义类型方案。

首先实现UserType接口(Hibernate 5+也可以用BasicType,不过UserType更常用):

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import org.mariadb.jdbc.Geometry;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Objects;

public class Vector2Type implements UserType {

    @Override
    public int[] sqlTypes() {
        // 对应MariaDB的POINT类型,JDBC类型用Types.OTHER
        return new int[]{Types.OTHER};
    }

    @Override
    public Class<Vector2> returnedClass() {
        return Vector2.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) return true;
        if (x == null || y == null) return false;
        Vector2 v1 = (Vector2) x;
        Vector2 v2 = (Vector2) y;
        return Double.compare(v1.getX(), v2.getX()) == 0 && Double.compare(v1.getY(), v2.getY()) == 0;
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return Objects.hash(((Vector2) x).getX(), ((Vector2) x).getY());
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
        Geometry geometry = rs.getObject(names[0], Geometry.class);
        if (rs.wasNull() || geometry == null || geometry.getType() != Geometry.GeometryType.POINT) {
            return null;
        }
        double[] coords = geometry.getCoordinates();
        return new Vector2(coords[0], coords[1]);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, Types.OTHER);
            return;
        }
        Vector2 vector2 = (Vector2) value;
        Geometry point = Geometry.createPoint(vector2.getX(), vector2.getY());
        st.setObject(index, point, Types.OTHER);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        if (value == null) return null;
        Vector2 v = (Vector2) value;
        return new Vector2(v.getX(), v.getY());
    }

    @Override
    public boolean isMutable() {
        return true; // Vector2的字段可以修改,所以标记为可变
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) deepCopy(value);
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return deepCopy(cached);
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }
}

然后在Ship实体类的position字段上标注使用这个自定义类型:

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import org.hibernate.annotations.Type;

@Entity
@Table(name = "ships")
public class Ship {

    // ... 其他字段

    @Column(name = "position", nullable = false)
    @Type(type = "com.yourpackage.Vector2Type") // 替换成你的类的全限定名
    public Vector2 position;

    // ... Getter/Setter等
}

如果想全局注册这个类型,避免每次都写全限定名,可以在Hibernate配置文件(比如hibernate.cfg.xml)里添加:

<hibernate-configuration>
    <session-factory>
        <!-- 其他配置项 -->
        <typedef name="vector2" class="com.yourpackage.Vector2Type"/>
    </session-factory>
</hibernate-configuration>

之后实体类里就可以简化成@Type(type = "vector2")。

一些注意事项

  1. 确保你的Vector2类有无参构造器,JPA和Hibernate都需要用它来实例化对象。
  2. 测试的时候一定要验证写入和读取流程:保存一个Ship对象到数据库,然后查询出来检查position的x、y值是否正确。
  3. 如果使用Spring Boot,可以通过HibernatePropertiesCustomizer来全局注册自定义类型,不需要手动修改配置文件。

内容的提问来源于stack exchange,提问作者aiko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:36:23