PostgreSQLデータベースでHibernateを使用するSpringアプリケーションがあります。データベースのテーブルにファイルを保存しようとしています。ファイルとともに行を格納しているようですが(EntityManagerでpersistメソッドを使用しているだけです)、オブジェクトがデータベースから読み込まれると、次の例外が発生します。
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" />
しかし、それは問題を解決しません。
一般にアプリケーションはうまく機能し、トランザクションが終了したときにのみデータをコミットし、何かが失敗した場合にロールバックを実行するので、なぜこれが起こっているのかわかりません。
何か案が?
ありがとう。
[〜#〜]更新[〜#〜]
Shekharによって提供されたリンクを見ると、トランザクションに呼び出しを含めることが提案されているので、トランザクション内にサービス呼び出しを設定し、それが機能するようにしました(@Transactionalアノテーションを追加しました)。
@Transactional
public List<myClass> find(String text) {
return myClassDAO.findByDescription(text);
}
問題は、データを永続化したくないので、なぜデータをトランザクション内に含める必要があるのか理解できないことです。データベースから一部のデータのみをロードしたときにコミットするのは意味がありますか?
ありがとう。
大きなオブジェクトは複数のレコードに格納できるため、トランザクションを使用する必要があります。すべての記録が正しいか、まったくありません。
https://www.postgresql.org/docs/current/static/largeobjects.html
可能であれば、たとえばMyClassとfileプロパティの間に中間エンティティを作成します。何かのようなもの:
@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をフェッチすることはできません。動作しません。中級クラスが必要です。
1GBを超えるファイルを保存する必要がない限り、ラージオブジェクトの代わりにbyteaをデータ型として使用することをお勧めします。
byteaは基本的に、他のデータベース(Oracleなど)にあるBLOBであり、その処理はJDBCとはるかに互換性があります。
接続のメソッド getAutoCommit() を使用して、自動コミットが実際にfalseに設定されているかどうかを確認する必要があります。
私の場合
<property name="hibernate.connection.autocommit" value="false" />
私のdb接続のタイプは自動コミットがtrueである必要があるため、機能しませんでした。
私の場合、問題はJBoss構成にありました。 JTAデータソースを使用していたので、問題が発生しました。 XAデータソースを使用すると、正常に機能しました。詳細については、 この投稿 を参照してください。
_@Transactional
_を使用する代わりに、この問題のためにPostgreSQL DBでこの列を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;
_