Большие объекты нельзя использовать в режиме автоматической фиксации - PullRequest
24 голосов
/ 02 июля 2010

У меня есть приложение 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);
}

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

Спасибо.

Ответы [ 5 ]

32 голосов
/ 02 июля 2010

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

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

5 голосов
/ 05 июля 2013

Если вы можете, например, создать промежуточный объект между 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.Это не работает.У вас должен быть промежуточный класс.

4 голосов
/ 04 июля 2010

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

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

0 голосов
/ 14 июня 2019

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

В моем случае

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

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

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

0 голосов
/ 28 ноября 2018

Вместо использования @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;
...