Как использовать аннотацию JDBI @Transaction в неинтерфейсном и неабстрактном классе?

Итак, я родом из Spring Boot, и я был действительно впечатлен тем, как аннотация Spring @Transactional просто работала без проблем вместе с Hibernate. Сейчас я работаю над приложением Dropwizard, которое использует Jdbi3. И я нашел похожую аннотацию @Transaction, которая работает точно так же, как Spring, но с некоторыми предварительными условиями.

Весна

Таким образом, в соответствии с рекомендациями Spring, Repository и Controller — это два интерфейса, которые взаимодействуют с базой данных и HTTP-запросами соответственно, Service Layer — это место, к которому принадлежит вся бизнес-логика. Всегда был случай, когда один метод в службе выполнял операции CRUD с использованием нескольких репозиториев. Таким образом, имеет смысл сделать сервисный метод аннотированным с помощью @Transational.

Jdbi с Dropwizard

Так что поправьте меня, если я ошибаюсь. Здесь в Jdbi Repository становится Dao, Controller становится Resource, а Service остается Service. Возможно, разные люди используют разную архитектуру слоев, но давайте просто предположим, что именно в этом и заключается моя проблема.

Постановка задачи

Я хочу достичь той же обработки транзакций в Jdbi, что и в Spring, идеологически, потому что это имеет для меня гораздо больше смысла без добавления какого-либо дополнительного уровня. Вот я накину код, чего я хочу добиться:

Dao1.kt

 interface Dao1{
    @SqlUpdate("INSERT INTO table1...")
    fun insert() : Int
 }

Dao2.kt

 interface Dao2{
    @SqlUpdate("INSERT INTO table2...")
    fun insert() : Int
 }

Service.kt

class Service{
  @Transaction
  fun save() {
    Dao1 =Dao1() //What should be expected way to instantiate
    Dao2 =Dao2() //What should be expected way to instantiate
    dao1.insert()
    dao2.insert()
  }
}

Несколько замечаний

  • Я знаю, что onDemand можно использовать только в классе или интерфейсе abstract, поэтому я не могу создать экземпляр Service с помощью onDemand. Также я не могу сделать реферат Service.

  • В нескольких статьях предлагается сделать аннотацию Repository и использовать там Transaction. Но, по моему мнению, когда я думаю о репозитории, я вижу, что он имеет однозначное сопоставление с entity/table. Или, может быть, связанные сущности. Поэтому, если я хочу обновить таблицы movie и user в одном и том же сервисном методе, помещать эти два оператора транзакции в метод в каком-то XRepository звучит очень абсурдно для меня. Это часть бизнес-логики и должна находиться в Service.

  • Я думаю, что могу использовать jdbi.inTransaction и jdbi.useTransaction. Но в этом случае мне приходится прикреплять каждый Dao вручную. Есть ли лучший способ сделать это?

Спасибо


person Prince Bansal    schedule 30.04.2020    source источник


Ответы (1)


Вы можете использовать метод Jdbi @Transaction для оформления в своих классах bizlogic. Он будет запускать базовые запросы к базе данных в транзакции.

Предполагая, что Repository может состоять из нескольких Dao's, и предполагая такой поток управления, вот как можно структурировать вашу бизнес-логику (классы бизнес-логики) и репозитории.

Resource -> BizLogic -> Repository -> Dao 

Например (Kotlin + Dropwizard + Jdbi):

Resource

    @GET
    @Path("{Id}")
    @Produces(MediaType.APPLICATION_JSON)
    fun getAccount(
            @PathParam("Id") Id: String,
            @Suspended asyncResponse: AsyncResponse
    ) = asyncResponse.with {
        accountManager.getAccount(Id)
    }

метод в BizLogic

    @Transaction
    suspend fun getAccount(Id: String): List<Account> = 
       accountRepository.getAccountsById(Id) ?: emptyList()

метод в Repository

    suspend fun getAccountsById(Id: String): List<Account>? = withContext(Dispatchers.IO){
        accountDao.lookupById(Id)
    }

Dao

        @SqlQuery("select user_id, name, email from users where user_id = :id")
        fun lookupById(@Bind("id") Id: String): List<Account>?

Ps: вам нужно будет обработать привязку репозитория/дао к экземпляру jdbi с помощью guice или spring-di

person Abhisek Padhi    schedule 06.05.2020
comment
Я вижу, что вы используете @Transaction в слое BizLogic (Сервис). Это то, что я ищу. Можете ли вы уточнить приведенный выше ответ, используя два репозитория, выполняя транзакции в двух разных таблицах. И предположим, что вторая транзакция зависит от некоторой бизнес-логики, связанной с результатом первой транзакции. - person Prince Bansal; 07.05.2020
comment
Вы можете размещать вызовы репозитория в методе сервисного уровня (с аннотацией @Transaction) последовательно, и они будут выполняться по порядку, поэтому вы можете дождаться результата 1-го вызова репозитория, чтобы использовать его во 2-м и так далее. С сопрограммами kotlin, даже если ваши методы репозитория (как в фрагментах, которыми я поделился) отправляются в разные пулы потоков, их сайт вызова все равно останется последовательным. - person Abhisek Padhi; 08.05.2020