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

如何通过Spring JPA实现PostgreSQL自定义类型与Java对象的映射?

用Spring JPA实现PostgreSQL自定义类型与Java对象双向映射的最简方案

前提准备

假设我们要映射的PostgreSQL自定义类型是一个地址结构:

CREATE TYPE address AS (
    street VARCHAR(100),
    city VARCHAR(50),
    zip VARCHAR(10)
);

方案一:Hibernate 6+ 最简实现(推荐)

Hibernate 6原生支持PostgreSQL的自定义struct类型,无需编写复杂转换器,仅需两步完成映射:

  1. 创建对应Java POJO
    定义与自定义类型字段一一匹配的Java类,需包含无参构造器(JPA要求),并重写equals()和hashCode():
import java.util.Objects;

public class Address {
    private String street;
    private String city;
    private String zip;

    public Address() {}

    public Address(String street, String city, String zip) {
        this.street = street;
        this.city = city;
        this.zip = zip;
    }

    // Getter、Setter方法
    public String getStreet() { return street; }
    public void setStreet(String street) { this.street = street; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public String getZip() { return zip; }
    public void setZip(String zip) { this.zip = zip; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(street, address.street) &&
               Objects.equals(city, address.city) &&
               Objects.equals(zip, address.zip);
    }

    @Override
    public int hashCode() {
        return Objects.hash(street, city, zip);
    }
}
  1. 在实体类中映射自定义类型
    使用Hibernate的@Struct注解指定PostgreSQL自定义类型名称,直接将POJO作为实体字段:
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.Struct;

@Entity
public class User {
    @Id
    private Long id;
    private String name;

    // 映射PostgreSQL的address自定义类型
    @Struct(name = "address")
    private Address address;

    // Getter、Setter、构造器省略
}

此方案自动完成双向转换:从数据库读取时将address类型转为Address对象,写入时将Address对象转为PostgreSQL的address类型。

方案二:Hibernate 5及以下版本实现

若使用低版本Hibernate,需自定义UserType处理转换逻辑:

  1. 实现UserType接口
    编写自定义类型转换器,处理Java对象与SQL类型的双向转换:
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
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 AddressUserType implements UserType {
    @Override
    public int[] sqlTypes() {
        return new int[]{Types.OTHER};
    }

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

    @Override
    public boolean equals(Object x, Object y) {
        return Objects.equals(x, y);
    }

    @Override
    public int hashCode(Object x) {
        return Objects.hashCode(x);
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws SQLException {
        String structStr = rs.getString(names[0]);
        if (structStr == null) return null;
        // 解析PostgreSQL返回的struct字符串(格式:(street,city,zip))
        structStr = structStr.substring(1, structStr.length() - 1);
        String[] parts = structStr.split(",", 3);
        return new Address(parts[0], parts[1], parts[2]);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws SQLException {
        if (value == null) {
            st.setNull(index, Types.OTHER);
            return;
        }
        Address address = (Address) value;
        // 构造PostgreSQL struct格式字符串
        String structStr = String.format("('%s','%s','%s')", address.getStreet(), address.getCity(), address.getZip());
        st.setObject(index, structStr, Types.OTHER);
    }

    // 以下方法按默认逻辑实现
    @Override
    public Object deepCopy(Object value) {
        if (value == null) return null;
        Address addr = (Address) value;
        return new Address(addr.getStreet(), addr.getCity(), addr.getZip());
    }

    @Override
    public boolean isMutable() {
        return true;
    }

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

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

    @Override
    public Object replace(Object original, Object target, Object owner) {
        return deepCopy(original);
    }
}
  1. 在实体类中指定自定义类型
    使用@Type注解绑定自定义的UserType:
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.Type;

@Entity
public class User {
    @Id
    private Long id;
    private String name;

    @Type(type = "com.example.AddressUserType")
    private Address address;

    // Getter、Setter、构造器省略
}

注意事项

  • 确保PostgreSQL驱动版本适配(推荐使用org.postgresql:postgresql 42.2+)
  • Hibernate 6+的@Struct注解需依赖org.hibernate:hibernate-core 6.0+
  • 自定义UserType时,若字段包含特殊字符(如逗号),建议改用JDBC的Struct API处理,避免字符串解析出错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:00:56