The problem lies in your data modeling, your class implementation GalleryModel
does not have support for multiple photos since you are saving in a string:
@Column(name="image", nullable=false)
private String image;
I checked your project and made some adjustments, but in fact the files arrive to your app (as evidenced below)
When checking the operations performed in the bank, you can see that an insertion has been performed, and then the same gallery has been updated in subsequent calls:
Hibernate: insert into posts (author, category, content, cover, created_at, method, price, sale, status, title, updated_at, uri, views) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into galleries (created_at, image, post_id) values (?, ?, ?)
Hibernate: update galleries set created_at=?, image=?, post_id=? where id=?
Hibernate: update galleries set created_at=?, image=?, post_id=? where id=?
Hibernate: update galleries set created_at=?, image=?, post_id=? where id=?
That way you’re always saving the last photo from the loop, when in fact you should be saving all the photos and relating them to the gallery itself.
To solve this I created an associative table called gallery_photo
, that will store the gallery photo records, and I changed the association in class gallery
:
@Data
@Entity
@Table(name="galleries")
public class GalleryModel implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
@Column(name="post_id", nullable=false)
private long postId;
@Column(name="created_at", columnDefinition="TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP")
private Date createdAt;
@ManyToMany(mappedBy = "gallery", cascade = CascadeType.ALL)
private Set<GalleryPhotoModel> photos;
}
@Data
@Entity
@Table(name="gallery_photo")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class GalleryPhotoModel {
public GalleryPhotoModel(String image) {
super();
this.image = image;
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
@Column
private String image;
@ManyToOne
private GalleryModel gallery;
}
Now when inserting the galleries
, all photos are saved:
Hibernate: insert into posts (author, category, content, cover, created_at, method, price, sale, status, title, updated_at, uri, views) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into galleries (created_at, post_id) values (?, ?)
Hibernate: insert into gallery_photo (gallery_id, image) values (?, ?)
Hibernate: insert into gallery_photo (gallery_id, image) values (?, ?)
Hibernate: insert into gallery_photo (gallery_id, image) values (?, ?)
Hibernate: insert into gallery_photo (gallery_id, image) values (?, ?)
Oh another thing, I suggest you create a new layer in your application to services
, to isolate the service layer (rules, conversions, etc...) of your controller, and thus reuse more code. I refactored the code that inserts the gallery, always remember the principle DRY (Don’t Repeat Yourself!), now the method has gone like this:
private void salvarGalerias(GalleryModel gallery, Long postId, List<MultipartFile> galleries) throws IOException {
for(MultipartFile gy : galleries) {
Path galleryNameAndPath = Paths.get(uploadDirectory, gy.getOriginalFilename());
Files.write(galleryNameAndPath, gy.getBytes());
}
gallery.setPostId(postId);
gallery.setCreatedAt(new Date(System.currentTimeMillis()));
gallery.setPhotos(galleries.stream().map(MultipartFile::getOriginalFilename).map(GalleryPhotoModel::new).collect(Collectors.toSet()));
galleryRepository.save(gallery);
}
It should be called whenever you insert a new galleries
, this method should be used (hence the idea of extracting it for a GalleryService
):
@PostMapping("/publicar")
public String postPublish(PostModel post, GalleryModel gallery,
@RequestParam("img_cover") MultipartFile cover,
@RequestParam("img_gallery") MultipartFile[] galleries) throws IOException {
Path fileNameAndPath = Paths.get(uploadDirectory, cover.getOriginalFilename());
Files.write(fileNameAndPath, cover.getBytes());
post.setAuthor(authUtility.getUserLogged().getId());
post.setCover(cover.getOriginalFilename());
post.setStatus(1);
post.setPrice(post.getPrice().replace(",", "."));
post.setUri(uriUtility.uri(post.getTitle()));
post.setCreatedAt(new Date(System.currentTimeMillis()));
postRepository.save(post);
salvarGalerias(gallery, post.getId(), Arrays.asList(galleries));
return "redirect:/publicar";
}
@PostMapping("/galeria/enviar")
public String enviarGaleria(GalleryModel gallery, @RequestParam("ref_gallery") Long postId,
@RequestParam(name="img_gallery", required=true) List<MultipartFile> galleries) throws IOException {
salvarGalerias(gallery, postId, galleries);
return "redirect:/anuncios";
}
There are several other details to correct (names of entities and tables, use Dtos and not entities for interaction with the controllers, layers of the app...), but for the purpose you wanted this will help you already :)
Well if you are saving only one you are probably getting only one, if possible edit the question and include the code you are using to send, I have never used Multipartfile this way, I have thus working @Requestparam(name = "files", required = true) List<Multipartfile> files,
– rnd_rss
Edited question, put the project on my github, feel free to evaluate the problem :D
– Kevin Sampaio
I downloaded your Github project and found the problem, then put the solution and send you a pull request
– nullptr