Большие объекты не могут использоваться в режиме автоматической фиксации

У меня есть приложение Spring, которое использует Hibernate в базе данных PostgreSQL. Я пытаюсь хранить файлы в таблице базы данных. Кажется, он хранит строку с файлом (я просто использую метод persist на EntityManager), но когда объект загружается из базы данных, я получаю следующее исключение:

org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.

Чтобы загрузить данные, я использую атрибут переходного процесса MultipartFile и его установщик. Я устанавливаю информацию, которую я хочу сохранить (byte [], fileName, size). Сущность, на которой я нахожусь, выглядит как эта (я опускал остальные получатели/сеттеры):

@Entity
@Table(name="myobjects")
public class MyClass {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequence")
    @SequenceGenerator(name="sequence", sequenceName="myobjects_pk_seq", allocationSize=1)
    @Column(name="id")
    private Integer id;

    @Lob
    private String description;

    @Temporal(TemporalType.TIMESTAMP)
    private Date creationDate;

    @Transient
    private MultipartFile multipartFile;

    @Lob
    @Basic(fetch=FetchType.LAZY, optional=true)
    byte[] file;

    String fileName;

    String fileContentType;

    Integer fileSize;

    public void setMultipartFile(MultipartFile multipartFile) {
        this.multipartFile = multipartFile;
        try {
            this.file = this.multipartFile.getBytes();
            this.fileName = this.multipartFile.getOriginalFilename();
            this.fileContentType = this.multipartFile.getContentType();
            this.fileSize = ((Long) this.multipartFile.getSize()).intValue();
        } catch (IOException e) {
            logger.error(e.getStackTrace());
        }
    }
}

Я вижу, что когда он сохраняется, у меня есть данные в строке, но когда я вызываю этот метод, он терпит неудачу:

public List<MyClass> findByDescription(String text) {
    Query query = getEntityManager().createQuery("from MyClass WHERE UPPER(description) like :query ORDER BY creationDate DESC");
    query.setParameter("query", "%" + text.toUpperCase() + "%");
    return query.getResultList();
}

Этот метод терпит неудачу, когда в результате есть объекты с файлами. Я попытался установить в свой persistence.xml

<property name="hibernate.connection.autocommit" value="false" />

но это не решает проблему.

В общем случае приложение работает хорошо, оно только фиксирует данные при завершении транзакции и выполняет откат, если что-то не удается, поэтому я не понимаю, почему это происходит.

Любая идея?

Спасибо.

UPDATE

Рассматривая ссылку, данную Шехаром, предлагается включить вызов в транзакцию, поэтому я установил вызов службы внутри транзакции, и он работает (я добавил аннотацию @Transactional).

@Transactional
public List<myClass> find(String text) {
    return myClassDAO.findByDescription(text);
}

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

Спасибо.

Ответы

Ответ 1

Большой объект может храниться в нескольких записях, поэтому вы должны использовать транзакцию. Все записи верны или вообще ничего.

https://www.postgresql.org/docs/current/static/largeobjects.html

Ответ 2

Если это возможно, создайте промежуточное сущность между MyClass и свойством файла, например. Что-то вроде:

@Entity
@Table(name="myobjects")
public class MyClass {
    @OneToOne(cascade = ALL, fetch = LAZY)
    private File file;
}

@Entity
@Table(name="file")
public class File {
     @Lob
     byte[] file;
}

Вы не можете использовать @Lob и тип получения Lazy. Это не работает. У вас должен быть промежуточный класс.

Ответ 3

Если вам не нужно хранить файлы размером более 1 ГБ, я предлагаю вам использовать bytea как тип данных, а не большой объект.

bytea - это в основном то, что BLOB находится в других базах данных (например, Oracle), и его обработка намного более совместима с JDBC.

Ответ 4

Вместо использования @Transactional все, что я сделал для этой проблемы, - @Transactional этот столбец в БД PostgreSQL с oid типа на bytea тип и добавил @Type для поля @Lob.

т.е.

ALTER TABLE person DROP COLUMN image;
ALTER TABLE person ADD COLUMN image bytea;

И поменял

@Lob
private byte[] image;

к

@Lob
@Type(type = "org.hibernate.type.ImageType")
private byte[] image;

Ответ 5

Вы должны проверить, действительно ли автоматическая фиксация установлена в false с помощью метода getAutoCommit() в вашем Соединении.

В моем случае

<property name="hibernate.connection.autocommit" value="false" />

не работал, потому что тип моего соединения с БД требовал автоматической фиксации, чтобы быть правдой.

В моем случае проблема была в конфигурации JBoss. Я использовал источник данных JTA, и это вызвало проблему. С источником данных XA все работало нормально. Смотрите этот пост для более подробной информации.