bigbang
bigbang

Reputation: 17

JDBI Json type column mapping to custom class

I am extremely sorry for posting this large question but I have been struggling with the argument factory and column mapper for too long.

MAIN QUESTION: I would like the Argument Factory and Column Mapper are generic so that I do not have to register it for every column explicitly.

I have a table in MySQL with schema:

create table employee {
   id int not null primary key,
   name varchar(255),
   job_details json,
   metadata json
}

I have the DAO layer interface that looks like

@UseClasspathSqlLocator
public interface EmployeeDao {

  @SqlQuery
  @RegisterBeanMapper(EmployeeDB.class)
  EmployeeDB fetchEmployee(@Bind("id") int id);

  @SqlUpdate
  void insertEmployee(@BindBean EmployeeDB employeeDB);
}

The SQL script files are available in resources which look like

fetchEmployee.sql

SELECT *
FROM employee
WHERE id = :id
LIMIT 1

insertEmployee.sql

INSERT INTO employee (id, name, job_details, metadata)
VALUES (:id, :name, :jobDetails, :metadata)

I also have repository class that looks like

public class EmployeeRepository {
  private final CommonJdbiProvider commonJdbiProvider;

  public EmployeeRepository(CommonJdbiProvider commonJdbiProvider) {
    this.commonJdbiProvider = commonJdbiProvider;
  }

  @Nullable
  public EmployeeDB getEmployee(int id) {
    EmployeeDB employeeDB = null;
    try {
      employeeDB =
          commonJdbiProvider
              .readOnlyJdbi()
              .withHandle(
                  handle -> {
                    EmployeeDao dao = handle.attach(EmployeeDao.class);
                    return dao.fetchEmployee(id);
                  });
    } catch (Exception ex) {
      log.error("Error while getting employee: {}", id, ex);
    }
    return employeeDB;
  }

  public void saveEmployee(EmployeeDB employee) {
    try {
      commonJdbiProvider
          .readWriteJdbi()
          .useTransaction(
              TransactionIsolationLevel.READ_COMMITTED,
              handle -> {
                EmployeeDao dao = handle.attach(EmployeeDao.class);
                dao.insertEmployee(employee);
              });
    } catch (Exception ex) {
      log.error("Error while saving employee: {}", employee, ex);
    }
  }
}

The CommonJdbiProvider class looks like

public class CommonJdbiProvider {
  private final JdbiProvider jdbiProvider;

  public CommonJdbiProvider(JdbiProvider jdbiProvider) {
    this.jdbiProvider = jdbiProvider;
  }

  public Jdbi readOnlyJdbi() {
    Jdbi jdbi = jdbiProvider.readOnly();
    if (Objects.isNull(jdbi)) {
      throw new IllegalStateException("Unable to initialize read-only Jdbi via JdbiProvider");
    }
    plugins().forEach(jdbi::installPlugin);
    return jdbi;
  }

  public Jdbi readWriteJdbi() {
    Jdbi jdbi = jdbiProvider.readWrite();
    if (Objects.isNull(jdbi)) {
      throw new IllegalStateException("Unable to initialize read-write Jdbi via JdbiProvider");
    }
    plugins().forEach(jdbi::installPlugin);
    registerGenericMappers(jdbi);
    return jdbi;
  }

  private List<JdbiPlugin> plugins() {
    List<JdbiPlugin> jdbiPlugins = new ArrayList<>();
    jdbiPlugins.add(new SqlObjectPlugin());
    jdbiPlugins.add(new Jackson2Plugin());
    return jdbiPlugins;
  }

  private void registerGenericMappers(Jdbi jdbi) {
    jdbi.registerArgument(new GenericJsonArgumentFactory());
    jdbi.registerColumnMapper(new GenericJsonColumnMapperFactory());
  }
}

Now I also have the POJO classes for Employee, JobDetailsType and MetadataType UDT:

public class EmployeeDB {

  @JsonProperty("id")
  private int id;

  @JsonProperty("name")
  private String name;

  @JsonProperty("job_details")
  private JobDetailsType jobDetails;

  @JsonProperty("metadata")
  private MetadataType metadata;
}
public class JobDetailsType {
  @JsonProperty("role")
  private String role;

  @JsonProperty("salary")
  private int salary;
}
public class MetadataType {
  @JsonProperty("joining_date")
  private Date joiningDate;

  @JsonProperty("random_field")
  private String randomField;
}

Please help me write an appropriate ArgumentFactory class and ColumnMapper class that can be used to serialize a class object to Json if needed and deserialize json to a class object as per the column name.

Thank you so much in advance

I tried using some way to write a generic mappers and factories but they all seem to be failing. Tried using ChatGPT but its of no help.

Upvotes: 0

Views: 167

Answers (0)

Related Questions