fix: use spring transactions instead of jooq transactions

This commit is contained in:
Gauthier Roebroeck 2021-06-24 17:33:48 +08:00
parent 75ecbe15ba
commit 73931f0bf8
24 changed files with 592 additions and 528 deletions

View file

@ -16,4 +16,6 @@ interface BookMetadataRepository {
fun delete(bookId: String)
fun delete(bookIds: Collection<String>)
fun count(): Long
}

View file

@ -15,4 +15,6 @@ interface MediaRepository {
fun delete(bookId: String)
fun deleteByBookIds(bookIds: Collection<String>)
fun count(): Long
}

View file

@ -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<String> =
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" }

View file

@ -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)

View file

@ -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)

View file

@ -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 {

View file

@ -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" }

View file

@ -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<BookMetadataPatchCapability>) {
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" }

View file

@ -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<Book>) {
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<Series>) {
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)

View file

@ -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<Book>) {
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<Book>) {
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<String>) {
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()

View file

@ -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<String>) {
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()

View file

@ -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<BookMetadata>) {
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<BookMetadata>) {
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<BookMetadata>) {
private fun insertAuthors(metadatas: Collection<BookMetadata>) {
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<BookMetadata>) {
private fun insertTags(metadatas: Collection<BookMetadata>) {
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<String>) {
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<Author>, tags: Set<String>) =
BookMetadata(
title = title,

View file

@ -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 =

View file

@ -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)

View file

@ -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<Media>) {
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<Media>) {
private fun insertPages(medias: Collection<Media>) {
if (medias.any { it.pages.isNotEmpty() }) {
dsl.batch(
dsl.insertInto(
@ -121,7 +122,7 @@ class MediaDao(
}
}
private fun insertFiles(dsl: DSLContext, medias: Collection<Media>) {
private fun insertFiles(medias: Collection<Media>) {
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<String>) {
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<BookPage>, files: List<String>) =
Media(
status = Media.Status.valueOf(status),

View file

@ -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<String>) {
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(

View file

@ -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<ReadProgress>) {
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<String>) {
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<String>) {
dsl.deleteFrom(rs).where(rs.SERIES_ID.`in`(seriesIds)).execute()
}
@Transactional
override fun deleteByBookIdsAndUserId(bookIds: Collection<String>, 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<String>, userId: String? = null) {
val seriesIds = this.select(b.SERIES_ID)
private fun aggregateSeriesProgress(bookIds: Collection<String>, 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)

View file

@ -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<String>) {
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(

View file

@ -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<String>) {
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()

View file

@ -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<String>) {
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()

View file

@ -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<SidecarStored> =
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<URL>) {
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))

View file

@ -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<String>) {
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))

View file

@ -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<String>) {
dsl.deleteFrom(ts).where(ts.SERIES_ID.`in`(seriesIds)).execute()
}

View file

@ -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<Collection<Media>>()) } 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<Collection<BookMetadata>>()) } 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)
}
}
}