Как перенести строки PostgreSQL в текстовый тип?

Мы используем аннотированный тип JPA, который выглядит следующим образом (groovy code):

@Entity
@EqualsAndHashCode
class TextNote extends Serializable {
    @Id Long id
    String text
}

Когда он был впервые написан, я был очень знаком с JPA и сначала написал SQL, а затем аннотированные классы соответствовали SQL. Чтение на PostgreSQL показалось мне следующим:

CREATE TABLE textnote (
    id bigint NOT NULL,
    text text
);

Это сработало, и у нас были таблицы, которые выглядели так:

 id  |          text
-----+------------------------
 837 | really long text here

Теперь я хочу, чтобы JPA Entity выглядела так:

@Entity
@EqualsAndHashCode
class TextNote extends Serializable {
    @Id Long id
    @Lob String text
}

Добавив аннотацию @Lob, поставщик JPA (в моем случае, спящий режим) может корректно создать DDL для меня, если мы захотим поменять базы данных. Он также документирует то, что я хочу, чтобы текстовое поле было. Теперь, когда создается заметка, я вижу что-то вроде этого:

 id  |          text
-----+------------------------
 837 | 33427

Что хорошо для новых заметок, так как когда я читаю его в коде с помощью String getText(), он возвращает действительно длинный текст. Честно говоря, я не знаю, как PostgreSQL реализует тип text, и мне не нужно теоретически. Однако в нашей базе данных уже есть много заметок, сохраненных с использованием старого кода без аннотации @Lob. Запуск нового кода в существующей базе данных вызывает такие проблемы:

org.springframework.dao.DataIntegrityViolationException: Bad value for type long : not a number this time; SQL [n/a]; nested exception is org.hibernate.exception.DataException: Bad value for type long : not a number this time

Для существующих заметок есть ли способ в SQL для переноса старых заметок для правильного использования типов @Lob и text? Спасибо заранее.

Ответ 1

Основываясь на Postgres large object docs, похоже, вам придется записывать каждый текстовый фрагмент в файл и импортировать его по отдельности. Это не то, что вы должны делать в SQL.

Я ничего не знаю о JPA, но что делает @Lob для DDL или для переключения баз данных? Вы полностью изменили тип столбца; что случилось с типом Postgres text?


Закрытие цикла здесь, чтобы это не было потеряно в комментариях:

Реальная проблема заключалась в том, что @Lob создает столбец Postgres text, но Hibernate рассматривает его как родной Postgres "большой объект" который хранит данные в другом месте и оставляет только таблицу в таблице (которая затем хранилась как текст в соответствии с типом столбца). Обычно это не то, что вы хотите для текста.

Решение OP состояло в том, чтобы надавить на @Type(type="org.hibernate.type.StringClobType"), чтобы заставить Hibernate хранить обычный текст.

Postgres обычно не сохраняет текст в виде пятизначного целого.:)

Ответ 2

Я использую JPA для аннотации моих сущностей и спящего режима для их сохранения. Но я не хочу добавлять аннотации hibernate для своих объектов. Поэтому у меня есть баннер данных, содержащий сущности + аннотации. Затем в моей реализации я указываю persistence.xml и добавляю "CustomTypes.hbm.xml". Который автоматически сканируется или добавляется через тег map-file в файле persistence.xml.

Это сопоставление содержит типы, которые я хочу переопределить из basictyperegistry. В этом случае используется тип materialized_clob. Который я не хочу, потому что, когда я просматриваю свою базу данных с помощью инструмента запроса, я хочу иметь возможность напрямую просматривать фактическое содержимое. Поэтому я добавляю:

<typedef name="materialized_clob" class="org.hibernate.type.TextType" />

Чтобы принудительно использовать указанный тип для всех clob без необходимости добавлять конкретные аннотации в моем пакете данных.

Вы можете просмотреть сопоставления, зарегистрировав org.hibernate.type.BasicTypeRegistry на уровне DEBUG.

Мне потребовалось некоторое время, чтобы понять это, и я надеюсь, что это поможет любому, кто сталкивается с теми же проблемами. Поскольку это, вероятно, также решит вашу проблему, я подумал, что стоит потратить ее здесь.

Ответ 3

Я уверен, что он опоздал, но для тех, у кого такая же проблема в будущем.

Я также столкнулся с аналогичной проблемой, когда у меня были старые данные в столбцах текста непосредственно в столбцах, а не как идентификаторы OID. И когда я пытался использовать эти данные с обновленным приложением, я тоже получал

Bad value for type long

Чтобы решить эту проблему, я создал этот script. Может быть, это может помочь кому-то в будущем.

Я получил большую помощь от этого сообщения здесь

Ответ 4

При использовании spring вы можете создать потомка LocalSessionFactoryBean, чтобы ввести переопределения типа Hibernate. Пример:

 public class CustomSessionFactoryBean extends LocalSessionFactoryBean {

    @Override
    protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
        // To store @Lob annotated Strings in TEXT fields.
        // By default for Postgres generated TEXT column, but stored OID of Postgres LOB object
        sfb.registerTypeOverride(new TextType() {
            @Override
            public String getName() {
                return StandardBasicTypes.MATERIALIZED_CLOB.getName();
            }
        });
        sfb.registerTypeOverride(new NTextType() {
            @Override
            public String getName() {
                return StandardBasicTypes.MATERIALIZED_NCLOB.getName();
            }
        });

        super.buildSessionFactory(sfb);
    }
 }

Вам не нужно использовать специальную аннотацию Hibernate @Type, просто аннотировать свойство String с помощью @Lob и использовать CustomSessionFactoryBean