Skip to content

Commit

Permalink
Store images in a separate table and fetch them on demand
Browse files Browse the repository at this point in the history
This fixes vaadin/starters#85

It also makes image handling smarter as the images are not loaded as part of an entity, which you rarely want
  • Loading branch information
Artur- committed Oct 6, 2022
1 parent 867136c commit 4bd44ae
Show file tree
Hide file tree
Showing 11 changed files with 287 additions and 269 deletions.
29 changes: 29 additions & 0 deletions src/main/java/my/app/data/entity/ImageData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package my.app.data.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;

@Entity
public class ImageData extends AbstractEntity {

@Lob
@Column(length = 1000000)
private byte[] data;

public ImageData() {
}

public ImageData(byte[] data) {
this.data = data;
}

public void setData(byte[] data) {
this.data = data;
}

public byte[] getData() {
return data;
}

}
25 changes: 16 additions & 9 deletions src/main/java/my/app/data/entity/SampleBook.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
package my.app.data.entity;

import java.time.LocalDate;
import javax.persistence.Column;
import java.util.UUID;

import javax.annotation.Nonnull;
import javax.persistence.Entity;
import javax.persistence.Lob;

import org.hibernate.annotations.Type;

@Entity
public class SampleBook extends AbstractEntity {

@Lob
@Column(length = 1000000)
private byte[] image;
@Nonnull
private String name;
@Nonnull
private String author;
private LocalDate publicationDate;
@Nonnull
private Integer pages;
@Nonnull
private String isbn;
@Type(type = "uuid-char")
private UUID imageId;

public byte[] getImage() {
return image;
public UUID getImageId() {
return imageId;
}
public void setImage(byte[] image) {
this.image = image;

public void setImage(UUID imageId) {
this.imageId = imageId;
}
public String getName() {
return name;
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/my/app/data/entity/SamplePerson.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package my.app.data.entity;

import java.time.LocalDate;
import javax.annotation.Nonnull;
import javax.persistence.Entity;
import javax.validation.constraints.Email;

@Entity
public class SamplePerson extends AbstractEntity {

@Nonnull
private String firstName;
@Nonnull
private String lastName;
@Email
@Nonnull
private String email;
@Nonnull
private String phone;
private LocalDate dateOfBirth;
@Nonnull
private String occupation;
private boolean important;

Expand Down
31 changes: 20 additions & 11 deletions src/main/java/my/app/data/entity/User.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
package my.app.data.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Set;
import javax.persistence.Column;
import java.util.UUID;

import javax.annotation.Nonnull;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Lob;
import javax.persistence.Table;

import org.hibernate.annotations.Type;

import com.fasterxml.jackson.annotation.JsonIgnore;

import my.app.data.Role;

@Entity
@Table(name = "application_user")
public class User extends AbstractEntity {

@Nonnull
private String username;
@Nonnull
private String name;
@JsonIgnore
private String hashedPassword;
@Enumerated(EnumType.STRING)
@ElementCollection(fetch = FetchType.EAGER)
@Nonnull
private Set<Role> roles;
@Lob
@Column(length = 1000000)
private byte[] profilePicture;
@Nonnull
@Type(type = "uuid-char")
private UUID profilePictureId;

public String getUsername() {
return username;
Expand All @@ -51,11 +59,12 @@ public Set<Role> getRoles() {
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public byte[] getProfilePicture() {
return profilePicture;
}
public void setProfilePicture(byte[] profilePicture) {
this.profilePicture = profilePicture;

public UUID getProfilePictureId() {
return profilePictureId;
}

public void setProfilePictureId(UUID profilePictureId) {
this.profilePictureId = profilePictureId;
}
}
11 changes: 11 additions & 0 deletions src/main/java/my/app/data/service/ImageDataRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package my.app.data.service;

import java.util.UUID;

import org.springframework.data.jpa.repository.JpaRepository;

import my.app.data.entity.ImageData;

public interface ImageDataRepository extends JpaRepository<ImageData, UUID> {

}
26 changes: 24 additions & 2 deletions src/main/java/my/app/data/service/SampleBookService.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package my.app.data.service;

import java.util.Base64;
import java.util.Optional;
import java.util.UUID;

import javax.transaction.Transactional;

import my.app.data.entity.ImageData;
import my.app.data.entity.SampleBook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
Expand All @@ -12,17 +17,24 @@
public class SampleBookService {

private final SampleBookRepository repository;
private final ImageDataRepository imageDataRepository;

@Autowired
public SampleBookService(SampleBookRepository repository) {
public SampleBookService(SampleBookRepository repository, ImageDataRepository imageDataRepository) {
this.repository = repository;
this.imageDataRepository = imageDataRepository;
}

public Optional<SampleBook> get(UUID id) {
return repository.findById(id);
}

public SampleBook update(SampleBook entity) {
@Transactional
public SampleBook update(SampleBook entity, ImageData sampleBookImage) {
if (sampleBookImage != null) {
sampleBookImage = imageDataRepository.save(sampleBookImage);
entity.setImage(sampleBookImage.getId());
}
return repository.save(entity);
}

Expand All @@ -38,4 +50,14 @@ public int count() {
return (int) repository.count();
}

@Transactional
public String getImageUrl(SampleBook entity) {
Optional<ImageData> image = imageDataRepository.findById(entity.getImageId());
if (image.isEmpty()) {
return "";
}
return "data:image;base64," + Base64.getEncoder().encodeToString(image.get().getData());
}


}
30 changes: 25 additions & 5 deletions src/main/java/my/app/data/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
package my.app.data.service;

import java.util.Base64;
import java.util.Optional;
import java.util.UUID;
import my.app.data.entity.User;
import org.springframework.beans.factory.annotation.Autowired;

import javax.transaction.Transactional;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import my.app.data.entity.ImageData;
import my.app.data.entity.User;

@Service
public class UserService {

private final UserRepository repository;
private final ImageDataRepository imageDataRepository;

@Autowired
public UserService(UserRepository repository) {
public UserService(UserRepository repository, ImageDataRepository imageDataRepository) {
this.repository = repository;
this.imageDataRepository = imageDataRepository;
}

public Optional<User> get(UUID id) {
return repository.findById(id);
}

public User update(User entity) {
@Transactional
public User update(User entity, ImageData profilePicture) {
if (profilePicture != null) {
profilePicture = imageDataRepository.save(profilePicture);
entity.setProfilePictureId(profilePicture.getId());
}
return repository.save(entity);
}

Expand All @@ -38,4 +49,13 @@ public int count() {
return (int) repository.count();
}

@Transactional
public String getProfilePictureUrl(User user) {
Optional<ImageData> image = imageDataRepository.findById(user.getProfilePictureId());
if (image.isEmpty()) {
return "";
}
return "data:image;base64," + Base64.getEncoder().encodeToString(image.get().getData());
}

}
19 changes: 11 additions & 8 deletions src/main/java/my/app/views/MainLayout.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package my.app.views;

import java.io.ByteArrayInputStream;
import java.util.Optional;

import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.applayout.DrawerToggle;
import com.vaadin.flow.component.avatar.Avatar;
Expand All @@ -17,11 +20,11 @@
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.auth.AccessAnnotationChecker;
import com.vaadin.flow.theme.lumo.LumoUtility;
import java.io.ByteArrayInputStream;
import java.util.Optional;

import my.app.components.appnav.AppNav;
import my.app.components.appnav.AppNavItem;
import my.app.data.entity.User;
import my.app.data.service.UserService;
import my.app.security.AuthenticatedUser;
import my.app.views.adminroleonly.AdminroleonlyView;
import my.app.views.loggedin.LoggedInView;
Expand All @@ -37,12 +40,14 @@ public class MainLayout extends AppLayout {

private H2 viewTitle;

private AuthenticatedUser authenticatedUser;
private AccessAnnotationChecker accessChecker;
private final AuthenticatedUser authenticatedUser;
private final AccessAnnotationChecker accessChecker;
private final UserService userService;

public MainLayout(AuthenticatedUser authenticatedUser, AccessAnnotationChecker accessChecker) {
public MainLayout(AuthenticatedUser authenticatedUser, AccessAnnotationChecker accessChecker, UserService userService) {
this.authenticatedUser = authenticatedUser;
this.accessChecker = accessChecker;
this.userService = userService;

setPrimarySection(Section.DRAWER);
addDrawerContent();
Expand Down Expand Up @@ -110,9 +115,7 @@ private Footer createFooter() {
User user = maybeUser.get();

Avatar avatar = new Avatar(user.getName());
StreamResource resource = new StreamResource("profile-pic",
() -> new ByteArrayInputStream(user.getProfilePicture()));
avatar.setImageResource(resource);
avatar.setImage(userService.getProfilePictureUrl(user));
avatar.setThemeName("xsmall");
avatar.getElement().setAttribute("tabindex", "-1");

Expand Down
Loading

0 comments on commit 4bd44ae

Please sign in to comment.