diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookMetadataRepository.kt b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookMetadataRepository.kt index 37966bf2a..95d60d9aa 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookMetadataRepository.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookMetadataRepository.kt @@ -16,4 +16,6 @@ interface BookMetadataRepository { fun delete(bookId: String) fun delete(bookIds: Collection) + + fun count(): Long } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/MediaRepository.kt b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/MediaRepository.kt index 84dab34a6..f8ac1cdc9 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/MediaRepository.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/MediaRepository.kt @@ -15,4 +15,6 @@ interface MediaRepository { fun delete(bookId: String) fun deleteByBookIds(bookIds: Collection) + + fun count(): Long } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookConverter.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookConverter.kt index d093f30e3..bb2443bbd 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookConverter.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookConverter.kt @@ -15,6 +15,7 @@ import org.gotson.komga.domain.persistence.BookRepository import org.gotson.komga.domain.persistence.LibraryRepository import org.gotson.komga.domain.persistence.MediaRepository import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.io.FileNotFoundException import java.nio.file.FileAlreadyExistsException import java.util.zip.Deflater @@ -54,6 +55,7 @@ class BookConverter( fun getConvertibleBookIds(library: Library): Collection = bookRepository.findAllIdsByLibraryIdAndMediaTypes(library.id, convertibleTypes) + @Transactional fun convertToCbz(book: Book) { if (!libraryRepository.findById(book.libraryId).convertToCbz) return logger.info { "Book conversion is disabled for the library, it may have changed since the task was submitted, skipping" } @@ -134,6 +136,7 @@ class BookConverter( bookRepository.findAllIdsByLibraryIdAndMismatchedExtension(library.id, mediaType, extension) } + @Transactional fun repairExtension(book: Book) { if (!libraryRepository.findById(book.libraryId).repairExtensions) return logger.info { "Repair extensions is disabled for the library, it may have changed since the task was submitted, skipping" } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt index ab6d840cd..651d097e5 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt @@ -21,6 +21,7 @@ import org.gotson.komga.domain.persistence.ThumbnailBookRepository import org.gotson.komga.infrastructure.image.ImageConverter import org.gotson.komga.infrastructure.image.ImageType import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.io.File import java.nio.file.Files import java.nio.file.Paths @@ -40,6 +41,7 @@ class BookLifecycle( private val eventPublisher: EventPublisher, ) { + @Transactional fun analyzeAndPersist(book: Book): Boolean { logger.info { "Analyze and persist book: $book" } val media = bookAnalyzer.analyze(book) @@ -58,6 +60,7 @@ class BookLifecycle( return media.status == Media.Status.READY } + @Transactional fun generateThumbnailAndPersist(book: Book) { logger.info { "Generate thumbnail and persist for book: $book" } try { @@ -67,6 +70,7 @@ class BookLifecycle( } } + @Transactional fun addThumbnailForBook(thumbnail: ThumbnailBook) { when (thumbnail.type) { ThumbnailBook.Type.GENERATED -> { @@ -93,6 +97,7 @@ class BookLifecycle( thumbnailsHouseKeeping(thumbnail.bookId) } + @Transactional fun getThumbnail(bookId: String): ThumbnailBook? { val selected = thumbnailBookRepository.findSelectedByBookIdOrNull(bookId) diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/KomgaUserLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/KomgaUserLifecycle.kt index 3fe49a214..e5c2ed5dd 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/KomgaUserLifecycle.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/KomgaUserLifecycle.kt @@ -12,6 +12,7 @@ import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.security.core.userdetails.UsernameNotFoundException import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional private val logger = KotlinLogging.logger {} @@ -54,6 +55,7 @@ class KomgaUserLifecycle( return createdUser } + @Transactional fun deleteUser(user: KomgaUser) { logger.info { "Deleting user: $user" } readProgressRepository.deleteByUserId(user.id) diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryContentLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryContentLifecycle.kt index 510142aad..f4b5e353e 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryContentLifecycle.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryContentLifecycle.kt @@ -14,6 +14,7 @@ import org.gotson.komga.domain.persistence.SidecarRepository import org.gotson.komga.infrastructure.configuration.KomgaProperties import org.gotson.komga.infrastructure.language.notEquals import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.nio.file.Paths import kotlin.time.measureTime @@ -34,6 +35,7 @@ class LibraryContentLifecycle( private val taskReceiver: TaskReceiver, ) { + @Transactional fun scanRootFolder(library: Library) { logger.info { "Updating library: $library" } measureTime { diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryLifecycle.kt index f1ee5afb0..4b98def83 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryLifecycle.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/LibraryLifecycle.kt @@ -12,6 +12,7 @@ import org.gotson.komga.domain.persistence.LibraryRepository import org.gotson.komga.domain.persistence.SeriesRepository import org.gotson.komga.domain.persistence.SidecarRepository import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.io.FileNotFoundException import java.nio.file.Files @@ -77,6 +78,7 @@ class LibraryLifecycle( } } + @Transactional fun deleteLibrary(library: Library) { logger.info { "Deleting library: $library" } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataLifecycle.kt index c1743e8ae..27e67a65e 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataLifecycle.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataLifecycle.kt @@ -25,6 +25,7 @@ import org.gotson.komga.infrastructure.metadata.barcode.IsbnBarcodeProvider import org.gotson.komga.infrastructure.metadata.comicrack.ComicInfoProvider import org.gotson.komga.infrastructure.metadata.epub.EpubMetadataProvider import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional private val logger = KotlinLogging.logger {} @@ -47,6 +48,7 @@ class MetadataLifecycle( private val eventPublisher: EventPublisher, ) { + @Transactional fun refreshMetadata(book: Book, capabilities: List) { logger.info { "Refresh metadata for book: $book with capabilities: $capabilities" } val media = mediaRepository.findById(book.id) @@ -141,6 +143,7 @@ class MetadataLifecycle( } } + @Transactional fun refreshMetadata(series: Series) { logger.info { "Refresh metadata for series: $series" } @@ -230,6 +233,7 @@ class MetadataLifecycle( } } + @Transactional fun aggregateMetadata(series: Series) { logger.info { "Aggregate book metadata for series: $series" } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt index 2ef64684e..ea80d097a 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt @@ -26,6 +26,7 @@ import org.gotson.komga.domain.persistence.SeriesMetadataRepository import org.gotson.komga.domain.persistence.SeriesRepository import org.gotson.komga.domain.persistence.ThumbnailSeriesRepository import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.io.File import java.nio.file.Files import java.nio.file.Paths @@ -89,6 +90,7 @@ class SeriesLifecycle( } } + @Transactional fun addBooks(series: Series, booksToAdd: Collection) { booksToAdd.forEach { check(it.libraryId == series.libraryId) { "Cannot add book to series if they don't share the same libraryId" } @@ -113,6 +115,7 @@ class SeriesLifecycle( toAdd.forEach { eventPublisher.publishEvent(DomainEvent.BookAdded(it)) } } + @Transactional fun createSeries(series: Series): Series { seriesRepository.insert(series) @@ -133,6 +136,7 @@ class SeriesLifecycle( return seriesRepository.findByIdOrNull(series.id)!! } + @Transactional fun deleteMany(series: Collection) { val seriesIds = series.map { it.id } logger.info { "Delete series ids: $seriesIds" } @@ -167,6 +171,7 @@ class SeriesLifecycle( progresses.forEach { eventPublisher.publishEvent(DomainEvent.ReadProgressDeleted(it)) } } + @Transactional fun getThumbnail(seriesId: String): ThumbnailSeries? { val selected = thumbnailsSeriesRepository.findSelectedBySeriesIdOrNull(seriesId) @@ -189,6 +194,7 @@ class SeriesLifecycle( return null } + @Transactional fun addThumbnailForSeries(thumbnail: ThumbnailSeries) { // delete existing thumbnail with the same url thumbnailsSeriesRepository.findAllBySeriesId(thumbnail.seriesId) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt index fec29cc10..65c626461 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt @@ -14,6 +14,7 @@ import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.net.URL import java.time.LocalDateTime import java.time.ZoneId @@ -176,54 +177,54 @@ class BookDao( .and(b.URL.notLike("%.$extension")) .fetch(b.ID) + @Transactional override fun insert(book: Book) { insert(listOf(book)) } + @Transactional override fun insert(books: Collection) { if (books.isNotEmpty()) { - dsl.transaction { config -> - config.dsl().batch( - config.dsl().insertInto( - b, - b.ID, - b.NAME, - b.URL, - b.NUMBER, - b.FILE_LAST_MODIFIED, - b.FILE_SIZE, - b.LIBRARY_ID, - b.SERIES_ID - ).values(null as String?, null, null, null, null, null, null, null) - ).also { step -> - books.forEach { - step.bind( - it.id, - it.name, - it.url, - it.number, - it.fileLastModified, - it.fileSize, - it.libraryId, - it.seriesId - ) - } - }.execute() - } + dsl.batch( + dsl.insertInto( + b, + b.ID, + b.NAME, + b.URL, + b.NUMBER, + b.FILE_LAST_MODIFIED, + b.FILE_SIZE, + b.LIBRARY_ID, + b.SERIES_ID + ).values(null as String?, null, null, null, null, null, null, null) + ).also { step -> + books.forEach { + step.bind( + it.id, + it.name, + it.url, + it.number, + it.fileLastModified, + it.fileSize, + it.libraryId, + it.seriesId + ) + } + }.execute() } } + @Transactional override fun update(book: Book) { - update(dsl, book) + updateBook(book) } + @Transactional override fun update(books: Collection) { - dsl.transaction { config -> - books.map { update(config.dsl(), it) } - } + books.map { updateBook(it) } } - private fun update(dsl: DSLContext, book: Book) { + private fun updateBook(book: Book) { dsl.update(b) .set(b.NAME, book.name) .set(b.URL, book.url.toString()) @@ -237,28 +238,19 @@ class BookDao( .execute() } + @Transactional override fun delete(bookId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(b).where(b.ID.eq(bookId)).execute() - } - } + dsl.deleteFrom(b).where(b.ID.eq(bookId)).execute() } + @Transactional override fun delete(bookIds: Collection) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(b).where(b.ID.`in`(bookIds)).execute() - } - } + dsl.deleteFrom(b).where(b.ID.`in`(bookIds)).execute() } + @Transactional override fun deleteAll() { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(b).execute() - } - } + dsl.deleteFrom(b).execute() } override fun count(): Long = dsl.fetchCount(b).toLong() diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataAggregationDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataAggregationDao.kt index 486241e29..764e2ced4 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataAggregationDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataAggregationDao.kt @@ -8,6 +8,7 @@ import org.gotson.komga.jooq.tables.records.BookMetadataAggregationAuthorRecord import org.gotson.komga.jooq.tables.records.BookMetadataAggregationRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId @@ -39,38 +40,36 @@ class BookMetadataAggregationDao( dr.toDomain(ar.filterNot { it.name == null }.map { it.toDomain() }) } + @Transactional override fun insert(metadata: BookMetadataAggregation) { - dsl.transaction { config -> - config.dsl().insertInto(d) - .set(d.SERIES_ID, metadata.seriesId) - .set(d.RELEASE_DATE, metadata.releaseDate) - .set(d.SUMMARY, metadata.summary) - .set(d.SUMMARY_NUMBER, metadata.summaryNumber) - .execute() + dsl.insertInto(d) + .set(d.SERIES_ID, metadata.seriesId) + .set(d.RELEASE_DATE, metadata.releaseDate) + .set(d.SUMMARY, metadata.summary) + .set(d.SUMMARY_NUMBER, metadata.summaryNumber) + .execute() - insertAuthors(config.dsl(), metadata) - } + insertAuthors(metadata) } + @Transactional override fun update(metadata: BookMetadataAggregation) { - dsl.transaction { config -> - config.dsl().update(d) - .set(d.SUMMARY, metadata.summary) - .set(d.SUMMARY_NUMBER, metadata.summaryNumber) - .set(d.RELEASE_DATE, metadata.releaseDate) - .set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) - .where(d.SERIES_ID.eq(metadata.seriesId)) - .execute() + dsl.update(d) + .set(d.SUMMARY, metadata.summary) + .set(d.SUMMARY_NUMBER, metadata.summaryNumber) + .set(d.RELEASE_DATE, metadata.releaseDate) + .set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + .where(d.SERIES_ID.eq(metadata.seriesId)) + .execute() - config.dsl().deleteFrom(a) - .where(a.SERIES_ID.eq(metadata.seriesId)) - .execute() + dsl.deleteFrom(a) + .where(a.SERIES_ID.eq(metadata.seriesId)) + .execute() - insertAuthors(config.dsl(), metadata) - } + insertAuthors(metadata) } - private fun insertAuthors(dsl: DSLContext, metadata: BookMetadataAggregation) { + private fun insertAuthors(metadata: BookMetadataAggregation) { if (metadata.authors.isNotEmpty()) { dsl.batch( dsl.insertInto(a, a.SERIES_ID, a.NAME, a.ROLE) @@ -83,22 +82,16 @@ class BookMetadataAggregationDao( } } + @Transactional override fun delete(seriesId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(a).where(a.SERIES_ID.eq(seriesId)).execute() - deleteFrom(d).where(d.SERIES_ID.eq(seriesId)).execute() - } - } + dsl.deleteFrom(a).where(a.SERIES_ID.eq(seriesId)).execute() + dsl.deleteFrom(d).where(d.SERIES_ID.eq(seriesId)).execute() } + @Transactional override fun delete(seriesIds: Collection) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(a).where(a.SERIES_ID.`in`(seriesIds)).execute() - deleteFrom(d).where(d.SERIES_ID.`in`(seriesIds)).execute() - } - } + dsl.deleteFrom(a).where(a.SERIES_ID.`in`(seriesIds)).execute() + dsl.deleteFrom(d).where(d.SERIES_ID.`in`(seriesIds)).execute() } override fun count(): Long = dsl.fetchCount(d).toLong() diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt index 17dab5ec8..52dcc9f96 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt @@ -8,6 +8,7 @@ import org.gotson.komga.jooq.tables.records.BookMetadataAuthorRecord import org.gotson.komga.jooq.tables.records.BookMetadataRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId @@ -51,108 +52,102 @@ class BookMetadataDao( .mapNotNull { it.tag } .toSet() + @Transactional override fun insert(metadata: BookMetadata) { insert(listOf(metadata)) } + @Transactional override fun insert(metadatas: Collection) { if (metadatas.isNotEmpty()) { - dsl.transaction { config -> - config.dsl().batch( - config.dsl().insertInto( - d, - d.BOOK_ID, - d.TITLE, - d.TITLE_LOCK, - d.SUMMARY, - d.SUMMARY_LOCK, - d.NUMBER, - d.NUMBER_LOCK, - d.NUMBER_SORT, - d.NUMBER_SORT_LOCK, - d.RELEASE_DATE, - d.RELEASE_DATE_LOCK, - d.AUTHORS_LOCK, - d.TAGS_LOCK, - d.ISBN, - d.ISBN_LOCK - ).values(null as String?, null, null, null, null, null, null, null, null, null, null, null, null, null, null) - ).also { step -> - metadatas.forEach { - step.bind( - it.bookId, - it.title, - it.titleLock, - it.summary, - it.summaryLock, - it.number, - it.numberLock, - it.numberSort, - it.numberSortLock, - it.releaseDate, - it.releaseDateLock, - it.authorsLock, - it.tagsLock, - it.isbn, - it.isbnLock - ) - } - }.execute() + dsl.batch( + dsl.insertInto( + d, + d.BOOK_ID, + d.TITLE, + d.TITLE_LOCK, + d.SUMMARY, + d.SUMMARY_LOCK, + d.NUMBER, + d.NUMBER_LOCK, + d.NUMBER_SORT, + d.NUMBER_SORT_LOCK, + d.RELEASE_DATE, + d.RELEASE_DATE_LOCK, + d.AUTHORS_LOCK, + d.TAGS_LOCK, + d.ISBN, + d.ISBN_LOCK + ).values(null as String?, null, null, null, null, null, null, null, null, null, null, null, null, null, null) + ).also { step -> + metadatas.forEach { + step.bind( + it.bookId, + it.title, + it.titleLock, + it.summary, + it.summaryLock, + it.number, + it.numberLock, + it.numberSort, + it.numberSortLock, + it.releaseDate, + it.releaseDateLock, + it.authorsLock, + it.tagsLock, + it.isbn, + it.isbnLock + ) + } + }.execute() - insertAuthors(config.dsl(), metadatas) - insertTags(config.dsl(), metadatas) - } + insertAuthors(metadatas) + insertTags(metadatas) } } + @Transactional override fun update(metadata: BookMetadata) { - dsl.transaction { config -> - updateMetadata(config.dsl(), metadata) - } + updateMetadata(metadata) } + @Transactional override fun update(metadatas: Collection) { - dsl.transaction { config -> - metadatas.forEach { updateMetadata(config.dsl(), it) } - } + metadatas.forEach { updateMetadata(it) } } - private fun updateMetadata(dsl: DSLContext, metadata: BookMetadata) { - dsl.transaction { config -> - with(config.dsl()) { - update(d) - .set(d.TITLE, metadata.title) - .set(d.TITLE_LOCK, metadata.titleLock) - .set(d.SUMMARY, metadata.summary) - .set(d.SUMMARY_LOCK, metadata.summaryLock) - .set(d.NUMBER, metadata.number) - .set(d.NUMBER_LOCK, metadata.numberLock) - .set(d.NUMBER_SORT, metadata.numberSort) - .set(d.NUMBER_SORT_LOCK, metadata.numberSortLock) - .set(d.RELEASE_DATE, metadata.releaseDate) - .set(d.RELEASE_DATE_LOCK, metadata.releaseDateLock) - .set(d.AUTHORS_LOCK, metadata.authorsLock) - .set(d.TAGS_LOCK, metadata.tagsLock) - .set(d.ISBN, metadata.isbn) - .set(d.ISBN_LOCK, metadata.isbnLock) - .set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) - .where(d.BOOK_ID.eq(metadata.bookId)) - .execute() + private fun updateMetadata(metadata: BookMetadata) { + dsl.update(d) + .set(d.TITLE, metadata.title) + .set(d.TITLE_LOCK, metadata.titleLock) + .set(d.SUMMARY, metadata.summary) + .set(d.SUMMARY_LOCK, metadata.summaryLock) + .set(d.NUMBER, metadata.number) + .set(d.NUMBER_LOCK, metadata.numberLock) + .set(d.NUMBER_SORT, metadata.numberSort) + .set(d.NUMBER_SORT_LOCK, metadata.numberSortLock) + .set(d.RELEASE_DATE, metadata.releaseDate) + .set(d.RELEASE_DATE_LOCK, metadata.releaseDateLock) + .set(d.AUTHORS_LOCK, metadata.authorsLock) + .set(d.TAGS_LOCK, metadata.tagsLock) + .set(d.ISBN, metadata.isbn) + .set(d.ISBN_LOCK, metadata.isbnLock) + .set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + .where(d.BOOK_ID.eq(metadata.bookId)) + .execute() - deleteFrom(a) - .where(a.BOOK_ID.eq(metadata.bookId)) - .execute() - deleteFrom(bt) - .where(bt.BOOK_ID.eq(metadata.bookId)) - .execute() + dsl.deleteFrom(a) + .where(a.BOOK_ID.eq(metadata.bookId)) + .execute() + dsl.deleteFrom(bt) + .where(bt.BOOK_ID.eq(metadata.bookId)) + .execute() - insertAuthors(this, listOf(metadata)) - insertTags(config.dsl(), listOf(metadata)) - } - } + insertAuthors(listOf(metadata)) + insertTags(listOf(metadata)) } - private fun insertAuthors(dsl: DSLContext, metadatas: Collection) { + private fun insertAuthors(metadatas: Collection) { if (metadatas.any { it.authors.isNotEmpty() }) { dsl.batch( dsl.insertInto(a, a.BOOK_ID, a.NAME, a.ROLE) @@ -167,7 +162,7 @@ class BookMetadataDao( } } - private fun insertTags(dsl: DSLContext, metadatas: Collection) { + private fun insertTags(metadatas: Collection) { if (metadatas.any { it.tags.isNotEmpty() }) { dsl.batch( dsl.insertInto(bt, bt.BOOK_ID, bt.TAG) @@ -182,26 +177,22 @@ class BookMetadataDao( } } + @Transactional override fun delete(bookId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(a).where(a.BOOK_ID.eq(bookId)).execute() - deleteFrom(bt).where(bt.BOOK_ID.eq(bookId)).execute() - deleteFrom(d).where(d.BOOK_ID.eq(bookId)).execute() - } - } + dsl.deleteFrom(a).where(a.BOOK_ID.eq(bookId)).execute() + dsl.deleteFrom(bt).where(bt.BOOK_ID.eq(bookId)).execute() + dsl.deleteFrom(d).where(d.BOOK_ID.eq(bookId)).execute() } + @Transactional override fun delete(bookIds: Collection) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(a).where(a.BOOK_ID.`in`(bookIds)).execute() - deleteFrom(bt).where(bt.BOOK_ID.`in`(bookIds)).execute() - deleteFrom(d).where(d.BOOK_ID.`in`(bookIds)).execute() - } - } + dsl.deleteFrom(a).where(a.BOOK_ID.`in`(bookIds)).execute() + dsl.deleteFrom(bt).where(bt.BOOK_ID.`in`(bookIds)).execute() + dsl.deleteFrom(d).where(d.BOOK_ID.`in`(bookIds)).execute() } + override fun count(): Long = dsl.fetchCount(d).toLong() + private fun BookMetadataRecord.toDomain(authors: List, tags: Set) = BookMetadata( title = title, diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt index 02a8c8f34..cfcc657ea 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaUserDao.kt @@ -7,6 +7,7 @@ import org.jooq.DSLContext import org.jooq.Record import org.jooq.ResultQuery import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId @@ -54,73 +55,61 @@ class KomgaUserDao( ) } + @Transactional override fun insert(user: KomgaUser) { - dsl.transaction { config -> - with(config.dsl()) { - insertInto(u) - .set(u.ID, user.id) - .set(u.EMAIL, user.email) - .set(u.PASSWORD, user.password) - .set(u.ROLE_ADMIN, user.roleAdmin) - .set(u.ROLE_FILE_DOWNLOAD, user.roleFileDownload) - .set(u.ROLE_PAGE_STREAMING, user.rolePageStreaming) - .set(u.SHARED_ALL_LIBRARIES, user.sharedAllLibraries) - .execute() + dsl.insertInto(u) + .set(u.ID, user.id) + .set(u.EMAIL, user.email) + .set(u.PASSWORD, user.password) + .set(u.ROLE_ADMIN, user.roleAdmin) + .set(u.ROLE_FILE_DOWNLOAD, user.roleFileDownload) + .set(u.ROLE_PAGE_STREAMING, user.rolePageStreaming) + .set(u.SHARED_ALL_LIBRARIES, user.sharedAllLibraries) + .execute() - user.sharedLibrariesIds.forEach { - insertInto(ul) - .columns(ul.USER_ID, ul.LIBRARY_ID) - .values(user.id, it) - .execute() - } - } + user.sharedLibrariesIds.forEach { + dsl.insertInto(ul) + .columns(ul.USER_ID, ul.LIBRARY_ID) + .values(user.id, it) + .execute() } } + @Transactional override fun update(user: KomgaUser) { - dsl.transaction { config -> - with(config.dsl()) { - update(u) - .set(u.EMAIL, user.email) - .set(u.PASSWORD, user.password) - .set(u.ROLE_ADMIN, user.roleAdmin) - .set(u.ROLE_FILE_DOWNLOAD, user.roleFileDownload) - .set(u.ROLE_PAGE_STREAMING, user.rolePageStreaming) - .set(u.SHARED_ALL_LIBRARIES, user.sharedAllLibraries) - .set(u.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) - .where(u.ID.eq(user.id)) - .execute() + dsl.update(u) + .set(u.EMAIL, user.email) + .set(u.PASSWORD, user.password) + .set(u.ROLE_ADMIN, user.roleAdmin) + .set(u.ROLE_FILE_DOWNLOAD, user.roleFileDownload) + .set(u.ROLE_PAGE_STREAMING, user.rolePageStreaming) + .set(u.SHARED_ALL_LIBRARIES, user.sharedAllLibraries) + .set(u.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + .where(u.ID.eq(user.id)) + .execute() - deleteFrom(ul) - .where(ul.USER_ID.eq(user.id)) - .execute() + dsl.deleteFrom(ul) + .where(ul.USER_ID.eq(user.id)) + .execute() - user.sharedLibrariesIds.forEach { - insertInto(ul) - .columns(ul.USER_ID, ul.LIBRARY_ID) - .values(user.id, it) - .execute() - } - } + user.sharedLibrariesIds.forEach { + dsl.insertInto(ul) + .columns(ul.USER_ID, ul.LIBRARY_ID) + .values(user.id, it) + .execute() } } + @Transactional override fun delete(userId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(ul).where(ul.USER_ID.equal(userId)).execute() - deleteFrom(u).where(u.ID.equal(userId)).execute() - } - } + dsl.deleteFrom(ul).where(ul.USER_ID.equal(userId)).execute() + dsl.deleteFrom(u).where(u.ID.equal(userId)).execute() } + @Transactional override fun deleteAll() { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(ul).execute() - deleteFrom(u).execute() - } - } + dsl.deleteFrom(ul).execute() + dsl.deleteFrom(u).execute() } override fun existsByEmailIgnoreCase(email: String): Boolean = diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/LibraryDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/LibraryDao.kt index ab263866b..e858ea694 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/LibraryDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/LibraryDao.kt @@ -6,6 +6,7 @@ import org.gotson.komga.jooq.Tables import org.gotson.komga.jooq.tables.records.LibraryRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.net.URL import java.time.LocalDateTime import java.time.ZoneId @@ -42,24 +43,19 @@ class LibraryDao( .fetchInto(l) .map { it.toDomain() } + @Transactional override fun delete(libraryId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(ul).where(ul.LIBRARY_ID.eq(libraryId)).execute() - deleteFrom(l).where(l.ID.eq(libraryId)).execute() - } - } + dsl.deleteFrom(ul).where(ul.LIBRARY_ID.eq(libraryId)).execute() + dsl.deleteFrom(l).where(l.ID.eq(libraryId)).execute() } + @Transactional override fun deleteAll() { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(ul).execute() - deleteFrom(l).execute() - } - } + dsl.deleteFrom(ul).execute() + dsl.deleteFrom(l).execute() } + @Transactional override fun insert(library: Library) { dsl.insertInto(l) .set(l.ID, library.id) @@ -80,6 +76,7 @@ class LibraryDao( .execute() } + @Transactional override fun update(library: Library) { dsl.update(l) .set(l.NAME, library.name) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/MediaDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/MediaDao.kt index e996a86b4..2d9ff2a8a 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/MediaDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/MediaDao.kt @@ -9,6 +9,7 @@ import org.gotson.komga.jooq.tables.records.MediaPageRecord import org.gotson.komga.jooq.tables.records.MediaRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId @@ -58,41 +59,41 @@ class MediaDao( mr.toDomain(pr.filterNot { it.bookId == null }.map { it.toDomain() }, files) }.first() + @Transactional override fun insert(media: Media) { insert(listOf(media)) } + @Transactional override fun insert(medias: Collection) { if (medias.isNotEmpty()) { - dsl.transaction { config -> - config.dsl().batch( - config.dsl().insertInto( - m, - m.BOOK_ID, - m.STATUS, - m.MEDIA_TYPE, - m.COMMENT, - m.PAGE_COUNT - ).values(null as String?, null, null, null, null) - ).also { step -> - medias.forEach { - step.bind( - it.bookId, - it.status, - it.mediaType, - it.comment, - it.pages.size - ) - } - }.execute() + dsl.batch( + dsl.insertInto( + m, + m.BOOK_ID, + m.STATUS, + m.MEDIA_TYPE, + m.COMMENT, + m.PAGE_COUNT + ).values(null as String?, null, null, null, null) + ).also { step -> + medias.forEach { + step.bind( + it.bookId, + it.status, + it.mediaType, + it.comment, + it.pages.size + ) + } + }.execute() - insertPages(config.dsl(), medias) - insertFiles(config.dsl(), medias) - } + insertPages(medias) + insertFiles(medias) } } - private fun insertPages(dsl: DSLContext, medias: Collection) { + private fun insertPages(medias: Collection) { if (medias.any { it.pages.isNotEmpty() }) { dsl.batch( dsl.insertInto( @@ -121,7 +122,7 @@ class MediaDao( } } - private fun insertFiles(dsl: DSLContext, medias: Collection) { + private fun insertFiles(medias: Collection) { if (medias.any { it.files.isNotEmpty() }) { dsl.batch( dsl.insertInto( @@ -142,52 +143,45 @@ class MediaDao( } } + @Transactional override fun update(media: Media) { - dsl.transaction { config -> - with(config.dsl()) { - update(m) - .set(m.STATUS, media.status.toString()) - .set(m.MEDIA_TYPE, media.mediaType) - .set(m.COMMENT, media.comment) - .set(m.PAGE_COUNT, media.pages.size) - .set(m.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) - .where(m.BOOK_ID.eq(media.bookId)) - .execute() + dsl.update(m) + .set(m.STATUS, media.status.toString()) + .set(m.MEDIA_TYPE, media.mediaType) + .set(m.COMMENT, media.comment) + .set(m.PAGE_COUNT, media.pages.size) + .set(m.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + .where(m.BOOK_ID.eq(media.bookId)) + .execute() - deleteFrom(p) - .where(p.BOOK_ID.eq(media.bookId)) - .execute() + dsl.deleteFrom(p) + .where(p.BOOK_ID.eq(media.bookId)) + .execute() - deleteFrom(f) - .where(f.BOOK_ID.eq(media.bookId)) - .execute() + dsl.deleteFrom(f) + .where(f.BOOK_ID.eq(media.bookId)) + .execute() - insertPages(this, listOf(media)) - insertFiles(this, listOf(media)) - } - } + insertPages(listOf(media)) + insertFiles(listOf(media)) } + @Transactional override fun delete(bookId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(p).where(p.BOOK_ID.eq(bookId)).execute() - deleteFrom(f).where(f.BOOK_ID.eq(bookId)).execute() - deleteFrom(m).where(m.BOOK_ID.eq(bookId)).execute() - } - } + dsl.deleteFrom(p).where(p.BOOK_ID.eq(bookId)).execute() + dsl.deleteFrom(f).where(f.BOOK_ID.eq(bookId)).execute() + dsl.deleteFrom(m).where(m.BOOK_ID.eq(bookId)).execute() } + @Transactional override fun deleteByBookIds(bookIds: Collection) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(p).where(p.BOOK_ID.`in`(bookIds)).execute() - deleteFrom(f).where(f.BOOK_ID.`in`(bookIds)).execute() - deleteFrom(m).where(m.BOOK_ID.`in`(bookIds)).execute() - } - } + dsl.deleteFrom(p).where(p.BOOK_ID.`in`(bookIds)).execute() + dsl.deleteFrom(f).where(f.BOOK_ID.`in`(bookIds)).execute() + dsl.deleteFrom(m).where(m.BOOK_ID.`in`(bookIds)).execute() } + override fun count(): Long = dsl.fetchCount(m).toLong() + private fun MediaRecord.toDomain(pages: List, files: List) = Media( status = Media.Status.valueOf(status), diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadListDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadListDao.kt index a94988475..792a8610c 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadListDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadListDao.kt @@ -14,6 +14,7 @@ import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId import java.util.SortedMap @@ -140,19 +141,18 @@ class ReadListDao( rr.toDomain(bookIds) } + @Transactional override fun insert(readList: ReadList) { - dsl.transaction { config -> - config.dsl().insertInto(rl) - .set(rl.ID, readList.id) - .set(rl.NAME, readList.name) - .set(rl.BOOK_COUNT, readList.bookIds.size) - .execute() + dsl.insertInto(rl) + .set(rl.ID, readList.id) + .set(rl.NAME, readList.name) + .set(rl.BOOK_COUNT, readList.bookIds.size) + .execute() - insertBooks(config.dsl(), readList) - } + insertBooks(readList) } - private fun insertBooks(dsl: DSLContext, readList: ReadList) { + private fun insertBooks(readList: ReadList) { readList.bookIds.map { (index, id) -> dsl.insertInto(rlb) .set(rlb.READLIST_ID, readList.id) @@ -162,53 +162,47 @@ class ReadListDao( } } + @Transactional override fun update(readList: ReadList) { - dsl.transaction { config -> - with(config.dsl()) { - update(rl) - .set(rl.NAME, readList.name) - .set(rl.BOOK_COUNT, readList.bookIds.size) - .set(rl.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) - .where(rl.ID.eq(readList.id)) - .execute() + dsl.update(rl) + .set(rl.NAME, readList.name) + .set(rl.BOOK_COUNT, readList.bookIds.size) + .set(rl.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + .where(rl.ID.eq(readList.id)) + .execute() - deleteFrom(rlb).where(rlb.READLIST_ID.eq(readList.id)).execute() + dsl.deleteFrom(rlb).where(rlb.READLIST_ID.eq(readList.id)).execute() - insertBooks(config.dsl(), readList) - } - } + insertBooks(readList) } + @Transactional override fun removeBookFromAll(bookId: String) { dsl.deleteFrom(rlb) .where(rlb.BOOK_ID.eq(bookId)) .execute() } + @Transactional override fun removeBooksFromAll(bookIds: Collection) { dsl.deleteFrom(rlb) .where(rlb.BOOK_ID.`in`(bookIds)) .execute() } + @Transactional override fun delete(readListId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(rlb).where(rlb.READLIST_ID.eq(readListId)).execute() - deleteFrom(rl).where(rl.ID.eq(readListId)).execute() - } - } + dsl.deleteFrom(rlb).where(rlb.READLIST_ID.eq(readListId)).execute() + dsl.deleteFrom(rl).where(rl.ID.eq(readListId)).execute() } + @Transactional override fun deleteAll() { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(rlb).execute() - deleteFrom(rl).execute() - } - } + dsl.deleteFrom(rlb).execute() + dsl.deleteFrom(rl).execute() } + @Transactional override fun deleteEmpty() { dsl.deleteFrom(rl) .where( diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadProgressDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadProgressDao.kt index 37ab92ea7..7ee5e6ead 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadProgressDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ReadProgressDao.kt @@ -8,6 +8,7 @@ import org.jooq.DSLContext import org.jooq.Query import org.jooq.impl.DSL import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId @@ -49,93 +50,85 @@ class ReadProgressDao( .fetchInto(r) .map { it.toDomain() } + @Transactional override fun save(readProgress: ReadProgress) { - dsl.transaction { config -> - config.dsl().saveQuery(readProgress).execute() - config.dsl().aggregateSeriesProgress(listOf(readProgress.bookId), readProgress.userId) - } + saveQuery(readProgress).execute() + aggregateSeriesProgress(listOf(readProgress.bookId), readProgress.userId) } + @Transactional override fun save(readProgresses: Collection) { - dsl.transaction { config -> - val queries = readProgresses.map { config.dsl().saveQuery(it) } - config.dsl().batch(queries).execute() + val queries = readProgresses.map { saveQuery(it) } + dsl.batch(queries).execute() - readProgresses.groupBy { it.userId } - .forEach { (userId, readProgresses) -> - config.dsl().aggregateSeriesProgress(readProgresses.map { it.bookId }, userId) - } - } + readProgresses.groupBy { it.userId } + .forEach { (userId, readProgresses) -> + aggregateSeriesProgress(readProgresses.map { it.bookId }, userId) + } } - private fun DSLContext.saveQuery(readProgress: ReadProgress): Query = - this.insertInto(r, r.BOOK_ID, r.USER_ID, r.PAGE, r.COMPLETED) + private fun saveQuery(readProgress: ReadProgress): Query = + dsl.insertInto(r, r.BOOK_ID, r.USER_ID, r.PAGE, r.COMPLETED) .values(readProgress.bookId, readProgress.userId, readProgress.page, readProgress.completed) .onDuplicateKeyUpdate() .set(r.PAGE, readProgress.page) .set(r.COMPLETED, readProgress.completed) .set(r.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + @Transactional override fun delete(bookId: String, userId: String) { - dsl.transaction { config -> - config.dsl().deleteFrom(r).where(r.BOOK_ID.eq(bookId).and(r.USER_ID.eq(userId))).execute() - config.dsl().aggregateSeriesProgress(listOf(bookId), userId) - } + dsl.deleteFrom(r).where(r.BOOK_ID.eq(bookId).and(r.USER_ID.eq(userId))).execute() + aggregateSeriesProgress(listOf(bookId), userId) } + @Transactional override fun deleteByUserId(userId: String) { - dsl.transaction { config -> - config.dsl().deleteFrom(r).where(r.USER_ID.eq(userId)).execute() - config.dsl().deleteFrom(rs).where(rs.USER_ID.eq(userId)).execute() - } + dsl.deleteFrom(r).where(r.USER_ID.eq(userId)).execute() + dsl.deleteFrom(rs).where(rs.USER_ID.eq(userId)).execute() } + @Transactional override fun deleteByBookId(bookId: String) { - dsl.transaction { config -> - config.dsl().deleteFrom(r).where(r.BOOK_ID.eq(bookId)).execute() - config.dsl().aggregateSeriesProgress(listOf(bookId)) - } + dsl.deleteFrom(r).where(r.BOOK_ID.eq(bookId)).execute() + aggregateSeriesProgress(listOf(bookId)) } + @Transactional override fun deleteByBookIds(bookIds: Collection) { - dsl.transaction { config -> - config.dsl().deleteFrom(r).where(r.BOOK_ID.`in`(bookIds)).execute() - config.dsl().aggregateSeriesProgress(bookIds) - } + dsl.deleteFrom(r).where(r.BOOK_ID.`in`(bookIds)).execute() + aggregateSeriesProgress(bookIds) } override fun deleteBySeriesIds(seriesIds: Collection) { dsl.deleteFrom(rs).where(rs.SERIES_ID.`in`(seriesIds)).execute() } + @Transactional override fun deleteByBookIdsAndUserId(bookIds: Collection, userId: String) { - dsl.transaction { config -> - config.dsl().deleteFrom(r).where(r.BOOK_ID.`in`(bookIds)).and(r.USER_ID.eq(userId)).execute() - config.dsl().aggregateSeriesProgress(bookIds, userId) - } + dsl.deleteFrom(r).where(r.BOOK_ID.`in`(bookIds)).and(r.USER_ID.eq(userId)).execute() + aggregateSeriesProgress(bookIds, userId) } + @Transactional override fun deleteAll() { - dsl.transaction { config -> - config.dsl().deleteFrom(r).execute() - config.dsl().deleteFrom(rs).execute() - } + dsl.deleteFrom(r).execute() + dsl.deleteFrom(rs).execute() } - private fun DSLContext.aggregateSeriesProgress(bookIds: Collection, userId: String? = null) { - val seriesIds = this.select(b.SERIES_ID) + private fun aggregateSeriesProgress(bookIds: Collection, userId: String? = null) { + val seriesIds = dsl.select(b.SERIES_ID) .from(b) .where(b.ID.`in`(bookIds)) .fetch(b.SERIES_ID) - this.deleteFrom(rs) + dsl.deleteFrom(rs) .where(rs.SERIES_ID.`in`(seriesIds)) .apply { userId?.let { and(rs.USER_ID.eq(it)) } } .execute() - this.insertInto(rs) + dsl.insertInto(rs) .select( - this.select(b.SERIES_ID, r.USER_ID) + dsl.select(b.SERIES_ID, r.USER_ID) .select(DSL.sum(DSL.`when`(r.COMPLETED.isTrue, 1).otherwise(0))) .select(DSL.sum(DSL.`when`(r.COMPLETED.isFalse, 1).otherwise(0))) .from(b) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesCollectionDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesCollectionDao.kt index 72ed24683..cf9ef8b58 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesCollectionDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesCollectionDao.kt @@ -14,6 +14,7 @@ import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId @@ -138,20 +139,19 @@ class SeriesCollectionDao( cr.toDomain(seriesIds) } + @Transactional override fun insert(collection: SeriesCollection) { - dsl.transaction { config -> - config.dsl().insertInto(c) - .set(c.ID, collection.id) - .set(c.NAME, collection.name) - .set(c.ORDERED, collection.ordered) - .set(c.SERIES_COUNT, collection.seriesIds.size) - .execute() + dsl.insertInto(c) + .set(c.ID, collection.id) + .set(c.NAME, collection.name) + .set(c.ORDERED, collection.ordered) + .set(c.SERIES_COUNT, collection.seriesIds.size) + .execute() - insertSeries(config.dsl(), collection) - } + insertSeries(collection) } - private fun insertSeries(dsl: DSLContext, collection: SeriesCollection) { + private fun insertSeries(collection: SeriesCollection) { collection.seriesIds.forEachIndexed { index, id -> dsl.insertInto(cs) .set(cs.COLLECTION_ID, collection.id) @@ -161,54 +161,48 @@ class SeriesCollectionDao( } } + @Transactional override fun update(collection: SeriesCollection) { - dsl.transaction { config -> - with(config.dsl()) { - update(c) - .set(c.NAME, collection.name) - .set(c.ORDERED, collection.ordered) - .set(c.SERIES_COUNT, collection.seriesIds.size) - .set(c.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) - .where(c.ID.eq(collection.id)) - .execute() + dsl.update(c) + .set(c.NAME, collection.name) + .set(c.ORDERED, collection.ordered) + .set(c.SERIES_COUNT, collection.seriesIds.size) + .set(c.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + .where(c.ID.eq(collection.id)) + .execute() - deleteFrom(cs).where(cs.COLLECTION_ID.eq(collection.id)).execute() + dsl.deleteFrom(cs).where(cs.COLLECTION_ID.eq(collection.id)).execute() - insertSeries(config.dsl(), collection) - } - } + insertSeries(collection) } + @Transactional override fun removeSeriesFromAll(seriesId: String) { dsl.deleteFrom(cs) .where(cs.SERIES_ID.eq(seriesId)) .execute() } + @Transactional override fun removeSeriesFromAll(seriesIds: Collection) { dsl.deleteFrom(cs) .where(cs.SERIES_ID.`in`(seriesIds)) .execute() } + @Transactional override fun delete(collectionId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(cs).where(cs.COLLECTION_ID.eq(collectionId)).execute() - deleteFrom(c).where(c.ID.eq(collectionId)).execute() - } - } + dsl.deleteFrom(cs).where(cs.COLLECTION_ID.eq(collectionId)).execute() + dsl.deleteFrom(c).where(c.ID.eq(collectionId)).execute() } + @Transactional override fun deleteAll() { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(cs).execute() - deleteFrom(c).execute() - } - } + dsl.deleteFrom(cs).execute() + dsl.deleteFrom(c).execute() } + @Transactional override fun deleteEmpty() { dsl.deleteFrom(c) .where( diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDao.kt index 99d40ef03..62a09a773 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDao.kt @@ -9,6 +9,7 @@ import org.jooq.Condition import org.jooq.DSLContext import org.jooq.impl.DSL import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.net.URL import java.time.LocalDateTime import java.time.ZoneId @@ -83,6 +84,7 @@ class SeriesDao( .map { it.toDomain() } } + @Transactional override fun insert(series: Series) { dsl.insertInto(s) .set(s.ID, series.id) @@ -93,6 +95,7 @@ class SeriesDao( .execute() } + @Transactional override fun update(series: Series) { dsl.update(s) .set(s.NAME, series.name) @@ -105,28 +108,19 @@ class SeriesDao( .execute() } + @Transactional override fun delete(seriesId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(s).where(s.ID.eq(seriesId)).execute() - } - } + dsl.deleteFrom(s).where(s.ID.eq(seriesId)).execute() } + @Transactional override fun deleteAll() { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(s).execute() - } - } + dsl.deleteFrom(s).execute() } + @Transactional override fun delete(seriesIds: Collection) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(s).where(s.ID.`in`(seriesIds)).execute() - } - } + dsl.deleteFrom(s).where(s.ID.`in`(seriesIds)).execute() } override fun count(): Long = dsl.fetchCount(s).toLong() diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt index 4d54febbe..9c1aa35a4 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt @@ -6,6 +6,7 @@ import org.gotson.komga.jooq.Tables import org.gotson.komga.jooq.tables.records.SeriesMetadataRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime import java.time.ZoneId @@ -45,74 +46,72 @@ class SeriesMetadataDao( .mapNotNull { it.tag } .toSet() + @Transactional override fun insert(metadata: SeriesMetadata) { - dsl.transaction { config -> - config.dsl().insertInto(d) - .set(d.SERIES_ID, metadata.seriesId) - .set(d.STATUS, metadata.status.toString()) - .set(d.TITLE, metadata.title) - .set(d.TITLE_SORT, metadata.titleSort) - .set(d.SUMMARY, metadata.summary) - .set(d.READING_DIRECTION, metadata.readingDirection?.toString()) - .set(d.PUBLISHER, metadata.publisher) - .set(d.AGE_RATING, metadata.ageRating) - .set(d.LANGUAGE, metadata.language) - .set(d.STATUS_LOCK, metadata.statusLock) - .set(d.TITLE_LOCK, metadata.titleLock) - .set(d.TITLE_SORT_LOCK, metadata.titleSortLock) - .set(d.SUMMARY_LOCK, metadata.summaryLock) - .set(d.READING_DIRECTION_LOCK, metadata.readingDirectionLock) - .set(d.PUBLISHER_LOCK, metadata.publisherLock) - .set(d.AGE_RATING_LOCK, metadata.ageRatingLock) - .set(d.LANGUAGE_LOCK, metadata.languageLock) - .set(d.GENRES_LOCK, metadata.genresLock) - .set(d.TAGS_LOCK, metadata.tagsLock) - .execute() + dsl.insertInto(d) + .set(d.SERIES_ID, metadata.seriesId) + .set(d.STATUS, metadata.status.toString()) + .set(d.TITLE, metadata.title) + .set(d.TITLE_SORT, metadata.titleSort) + .set(d.SUMMARY, metadata.summary) + .set(d.READING_DIRECTION, metadata.readingDirection?.toString()) + .set(d.PUBLISHER, metadata.publisher) + .set(d.AGE_RATING, metadata.ageRating) + .set(d.LANGUAGE, metadata.language) + .set(d.STATUS_LOCK, metadata.statusLock) + .set(d.TITLE_LOCK, metadata.titleLock) + .set(d.TITLE_SORT_LOCK, metadata.titleSortLock) + .set(d.SUMMARY_LOCK, metadata.summaryLock) + .set(d.READING_DIRECTION_LOCK, metadata.readingDirectionLock) + .set(d.PUBLISHER_LOCK, metadata.publisherLock) + .set(d.AGE_RATING_LOCK, metadata.ageRatingLock) + .set(d.LANGUAGE_LOCK, metadata.languageLock) + .set(d.GENRES_LOCK, metadata.genresLock) + .set(d.TAGS_LOCK, metadata.tagsLock) + .execute() - insertGenres(config.dsl(), metadata) - insertTags(config.dsl(), metadata) - } + insertGenres(metadata) + insertTags(metadata) } + @Transactional override fun update(metadata: SeriesMetadata) { - dsl.transaction { config -> - config.dsl().update(d) - .set(d.STATUS, metadata.status.toString()) - .set(d.TITLE, metadata.title) - .set(d.TITLE_SORT, metadata.titleSort) - .set(d.SUMMARY, metadata.summary) - .set(d.READING_DIRECTION, metadata.readingDirection?.toString()) - .set(d.PUBLISHER, metadata.publisher) - .set(d.AGE_RATING, metadata.ageRating) - .set(d.LANGUAGE, metadata.language) - .set(d.STATUS_LOCK, metadata.statusLock) - .set(d.TITLE_LOCK, metadata.titleLock) - .set(d.TITLE_SORT_LOCK, metadata.titleSortLock) - .set(d.SUMMARY_LOCK, metadata.summaryLock) - .set(d.READING_DIRECTION_LOCK, metadata.readingDirectionLock) - .set(d.PUBLISHER_LOCK, metadata.publisherLock) - .set(d.AGE_RATING_LOCK, metadata.ageRatingLock) - .set(d.LANGUAGE_LOCK, metadata.languageLock) - .set(d.GENRES_LOCK, metadata.genresLock) - .set(d.TAGS_LOCK, metadata.tagsLock) - .set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) - .where(d.SERIES_ID.eq(metadata.seriesId)) - .execute() + dsl.update(d) + .set(d.STATUS, metadata.status.toString()) + .set(d.TITLE, metadata.title) + .set(d.TITLE_SORT, metadata.titleSort) + .set(d.SUMMARY, metadata.summary) + .set(d.READING_DIRECTION, metadata.readingDirection?.toString()) + .set(d.PUBLISHER, metadata.publisher) + .set(d.AGE_RATING, metadata.ageRating) + .set(d.LANGUAGE, metadata.language) + .set(d.STATUS_LOCK, metadata.statusLock) + .set(d.TITLE_LOCK, metadata.titleLock) + .set(d.TITLE_SORT_LOCK, metadata.titleSortLock) + .set(d.SUMMARY_LOCK, metadata.summaryLock) + .set(d.READING_DIRECTION_LOCK, metadata.readingDirectionLock) + .set(d.PUBLISHER_LOCK, metadata.publisherLock) + .set(d.AGE_RATING_LOCK, metadata.ageRatingLock) + .set(d.LANGUAGE_LOCK, metadata.languageLock) + .set(d.GENRES_LOCK, metadata.genresLock) + .set(d.TAGS_LOCK, metadata.tagsLock) + .set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) + .where(d.SERIES_ID.eq(metadata.seriesId)) + .execute() - config.dsl().deleteFrom(g) - .where(g.SERIES_ID.eq(metadata.seriesId)) - .execute() + dsl.deleteFrom(g) + .where(g.SERIES_ID.eq(metadata.seriesId)) + .execute() - config.dsl().deleteFrom(st) - .where(st.SERIES_ID.eq(metadata.seriesId)) - .execute() + dsl.deleteFrom(st) + .where(st.SERIES_ID.eq(metadata.seriesId)) + .execute() - insertGenres(config.dsl(), metadata) - insertTags(config.dsl(), metadata) - } + insertGenres(metadata) + insertTags(metadata) } - private fun insertGenres(dsl: DSLContext, metadata: SeriesMetadata) { + private fun insertGenres(metadata: SeriesMetadata) { if (metadata.genres.isNotEmpty()) { dsl.batch( dsl.insertInto(g, g.SERIES_ID, g.GENRE) @@ -125,7 +124,7 @@ class SeriesMetadataDao( } } - private fun insertTags(dsl: DSLContext, metadata: SeriesMetadata) { + private fun insertTags(metadata: SeriesMetadata) { if (metadata.tags.isNotEmpty()) { dsl.batch( dsl.insertInto(st, st.SERIES_ID, st.TAG) @@ -138,24 +137,18 @@ class SeriesMetadataDao( } } + @Transactional override fun delete(seriesId: String) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(g).where(g.SERIES_ID.eq(seriesId)).execute() - deleteFrom(st).where(st.SERIES_ID.eq(seriesId)).execute() - deleteFrom(d).where(d.SERIES_ID.eq(seriesId)).execute() - } - } + dsl.deleteFrom(g).where(g.SERIES_ID.eq(seriesId)).execute() + dsl.deleteFrom(st).where(st.SERIES_ID.eq(seriesId)).execute() + dsl.deleteFrom(d).where(d.SERIES_ID.eq(seriesId)).execute() } + @Transactional override fun delete(seriesIds: Collection) { - dsl.transaction { config -> - with(config.dsl()) { - deleteFrom(g).where(g.SERIES_ID.`in`(seriesIds)).execute() - deleteFrom(st).where(st.SERIES_ID.`in`(seriesIds)).execute() - deleteFrom(d).where(d.SERIES_ID.`in`(seriesIds)).execute() - } - } + dsl.deleteFrom(g).where(g.SERIES_ID.`in`(seriesIds)).execute() + dsl.deleteFrom(st).where(st.SERIES_ID.`in`(seriesIds)).execute() + dsl.deleteFrom(d).where(d.SERIES_ID.`in`(seriesIds)).execute() } override fun count(): Long = dsl.fetchCount(d).toLong() diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SidecarDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SidecarDao.kt index 96339f191..643dea670 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SidecarDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SidecarDao.kt @@ -7,6 +7,7 @@ import org.gotson.komga.jooq.Tables import org.gotson.komga.jooq.tables.records.SidecarRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.net.URL @Component @@ -19,6 +20,7 @@ class SidecarDao( override fun findAll(): Collection = dsl.selectFrom(sc).fetch().map { it.toDomain() } + @Transactional override fun save(libraryId: String, sidecar: Sidecar) { dsl.insertInto(sc) .values( @@ -34,6 +36,7 @@ class SidecarDao( .execute() } + @Transactional override fun deleteByLibraryIdAndUrls(libraryId: String, urls: Collection) { dsl.deleteFrom(sc) .where(sc.LIBRARY_ID.eq(libraryId)) @@ -41,6 +44,7 @@ class SidecarDao( .execute() } + @Transactional override fun deleteByLibraryId(libraryId: String) { dsl.deleteFrom(sc) .where(sc.LIBRARY_ID.eq(libraryId)) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt index a94532efd..e31bf366b 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt @@ -6,6 +6,7 @@ import org.gotson.komga.jooq.Tables import org.gotson.komga.jooq.tables.records.ThumbnailBookRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.net.URL @Component @@ -36,6 +37,7 @@ class ThumbnailBookDao( .map { it.toDomain() } .firstOrNull() + @Transactional override fun insert(thumbnail: ThumbnailBook) { dsl.insertInto(tb) .set(tb.ID, thumbnail.id) @@ -47,6 +49,7 @@ class ThumbnailBookDao( .execute() } + @Transactional override fun update(thumbnail: ThumbnailBook) { dsl.update(tb) .set(tb.BOOK_ID, thumbnail.bookId) @@ -58,34 +61,37 @@ class ThumbnailBookDao( .execute() } + @Transactional override fun markSelected(thumbnail: ThumbnailBook) { - dsl.transaction { config -> - config.dsl().update(tb) - .set(tb.SELECTED, false) - .where(tb.BOOK_ID.eq(thumbnail.bookId)) - .and(tb.ID.ne(thumbnail.id)) - .execute() + dsl.update(tb) + .set(tb.SELECTED, false) + .where(tb.BOOK_ID.eq(thumbnail.bookId)) + .and(tb.ID.ne(thumbnail.id)) + .execute() - config.dsl().update(tb) - .set(tb.SELECTED, true) - .where(tb.BOOK_ID.eq(thumbnail.bookId)) - .and(tb.ID.eq(thumbnail.id)) - .execute() - } + dsl.update(tb) + .set(tb.SELECTED, true) + .where(tb.BOOK_ID.eq(thumbnail.bookId)) + .and(tb.ID.eq(thumbnail.id)) + .execute() } + @Transactional override fun delete(thumbnailBookId: String) { dsl.deleteFrom(tb).where(tb.ID.eq(thumbnailBookId)).execute() } + @Transactional override fun deleteByBookId(bookId: String) { dsl.deleteFrom(tb).where(tb.BOOK_ID.eq(bookId)).execute() } + @Transactional override fun deleteByBookIds(bookIds: Collection) { dsl.deleteFrom(tb).where(tb.BOOK_ID.`in`(bookIds)).execute() } + @Transactional override fun deleteByBookIdAndType(bookId: String, type: ThumbnailBook.Type) { dsl.deleteFrom(tb) .where(tb.BOOK_ID.eq(bookId)) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailSeriesDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailSeriesDao.kt index 769bd7b92..c003d9fe5 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailSeriesDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailSeriesDao.kt @@ -6,6 +6,7 @@ import org.gotson.komga.jooq.Tables import org.gotson.komga.jooq.tables.records.ThumbnailSeriesRecord import org.jooq.DSLContext import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.net.URL @Component @@ -29,6 +30,7 @@ class ThumbnailSeriesDao( .map { it.toDomain() } .firstOrNull() + @Transactional override fun insert(thumbnail: ThumbnailSeries) { dsl.insertInto(ts) .set(ts.ID, thumbnail.id) @@ -38,30 +40,32 @@ class ThumbnailSeriesDao( .execute() } + @Transactional override fun markSelected(thumbnail: ThumbnailSeries) { - dsl.transaction { config -> - config.dsl().update(ts) - .set(ts.SELECTED, false) - .where(ts.SERIES_ID.eq(thumbnail.seriesId)) - .and(ts.ID.ne(thumbnail.id)) - .execute() + dsl.update(ts) + .set(ts.SELECTED, false) + .where(ts.SERIES_ID.eq(thumbnail.seriesId)) + .and(ts.ID.ne(thumbnail.id)) + .execute() - config.dsl().update(ts) - .set(ts.SELECTED, true) - .where(ts.SERIES_ID.eq(thumbnail.seriesId)) - .and(ts.ID.eq(thumbnail.id)) - .execute() - } + dsl.update(ts) + .set(ts.SELECTED, true) + .where(ts.SERIES_ID.eq(thumbnail.seriesId)) + .and(ts.ID.eq(thumbnail.id)) + .execute() } + @Transactional override fun delete(thumbnailSeriesId: String) { dsl.deleteFrom(ts).where(ts.ID.eq(thumbnailSeriesId)).execute() } + @Transactional override fun deleteBySeriesId(seriesId: String) { dsl.deleteFrom(ts).where(ts.SERIES_ID.eq(seriesId)).execute() } + @Transactional override fun deleteBySeriesIds(seriesIds: Collection) { dsl.deleteFrom(ts).where(ts.SERIES_ID.`in`(seriesIds)).execute() } diff --git a/komga/src/test/kotlin/org/gotson/komga/domain/service/SeriesLifecycleTest.kt b/komga/src/test/kotlin/org/gotson/komga/domain/service/SeriesLifecycleTest.kt index d8313738a..1dbb302d0 100644 --- a/komga/src/test/kotlin/org/gotson/komga/domain/service/SeriesLifecycleTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/domain/service/SeriesLifecycleTest.kt @@ -1,16 +1,26 @@ package org.gotson.komga.domain.service +import com.ninjasquad.springmockk.SpykBean +import io.mockk.every import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.catchThrowable +import org.gotson.komga.domain.model.BookMetadata +import org.gotson.komga.domain.model.Media import org.gotson.komga.domain.model.makeBook import org.gotson.komga.domain.model.makeLibrary import org.gotson.komga.domain.model.makeSeries +import org.gotson.komga.domain.persistence.BookMetadataAggregationRepository +import org.gotson.komga.domain.persistence.BookMetadataRepository import org.gotson.komga.domain.persistence.BookRepository import org.gotson.komga.domain.persistence.LibraryRepository +import org.gotson.komga.domain.persistence.MediaRepository import org.gotson.komga.domain.persistence.SeriesMetadataRepository import org.gotson.komga.domain.persistence.SeriesRepository +import org.jooq.exception.DataAccessException import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired @@ -23,11 +33,22 @@ class SeriesLifecycleTest( @Autowired private val seriesLifecycle: SeriesLifecycle, @Autowired private val bookLifecycle: BookLifecycle, @Autowired private val seriesRepository: SeriesRepository, - @Autowired private val seriesMetadataRepository: SeriesMetadataRepository, @Autowired private val bookRepository: BookRepository, @Autowired private val libraryRepository: LibraryRepository ) { + @SpykBean + private lateinit var seriesMetadataRepository: SeriesMetadataRepository + + @SpykBean + private lateinit var bookMetadataAggregationRepository: BookMetadataAggregationRepository + + @SpykBean + private lateinit var mediaRepository: MediaRepository + + @SpykBean + private lateinit var bookMetadataRepository: BookMetadataRepository + private val library = makeLibrary() @BeforeAll @@ -155,4 +176,81 @@ class SeriesLifecycleTest( assertThat(titleSort).isEqualTo("Ecarlate") } } + + @Nested + inner class Transactions { + @Test + fun `given series when saving and an exception occur while saving metadata then series is not saved`() { + // given + val series = makeSeries(name = "series", libraryId = library.id) + every { seriesMetadataRepository.insert(any()) } throws DataAccessException("") + + // when + val thrown = catchThrowable { seriesLifecycle.createSeries(series) } + + // then + assertThat(thrown).isInstanceOf(RuntimeException::class.java) + assertThat(bookMetadataAggregationRepository.count()).isEqualTo(0) + assertThat(seriesMetadataRepository.count()).isEqualTo(0) + assertThat(seriesRepository.count()).isEqualTo(0) + } + + @Test + fun `given series when saving and an exception occur while saving metadata aggregation then series is not saved`() { + // given + val series = makeSeries(name = "series", libraryId = library.id) + every { bookMetadataAggregationRepository.insert(any()) } throws DataAccessException("") + + // when + val thrown = catchThrowable { seriesLifecycle.createSeries(series) } + + // then + assertThat(thrown).isInstanceOf(RuntimeException::class.java) + assertThat(bookMetadataAggregationRepository.count()).isEqualTo(0) + assertThat(seriesMetadataRepository.count()).isEqualTo(0) + assertThat(seriesRepository.count()).isEqualTo(0) + } + + @Test + fun `given series when adding books and an exception occur while saving media then books are not saved`() { + val books = listOf( + makeBook("book 1", libraryId = library.id), + ) + val createdSeries = makeSeries(name = "series", libraryId = library.id).let { + seriesLifecycle.createSeries(it) + } + + every { mediaRepository.insert(any>()) } throws DataAccessException("") + + // when + val thrown = catchThrowable { seriesLifecycle.addBooks(createdSeries, books) } + + // then + assertThat(thrown).isInstanceOf(Exception::class.java) + assertThat(mediaRepository.count()).isEqualTo(0) + assertThat(bookMetadataRepository.count()).isEqualTo(0) + assertThat(bookRepository.count()).isEqualTo(0) + } + + @Test + fun `given series when adding books and an exception occur while saving metadata then books are not saved`() { + val books = listOf( + makeBook("book 1", libraryId = library.id), + ) + val createdSeries = makeSeries(name = "series", libraryId = library.id).let { + seriesLifecycle.createSeries(it) + } + + every { bookMetadataRepository.insert(any>()) } throws DataAccessException("") + + // when + val thrown = catchThrowable { seriesLifecycle.addBooks(createdSeries, books) } + + // then + assertThat(thrown).isInstanceOf(Exception::class.java) + assertThat(mediaRepository.count()).isEqualTo(0) + assertThat(bookMetadataRepository.count()).isEqualTo(0) + assertThat(bookRepository.count()).isEqualTo(0) + } + } }