Created
February 10, 2023 10:25
-
-
Save MuellerConstantin/f1d86fa793cfbf0a9a7341242f45dffd to your computer and use it in GitHub Desktop.
Hibernate/JPA base entity which supports soft deletes and keeps deleted records in the database.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import lombok.*; | |
import lombok.experimental.SuperBuilder; | |
import org.hibernate.annotations.Type; | |
import org.springframework.data.annotation.CreatedDate; | |
import org.springframework.data.annotation.LastModifiedDate; | |
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | |
import javax.persistence.*; | |
import java.io.Serializable; | |
import java.time.Instant; | |
import java.util.UUID; | |
@MappedSuperclass | |
@EntityListeners(AuditingEntityListener.class) | |
@AllArgsConstructor | |
@NoArgsConstructor | |
@Data | |
@SuperBuilder | |
public abstract class BaseEntity implements Serializable { | |
@Id | |
@Type(type = "uuid-char") | |
@GeneratedValue | |
@Column(name = "id", unique = true, nullable = false) | |
private UUID id; | |
@CreatedDate | |
@Column(name = "created_at", nullable = false) | |
private Instant createdAt; | |
@LastModifiedDate | |
@Column(name = "last_modified_at", nullable = false) | |
private Instant lastModifiedAt; | |
@Version | |
@Column(name = "version", nullable = false) | |
@Setter(AccessLevel.PROTECTED) | |
@Builder.Default | |
@EqualsAndHashCode.Exclude | |
private long version = 0L; | |
@Column(name = "deleted", nullable = false) | |
@Builder.Default | |
private boolean deleted = false; | |
@Column(name = "deleted_at") | |
private Instant deletedAt; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import de.x1c1b.attoly.api.domain.model.BaseEntity; | |
import org.springframework.dao.DataIntegrityViolationException; | |
import org.springframework.dao.EmptyResultDataAccessException; | |
import org.springframework.data.domain.Page; | |
import org.springframework.data.domain.Pageable; | |
import org.springframework.data.domain.Sort; | |
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; | |
import org.springframework.data.jpa.repository.Modifying; | |
import org.springframework.data.jpa.repository.Query; | |
import org.springframework.data.repository.NoRepositoryBean; | |
import org.springframework.data.repository.PagingAndSortingRepository; | |
import org.springframework.transaction.annotation.Transactional; | |
import java.time.Instant; | |
import java.util.List; | |
import java.util.Optional; | |
import java.util.UUID; | |
@NoRepositoryBean | |
public interface BaseRepository<T extends BaseEntity, ID extends UUID> extends PagingAndSortingRepository<T, ID>, JpaSpecificationExecutor<T> { | |
@Override | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = false") | |
List<T> findAll(); | |
@Override | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = false AND e.id = ?1") | |
Optional<T> findById(ID id); | |
@Override | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = false AND e.id IN ?1") | |
List<T> findAllById(Iterable<ID> ids); | |
@Override | |
@Transactional(readOnly = true) | |
@Query("SELECT count(e) FROM #{#entityName} e WHERE e.deleted = false") | |
long count(); | |
@Override | |
@Transactional(readOnly = true) | |
default boolean existsById(ID id) { | |
return findById(id).isPresent(); | |
} | |
@Override | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = false") | |
List<T> findAll(Sort sort); | |
@Override | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = false") | |
Page<T> findAll(Pageable pageable); | |
@Transactional | |
@Modifying | |
default void deleteSoftById(ID id) { | |
T entity = findById(id).orElseThrow(() -> new EmptyResultDataAccessException( | |
String.format("Entity with ID [%s] wasn't found in the database. Nothing to soft-delete.", id), 1)); | |
if (entity.isDeleted()) { | |
throw new DataIntegrityViolationException(String.format("Entity with ID [%s] is already soft-deleted.", id)); | |
} | |
entity.setDeleted(true); | |
entity.setDeletedAt(Instant.now()); | |
save(entity); | |
} | |
@Transactional | |
@Modifying | |
default void deleteSoft(T entity) { | |
if (entity.isDeleted()) { | |
throw new DataIntegrityViolationException(String.format("Entity with ID [%s] is already soft-deleted.", entity.getId())); | |
} | |
entity.setDeleted(true); | |
entity.setDeletedAt(Instant.now()); | |
save(entity); | |
} | |
@Transactional | |
@Modifying | |
default void deleteAllSoftById(Iterable<? extends ID> ids) { | |
ids.forEach(this::deleteSoftById); | |
} | |
@Transactional | |
@Modifying | |
default void deleteAllSoft(Iterable<? extends T> entities) { | |
entities.forEach(this::deleteSoft); | |
} | |
@Transactional | |
@Modifying | |
@Query("UPDATE #{#entityName} e SET e.deleted = true") | |
void deleteAllSoft(); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = true") | |
List<T> findAllDeleted(); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = true AND e.id = ?1") | |
Optional<T> findDeletedById(ID id); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = true AND e.id IN ?1") | |
List<T> findAllDeletedById(Iterable<ID> ids); | |
@Transactional(readOnly = true) | |
@Query("SELECT count(e) FROM #{#entityName} e WHERE e.deleted = true") | |
long countDeleted(); | |
@Transactional(readOnly = true) | |
default boolean existsDeletedById(ID id) { | |
return findDeletedById(id).isPresent(); | |
} | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = true") | |
List<T> findAllDeleted(Sort sort); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = true") | |
Page<T> findAllDeleted(Pageable pageable); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e") | |
List<T> findAllAny(); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.id = ?1") | |
Optional<T> findAnyById(ID id); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e WHERE e.id IN ?1") | |
List<T> findAllAnyById(Iterable<ID> ids); | |
@Transactional(readOnly = true) | |
@Query("SELECT count(e) FROM #{#entityName} e") | |
long countAny(); | |
@Transactional(readOnly = true) | |
default boolean existsAnyById(ID id) { | |
return findDeletedById(id).isPresent(); | |
} | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e") | |
List<T> findAllAny(Sort sort); | |
@Transactional(readOnly = true) | |
@Query("SELECT e FROM #{#entityName} e") | |
Page<T> findAllAny(Pageable pageable); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment