PostgreSQLを使用してSpring-Bootでサーバーを作成しています。特定のエンティティにリンクされているイメージに関する情報を取得しようとしています。サーバーからフロントエンドのAngularアプリにユーザー情報を取得しようとしています。私のシステムでは、ユーザーは自分のアカウントにリンクされた画像を持っているので、クラスImageEntityを実行しました
@Entity @Table(name = "image") @Data
public class ImageEntity {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
private String type;
@Lob
private byte[] image;
@JsonIgnore
public byte[] getImage() {
return image;
}
}
次に、画像のリストをユーザーアカウントクラスにリンクしました
@Entity @Data
public class UserAccount{
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String firstName;
private String lastName
@OneToMany(cascade = CascadeType.ALL)
@JoinTable(
name = "user_images",
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "image_id", referencedColumnName = "id")}
)
private List<ImageEntity> images;
public void addImage(ImageEntity image) {
images.add(image);
}
}
次に、IDでユーザーを取得するエンドポイントを作成します
@GetMapping("users/{id}")
public Optional<User> getUserById(@PathVariable Long id) {
return service.getUserById(id);
}
サービス方法はとても簡単です
@Transactional
public Optional<User> getUserById(Long id) {
return repository.findById(id);
}
フロントエンドで画像を取得できるため、別のエンドポイントを介していくつかの画像を追加しても問題なく動作します。
問題は、サーバーからユーザー情報をJSONとして取得したい場合です(実際の画像ではなく画像の情報のみを取得したいので、@ Lobフィールドに@JsonIgnoreと書き込みます)このエラーが発生します
Resolved exception caused by handler execution: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Unable to access lob stream; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to access lob stream (through reference chain: com.app.model.user.User["images"])
私はいくつかの同様の記事を読み、画像のゲッターで@JsonIgnoreを与えようとしました@Lob画像私は要素を取得するサービスメソッドに@Transactionalを追加しましたが、それは機能していません。
私は単にサーバーからその種のメッセージを達成したいのです:
{
id: "1"
firstName: "test",
lstName: "test_ln",
images: {
{
"id": 10,
"name": "IMG12.jpg",
"type": "image/jpeg"
},
{
"id": 20,
"name": "IMG456.jpg",
"type": "image/jpeg"
}
}
}
最速の解決策(最良の解決策ではありません)はfecth = EAGER
、OneToMany
画像の関係に追加することです...この解決策の問題は、ユーザーエンティティ(可能性があります)を処理するときに常に画像エンティティ(byte []画像を含む)をロードすることですパフォーマンスの問題)..。
次の「最善の」解決策は、EAGER
前述の構成を省略し、リポジトリに新しいメソッドを作成することです...このようなメソッドは次のようなJPAクエリを実行する必要があります。
SELECT ua
FROM
UserAccount ua
LEFT JOIN FECTH ua.images img
WHERE
ua.id = :id
これにより、ユーザーとその関連画像が読み込まれます...次に、サービスでそのようなメソッドを呼び出します(このソリューションの問題はimage
、の他の属性のみが必要な場合でも、byte []を読み込むことですImageEntity
)
最善の解決策は、ソリューション#2を拡張して、必要な属性のみを取得することですImageEntity
。その結果、次のようなクエリが生成されます。
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
WHERE
ua.id = :id
次に、リポジトリメソッドはJPATuple
を返す必要があり、サービスメソッドでは、そのタプルを返したいユーザーに変換します(関連する画像のメタデータを含む)... (UPDATE)例(コメントで指定したメソッドを使用))::
// @Transactional // Remove the transactional annotation to avoid cascade issues!
public User getUserById(Long id) {
List<ImageEntity> images;
List<Tuple> tuples;
User user;
tuples = repository.getUserById(id);
user = null;
if (!tuples.isEmpty()) {
user = tuples.get(0).get(0, User.class);
images = new ArrayList<>();
for (Tuple t : tuples) {
if (t.get(1) != null) {
images.add(new ImageEntity(
t.get(1, Long.class),
t.get(2, String.class)
));
}
}
user.setImages(images);
}
return user;
}
これを機能させるには、次のものが必要です。
getUserById
(リポジトリ内の)メソッドのシグネチャを変更して、タプルのリストを返しますImageEntity(long id, String name) { ... }
setImages(List<ImageEntity> images) { ... }
UPDATE2:このようなことを行うには、すべてのユーザーを取得するときに、次のものが必要になります。
1)クエリが次のようになるメソッドをユーザーリポジトリに作成(またはオーバーライド)します(それをfindAllと呼びましょう):
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
2)サービスで、次のようなメソッドを実装します。
public List<User> findAll(Long id) {
List<ImageEntity> images;
List<Tuple> tuples;
Map<Long, User> index;
tuples = repository.findAll();
index = new HashMap<>();
for (Tuple t : tuples) {
user = t.get(0, User.class);
if (!index.containsKey(user.getId()) {
images = new ArrayList<>();
user.setImages(images);
index.put(user.getId(), user)
} else {
user = index.get(user.getId());
images = user.getImages():
}
if (t.get(1) != null) {
images.add(new ImageEntity(
t.get(1, Long.class),
t.get(2, String.class)
));
}
}
return index.values();
}
説明:重要な点は、画像メタデータ(コード、名前、タイプのみ)を使用してユーザーを取得し、lob属性を読み込まないようにすることです(画像はMBである可能性があり、使用/シリアル化されないため)。 。そのため、次のようなクエリを実行します。
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
LEFT JOIN
(画像のないものを含む)すべてのユーザーを取得するための力
ORM(つまり、JPA実装、たとえばHibernate)は、この種のクエリをタプルオブジェクトに(常に)マップします!
クエリはNx Mタプルを生成します...ここで、Nはユーザーの合計であり、Mは画像の合計です...たとえば、2つの画像を持つユーザーが1人しかない場合、結果は2つのタプルになります。タプルのコンポーネントは常に同じユーザーであり、他のコンポーネントは各画像の属性になります。
次に、タプルオブジェクトをユーザーオブジェクトに変換する必要があります(これはサービスメソッドで行うことです)...ここでの重要なポイントは、ArrayList
新しいImageEntityを追加する前に、images属性にnewを使用することです。 。ORMはロードされたユーザーごとにプロキシリストを挿入するため、これを行う必要があります...このプロキシに何かを追加すると、ORMはそのようなプロキシの遅延読み込みを実行し、関連する画像を取得します(これは私たちがしたいことです)避ける)...
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加