P
P
pqgg7nwkd42016-08-22 18:23:07
PostgreSQL
pqgg7nwkd4, 2016-08-22 18:23:07

How do I get JDBC to use my class for decimal fields?

Good afternoon.
I have some class Curr (extends Number).
I want JDBC for decimal fields to return an instance of my class instead of BigDecimal.
Tell me how this can be done and is it possible?
Postgresql base.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
E
Evhen, 2016-08-23
@pqgg7nwkd4

You will not be able to somehow replace the returned BigDecimal with the ResultSet#getBigDecimal method, since this is defined by the jdbc specification.
In addition, BigDecimal is an immutable class, and it will not work to override it, even if your Curr class extends Number, you can only cast them to a common type, i.e. to number. Attempting to cast BigDecimal to Curr or vice versa will result in a ClassCastException.
It's easier to do as Vitaliy Vitrenko wrote , but you can get confused and request Driver, Connection, Statement and ResultSet and after calling the gеBigDecimal method perform the necessary transformations ...
Here is such a bicycle :)
Proxy pseudo driver, which itself registers in the DriverManager

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.sql.*;

public final class PostgresDriverProxyRegister implements InvocationHandler {

  static {
    try {
      DriverManager.registerDriver((Driver) newProxy(new org.postgresql.Driver()));
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }


  public static Object newProxy(Object target) {
    Class<?> clazz = defineInterface(target);
    return clazz == null ? target
        : Proxy.newProxyInstance(Driver.class.getClassLoader(), new Class[]{clazz}, new PostgresDriverProxyRegister(target));
  }

  public static Class<?> defineInterface(Object o) {

    if (o == null)
      return null;

    if (o instanceof Driver)
      return Driver.class;
    if (o instanceof Connection)
      return Connection.class;
    if (o instanceof Statement)
      return Statement.class;
    if (o instanceof PreparedStatement)
      return PreparedStatement.class;
    if (o instanceof ResultSet)
      return ResultSet.class;

    return null;
  }


  private final Object target;

  private PostgresDriverProxyRegister(Object target) {
    this.target = target;
  }


  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    String methodName = method.getName();

    // в этом блоки можно делать нужные изменения для возвращаемых значений методамы ResultSet
    if (proxy instanceof ResultSet) {
      Object invokeResult = method.invoke(target, args);
      if (invokeResult != null) {
        if ("getBigDecimal".equals(methodName)) {
          // округляем до 2-х знаков после запятой
          BigDecimal bigDecimal = (BigDecimal) invokeResult;
          invokeResult = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP);
        } else if ("getString".equals(methodName)) {
          // убираем пробелы в начале и конце строки, делаем строку в верхнем регистре
          String s = (String) invokeResult;
          invokeResult = s.trim().toUpperCase();
        }
      }

      return invokeResult;
    }

    if (proxy instanceof Driver) {
      if ("acceptsURL".equals(methodName) || "connect".equals(methodName)) {
        // меняем префикс на оригинал
        String url = (String) args[0];
        if (url.startsWith("jdbc:postgresql-proxy:")) {
          args[0] = url.replace("jdbc:postgresql-proxy:", "jdbc:postgresql:");
        }
      }
    }

    return invokeAndProxy(method, args);
  }

  public Object invokeAndProxy(Method method, Object[] args) throws Throwable {
    Object returnValue = method.invoke(target, args);
    return newProxy(returnValue);
  }
}

For the program itself, working with jdbc will not change in any way, you just need to change the url prefix to the database
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Main {

    public static void main(String[] args) throws Exception {

        Class.forName("ua.home.jdbc.driver.warp.postgres.PostgresDriverProxyRegister");

        try(Connection connection = DriverManager
                // используем отличный от оригинала прификс к url 'jdbc:postgresql-proxy:', что бы именно наш драйвер грузился
                .getConnection("jdbc:postgresql-proxy://localhost:5432/test_db", "test_user", "test_password");
            Statement statement = connection.createStatement();
            ResultSet rs = statement.executeQuery("SELECT 12.65456161 as testDecimal, '  tEsTsTrinG    ' as testString")) {

            if (rs.next()) {
                System.out.printf("testDecimal = '%s'\n", rs.getBigDecimal("testDecimal"));
                System.out.printf("testString = '%s'\n", rs.getString("testString"));
            }

            /* output in console
                    testDecimal = '12.65'
                    testString = 'TESTSTRING'
            */

        }
    }
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question