@pqgg7nwkd4

Как сделать, чтобы JDBC использовал мой класс для decimal полей?

Добрый день.

У меня некий класс Curr (extends Number).
Я хочу, чтобы JDBC для decimal-полей возвращал экземпляр моего класса, вместо BigDecimal.
Подскажите как это можно сделать и можно ли?

База Postgresql.
  • Вопрос задан
  • 324 просмотра
Решения вопроса 1
EugeneP2
@EugeneP2
Java Dev
Как-то подменить возвращаемый BigDecimal методом ResultSet#getBigDecimal у вас не выйдет, так как это определено jdbc спецификацией.
Помимо этого, BigDecimal immutable клаcc, и переопределить его не выйдет, даже если ваш класс Curr расширяет Number, вы сможете только привести их общему типу, т.е. к Number. Попытка привести BigDecimal к Curr или на оборот приведет к ClassCastException.

Проще сделать как написал Виталий Витренко, но можно заморочиться и запроксить Driver, Connection, Statement и ResultSet и после вызова метода gеBigDecimal выполнять нужные преобразования...

Вот такой вот велосипед :)

Прокси псевдо драйвер, который сам регистрируется в 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);
	}
}


Для самой программы, работы с jdbc никак не изменится, только нужно изменить префикс url-а к БД

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'
            */

        }
    }
}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы