Mybatis. Выбрать из БД byte[]
От: karbofos42 Россия  
Дата: 19.09.23 06:30
Оценка:
Имеется Mybatis 3.5.13, БД Postgresql с колонкой типа bytea.
Прописываю в интерфейсе Mapper'а метод:
byte[] getContent(long id);

и запрос для него в xml:
<select id="getContent">
  select content
  from table1
  where id = #{id}
  limit 1
</select>

Получаю ошибку при выполнении: "Error attempting to get column 'content' from result set. Cause: org.postgresql.util.PSQLException: Bad value for type byte : \x504 ..."
Пробовал в xml прописывать resultType="_byte[]" — исключение меняется на "java.lang.IllegalArgumentException: argument type mismatch".
Если метод в интерфейсе поменять на:
byte[][] getContent(long id);

то отрабатывает нормально и можно данные забирать из result[0].

Как-то можно прописать, чтобы вариант с byte[] работал?
Re: Mybatis. Выбрать из БД byte[]
От: r0nd  
Дата: 19.09.23 18:03
Оценка: 4 (1)
Здравствуйте, karbofos42, Вы писали:

K>Как-то можно прописать, чтобы вариант с byte[] работал?


Проблема здесь связана с тем, что MyBatis не знает, как правильно маппить столбец типа bytea в Postgresql на тип byte[] в Java. Вам нужно использовать org.apache.ibatis.type.TypeHandler для корректного маппинга bytea в byte[].

Далее, создайте класс, который реализует org.apache.ibatis.type.TypeHandler<byte[]> для маппинга столбцов типа bytea на массивы байтов byte[]:

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.postgresql.util.PGobject;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class ByteArrayTypeHandler extends BaseTypeHandler<byte[]> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, byte[] parameter, JdbcType jdbcType) throws SQLException {
        PGobject pgObject = new PGobject();
        pgObject.setType("bytea");
        pgObject.setValue(new String(parameter));
        ps.setObject(i, pgObject);
    }

    @Override
    public byte[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
        PGobject pgObject = (PGobject) rs.getObject(columnName);
        if (pgObject != null) {
            return pgObject.getValue().getBytes();
        }
        return null;
    }

    @Override
    public byte[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        PGobject pgObject = (PGobject) rs.getObject(columnIndex);
        if (pgObject != null) {
            return pgObject.getValue().getBytes();
        }
        return null;
    }

    @Override
    public byte[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        PGobject pgObject = (PGobject) cs.getObject(columnIndex);
        if (pgObject != null) {
            return pgObject.getValue().getBytes();
        }
        return null;
    }
}


Зарегистрируйте этот обработчик типов в вашем конфигурационном файле MyBatis (например, mybatis-config.xml):


<typeHandlers>
    <typeHandler handler="your.package.name.ByteArrayTypeHandler"/>
</typeHandlers>


Где your.package.name — это имя пакета, в котором находится ByteArrayTypeHandler.

Теперь вы можете использовать byte[] в вашем интерфейсе Mapper и в XML-запросах без каких-либо изменений:

byte[] getContent(long id);


<select id="getContent">
  select content
  from table1
  where id = #{id}
  limit 1
</select>


С этими настройками MyBatis должен корректно маппить столбцы типа bytea на массивы байтов byte[].
...<< Dementor 1.4.2 ✪ Lets Play a Game ⚀⚀⚂⚂⚃>>
Re[2]: Mybatis. Выбрать из БД byte[]
От: karbofos42 Россия  
Дата: 20.09.23 06:02
Оценка:
Здравствуйте, r0nd, Вы писали:

R>Проблема здесь связана с тем, что MyBatis не знает, как правильно маппить столбец типа bytea в Postgresql на тип byte[] в Java. Вам нужно использовать org.apache.ibatis.type.TypeHandler для корректного маппинга bytea в byte[].


Ну, он же маппит как-то в byte[][]. Я предполагаю, что он целевой массив воспринимает как: мне нужно из БД вернуть множество записей, а потом не может в единственный byte засунуть содержимое столбца bytea.
Поэтому когда прописываю массив массивов, там всё у него получается. Первый массив — это для строк, а вложенные — это уже под строки.
С объектами и примитивами такой проблемы не было, т.к. они не являются коллекцией и библиотека чётко может понять, что нужно единственную запись взять.

R>С этими настройками MyBatis должен корректно маппить столбцы типа bytea на массивы байтов byte[].


В MyBatis есть стандартный ByteArrayTypeHadler, который несколько иначе работает, но прописывается в репозитории хендлеров.
Если я у запроса не пропишу resultType, то берётся стандартный хендлер, который падает. Если прописываю тип "_byte[]" и попадаю в предложенный самодельный, то возникает две проблемы:
1) rs.getObject(columnName) возвращает не PGobject, а byte[] (забавно, что rs.getBytes(columnName) падает с исключением), следовательно в исходном виде падает на попытке преобразования в PGobject
2) если я из методов без промежуточного PGobject возвращаю byte[] из getObject, то в библиотеке внутри что-то дальше падает с ошибкой IllegalArgument

Похоже, что проще забить и оставить костылик в виде byte[][].
Ну, либо делать класс-обёртку, чтобы он библиотекой как коллекция не воспринимался и нормально для него TypeHandler подхватывался без конфликтов со стандартными.
Re: Mybatis. Выбрать из БД byte[]
От: Pavel Dvorkin Россия  
Дата: 20.09.23 07:31
Оценка:
Здравствуйте, karbofos42, Вы писали:

Это смотрел ?

https://stackoverflow.com/questions/8536043/how-can-i-get-byte-from-bytea-column-with-mybatis
With best regards
Pavel Dvorkin
Re: Mybatis. Выбрать из БД byte[]
От: gyraboo  
Дата: 20.09.23 07:45
Оценка:
Здравствуйте, karbofos42, Вы писали:

K>Как-то можно прописать, чтобы вариант с byte[] работал?


Не связывайся с MyBatis, потом хуже будет.
Re[2]: Mybatis. Выбрать из БД byte[]
От: karbofos42 Россия  
Дата: 20.09.23 07:49
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Это смотрел ?


Ага. Тоже костыльно как-то получается. В моём варианте прописывается byte[][] и потом возвращается нулевой элемент.
Там предлагают возвращать Object, в котором по сути будет лежать byte[].
Вариант по ссылке несколько оптимальнее будет, но конечно печально, что нормального решения похоже нет.
Re[2]: Mybatis. Выбрать из БД byte[]
От: karbofos42 Россия  
Дата: 20.09.23 08:21
Оценка:
Здравствуйте, gyraboo, Вы писали:

G>Не связывайся с MyBatis, потом хуже будет.


Ну, сжатые сроки, руководство сверху рекомендовало эту библиотеку, а я Java в последний раз видел 15 лет назад, так что пока придётся с этим разбираться
В целом вроде жить можно, пока только с мелкими неудобствами сталкивался. Ну, и конечно вот эта смесь SQL и XML — нечто своеобразное.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.