Java.Hibernate.Medium.What are user-defined types and how do you implement them?

Short Answer

In Hibernate, user-defined types let you customize how your Java objects are mapped to database columns by implementing the UserType (or the newer BasicType/AttributeConverter in JPA).
They’re useful when you need to map non-standard data types (like custom value objects, complex types, or database-specific columns) that JPA doesn’t handle out of the box.


🔎 Detailed Explanation

🔹 What is a UserType?

  • A custom type implementing org.hibernate.usertype.UserType gives you full control over:
    • How a Java value is read from the database.
    • How it’s written to the database.
    • How it’s compared, deep-copied, etc.
  • You can use it to map:
    ✅ Value objects (e.g., Money, RGB Color).
    ✅ JSON columns (before Hibernate’s JSON support).
    ✅ Database types like PostgreSQL’s hstore or inet.
public class MyCustomType implements UserType<Object> {

    @Override
    public int getSqlType() { ... } // JDBC SQL type code

    @Override
    public Object nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) { ... }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) { ... }

    @Override
    public Class<Object> returnedClass() { ... }

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

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

    @Override
    public Object deepCopy(Object value) { ... }

    @Override
    public boolean isMutable() { ... }
}

🔹 Example: Mapping a Money type

Say you have:

public class Money {
    private BigDecimal amount;
    private String currency;
}

You want to map it to two DB columns: amount and currency.

1️⃣ Implement UserType:

public class MoneyUserType implements UserType<Money> {
    @Override
    public int[] sqlTypes() {
        return new int[] { Types.NUMERIC, Types.VARCHAR };
    }

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

    @Override
    public Money nullSafeGet(ResultSet rs, int[] positions, SharedSessionContractImplementor session, Object owner) throws SQLException {
        BigDecimal amount = rs.getBigDecimal(positions[0]);
        String currency = rs.getString(positions[1]);
        return amount == null && currency == null ? null : new Money(amount, currency);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Money value, int index, SharedSessionContractImplementor session) throws SQLException {
        if (value == null) {
            st.setNull(index, Types.NUMERIC);
            st.setNull(index + 1, Types.VARCHAR);
        } else {
            st.setBigDecimal(index, value.getAmount());
            st.setString(index + 1, value.getCurrency());
        }
    }

    // ... implement equals(), hashCode(), deepCopy(), isMutable()
}

2️⃣ Annotate the entity field with @Type:

@Entity
public class Product {
    @Id
    private Long id;

    @Type(value = MoneyUserType.class)
    private Money price;
}

✅ Hibernate will now use your custom type for reading and writing Money fields.

🔹 Alternative: JPA AttributeConverter (simpler case)
If your custom type maps to a single DB column, you can use JPA’s @Converter:

@Converter(autoApply = true)
public class StatusConverter implements AttributeConverter<StatusEnum, String> {
    @Override
    public String convertToDatabaseColumn(StatusEnum status) { ... }
    @Override
    public StatusEnum convertToEntityAttribute(String dbData) { ... }
}

✅ Use AttributeConverter for simple mappings; use UserType when:

  • You map multiple columns.
  • Need fine-grained control (e.g., custom comparison, mutability).
  • Deal with DB-specific or non-standard JDBC types.

📌 Key Takeaways

✅ User-defined types let you customize how Java objects map to SQL beyond JPA’s built-ins.
✅ Implement UserType when you need multi-column or complex mappings.
✅ Use AttributeConverter for simpler cases with one DB column.

This entry was posted in Без рубрики. Bookmark the permalink.

Leave a Reply

Your email address will not be published.