You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过Micronaut Data与PostgreSQL JDBC驱动存储JSONB类型数据

Great question! Yes, Micronaut Data absolutely supports native conversion of Java objects to PostgreSQL's JSONB type—no need to manually handle PGObject or raw JSON strings if you set things up correctly. Let's walk through the best approaches tailored to your use case, including how to leverage the PostgreSQL JDBC driver's jsonb support in both Micronaut Data and Predator.

1. Simplest Approach: Use Jackson-Annotated POJOs with Micronaut Data

Micronaut Data integrates seamlessly with Jackson, so you can map your Java object directly to a JSONB column with minimal configuration. Here's how:

Step 1: Define Your Entity and POJO

First, create a POJO for your JSON structure, then reference it in your entity class with the correct column definition:

import io.micronaut.data.annotation.Column;
import io.micronaut.data.annotation.Entity;
import io.micronaut.data.annotation.Id;

@Entity(name = "user_data")
public class UserData {
    @Id
    private Long id;

    // Explicitly define the column type as JSONB
    @Column(columnDefinition = "jsonb")
    private UserInfo userInfo;

    // Getters, setters, and constructors
}

// Your JSON structure POJO
class UserInfo {
    private String fname;
    private String lname;

    // Getters, setters, and no-arg/parameterized constructors
    public UserInfo() {}
    public UserInfo(String fname, String lname) {
        this.fname = fname;
        this.lname = lname;
    }
}

Step 2: Configure the Repository

Create a standard Micronaut Data repository interface, specifying the PostgreSQL dialect:

import io.micronaut.data.annotation.Repository;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;

@JdbcRepository(dialect = Dialect.POSTGRES)
@Repository
public interface UserDataRepository extends io.micronaut.data.repository.CrudRepository<UserData, Long> {
}

Step 3: Use the Repository in Your Service

You can now save and retrieve your UserInfo objects directly—Micronaut handles the JSON serialization/deserialization automatically:

import jakarta.inject.Singleton;
import java.util.Optional;

@Singleton
public class UserDataService {
    private final UserDataRepository repository;

    public UserDataService(UserDataRepository repository) {
        this.repository = repository;
    }

    public UserData saveJohnDoe() {
        UserInfo john = new UserInfo("john", "doe");
        UserData data = new UserData();
        data.setUserInfo(john);
        return repository.save(data);
    }

    public Optional<UserInfo> getUserInfo(Long id) {
        return repository.findById(id).map(UserData::getUserInfo);
    }
}

2. Leveraging PostgreSQL JDBC Driver's JSONB Type (Custom Converters)

If you want direct control over how your Java object is converted to PostgreSQL's native jsonb type (similar to your raw JDBC attempt), you can create custom type converters. This is useful if you need to handle edge cases or custom serialization logic.

Step 1: Create Conversion Classes

First, create a converter to turn your UserInfo POJO into a PostgreSQL PGobject (the driver's native JSONB type):

import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.TypeConverter;
import org.postgresql.util.PGobject;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.inject.Singleton;
import java.sql.SQLException;
import java.util.Optional;

@Singleton
public class UserInfoToPgJsonbConverter implements TypeConverter<UserInfo, PGobject> {
    private final ObjectMapper objectMapper;

    public UserInfoToPgJsonbConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public Optional<PGobject> convert(UserInfo userInfo, Class<PGobject> targetType, ConversionContext context) {
        try {
            PGobject jsonbObject = new PGobject();
            jsonbObject.setType("jsonb");
            jsonbObject.setValue(objectMapper.writeValueAsString(userInfo));
            return Optional.of(jsonbObject);
        } catch (SQLException | com.fasterxml.jackson.core.JsonProcessingException e) {
            context.reject(e);
            return Optional.empty();
        }
    }
}

Then, create a converter to convert back from PGobject to UserInfo:

@Singleton
public class PgJsonbToUserInfoConverter implements TypeConverter<PGobject, UserInfo> {
    private final ObjectMapper objectMapper;

    public PgJsonbToUserInfoConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public Optional<UserInfo> convert(PGobject pgObject, Class<UserInfo> targetType, ConversionContext context) {
        try {
            return Optional.of(objectMapper.readValue(pgObject.getValue(), UserInfo.class));
        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
            context.reject(e);
            return Optional.empty();
        }
    }
}

Step 2: Update the Entity

You can keep your entity the same as before, but now Micronaut will use your custom converters to interact directly with the PostgreSQL jsonb type.

3. Micronaut Data Predator Notes

If you're using Micronaut Data Predator (the next-gen version of Micronaut Data), the process is even more streamlined. Predator has enhanced support for native database types, so you can often skip the custom converters entirely. Just ensure your entity uses @Column(columnDefinition = "jsonb") and your repository specifies the PostgreSQL dialect—Predator will handle the rest using Jackson under the hood.

Required Dependencies

Make sure your build file includes these dependencies (Gradle example):

implementation("io.micronaut.data:micronaut-data-jdbc")
implementation("io.micronaut.sql:micronaut-jdbc-hikari") // Connection pool
implementation("org.postgresql:postgresql") // PostgreSQL JDBC driver
implementation("io.micronaut.jackson:micronaut-jackson-databind") // Jackson integration

Bonus: Querying JSONB Fields

You can query JSONB fields directly using JPQL or native queries. For example:

// JPQL using PostgreSQL's JSON operators
@Query("SELECT ud FROM UserData ud WHERE ud.userInfo ->> 'fname' = :fname")
Optional<UserData> findByFname(String fname);

// Native query with JSONB containment
@Query(value = "SELECT * FROM user_data WHERE user_info @> :userInfo::jsonb", nativeQuery = true)
Optional<UserData> findByUserInfo(UserInfo userInfo);

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

火山引擎 最新活动