Как перенести строки 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