Using now MapStruct for mapping JSON entities
This commit is contained in:
parent
82485a29b1
commit
474d4d1f91
|
@ -24,6 +24,9 @@ ext {
|
||||||
textInfoExtractorVersion = "1.0.1"
|
textInfoExtractorVersion = "1.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
mapStructVersion = "1.4.0.Beta3"
|
||||||
|
|
||||||
|
|
||||||
hbci4jVersion = '3.1.37'
|
hbci4jVersion = '3.1.37'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
|
|
||||||
sourceCompatibility = "1.7"
|
sourceCompatibility = "1.7"
|
||||||
|
@ -16,6 +17,10 @@ compileTestKotlin {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':BankingUiCommon')
|
implementation project(':BankingUiCommon')
|
||||||
|
|
||||||
|
implementation("org.mapstruct:mapstruct:$mapStructVersion")
|
||||||
|
|
||||||
|
kapt("org.mapstruct:mapstruct-processor:$mapStructVersion")
|
||||||
|
|
||||||
|
|
||||||
testImplementation "junit:junit:$junitVersion"
|
testImplementation "junit:junit:$junitVersion"
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package net.dankito.banking.persistence
|
package net.dankito.banking.persistence
|
||||||
|
|
||||||
import net.dankito.banking.persistence.mapper.EntitiesMapper
|
import net.dankito.banking.persistence.mapper.CustomerConverter
|
||||||
import net.dankito.banking.persistence.model.CustomerEntity
|
import net.dankito.banking.persistence.model.CustomerEntity
|
||||||
import net.dankito.utils.multiplatform.File
|
import net.dankito.utils.multiplatform.File
|
||||||
import net.dankito.banking.ui.model.Customer
|
import net.dankito.banking.ui.model.Customer
|
||||||
import net.dankito.banking.ui.model.AccountTransaction
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
import net.dankito.banking.ui.model.BankAccount
|
import net.dankito.banking.ui.model.BankAccount
|
||||||
import net.dankito.banking.util.ISerializer
|
import net.dankito.banking.util.ISerializer
|
||||||
import net.dankito.utils.multiplatform.Month
|
import org.mapstruct.factory.Mappers
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ open class BankingPersistenceJson(
|
||||||
protected val serializer: ISerializer
|
protected val serializer: ISerializer
|
||||||
) : IBankingPersistence {
|
) : IBankingPersistence {
|
||||||
|
|
||||||
protected val mapper = EntitiesMapper()
|
protected val mapper = Mappers.getMapper(CustomerConverter::class.java)
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
package net.dankito.banking.persistence.mapper
|
||||||
|
|
||||||
|
import net.dankito.banking.persistence.model.AccountTransactionEntity
|
||||||
|
import net.dankito.banking.persistence.model.BankAccountEntity
|
||||||
|
import net.dankito.banking.persistence.model.CustomerEntity
|
||||||
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
|
import net.dankito.banking.ui.model.BankAccount
|
||||||
|
import net.dankito.banking.ui.model.Customer
|
||||||
|
import org.mapstruct.*
|
||||||
|
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
abstract class CustomerConverter {
|
||||||
|
|
||||||
|
// Context is needed to fix cycle dependencies issue
|
||||||
|
|
||||||
|
protected val bankAccountCustomerField = BankAccount::class.java.getDeclaredField(BankAccount::customer.name)
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
bankAccountCustomerField.isAccessible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Mapping(source = "technicalId", target = "id")
|
||||||
|
abstract fun mapToEntity(customer: Customer, @Context context: CycleAvoidingMappingContext): CustomerEntity
|
||||||
|
|
||||||
|
@InheritInverseConfiguration
|
||||||
|
abstract fun mapCustomer(customer: CustomerEntity, @Context context: CycleAvoidingMappingContext): Customer
|
||||||
|
|
||||||
|
abstract fun mapCustomers(customers: List<Customer>, @Context context: CycleAvoidingMappingContext): List<CustomerEntity>
|
||||||
|
|
||||||
|
open fun mapCustomers(customers: List<Customer>): List<CustomerEntity> {
|
||||||
|
// create a new context instance each time as otherwise just cached instance would be taken und BankAccounts and AccountTransactions would never get updated
|
||||||
|
return mapCustomers(customers, CycleAvoidingMappingContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun mapCustomerEntities(customers: List<CustomerEntity>, @Context context: CycleAvoidingMappingContext): List<Customer>
|
||||||
|
|
||||||
|
open fun mapCustomerEntities(customers: List<CustomerEntity>): List<Customer> {
|
||||||
|
// create a new context instance each time as otherwise just cached instance would be taken und BankAccounts and AccountTransactions would never get updated
|
||||||
|
return mapCustomerEntities(customers, CycleAvoidingMappingContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Mapping(source = "technicalId", target = "id")
|
||||||
|
abstract fun mapBankAccount(account: BankAccount, @Context context: CycleAvoidingMappingContext): BankAccountEntity
|
||||||
|
|
||||||
|
@InheritInverseConfiguration
|
||||||
|
abstract fun mapBankAccount(account: BankAccountEntity, @Context context: CycleAvoidingMappingContext): BankAccount
|
||||||
|
|
||||||
|
abstract fun mapBankAccounts(accounts: List<BankAccount>, @Context context: CycleAvoidingMappingContext): List<BankAccountEntity>
|
||||||
|
|
||||||
|
abstract fun mapBankAccountEntities(accounts: List<BankAccountEntity>, @Context context: CycleAvoidingMappingContext): List<BankAccount>
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
open fun mapBankAccountCustomer(serializedAccount: BankAccountEntity, @MappingTarget account: BankAccount, @Context context: CycleAvoidingMappingContext) {
|
||||||
|
val mappedCustomer = mapCustomer(serializedAccount.customer, context)
|
||||||
|
|
||||||
|
bankAccountCustomerField.set(account, mappedCustomer)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Mapping(source = "technicalId", target = "id")
|
||||||
|
abstract fun mapTransaction(transaction: AccountTransaction, @Context context: CycleAvoidingMappingContext): AccountTransactionEntity
|
||||||
|
|
||||||
|
@InheritInverseConfiguration
|
||||||
|
fun mapTransaction(transaction: AccountTransactionEntity, @Context context: CycleAvoidingMappingContext): AccountTransaction {
|
||||||
|
val account = mapBankAccount(transaction.bankAccount, context)
|
||||||
|
|
||||||
|
val mappedTransaction = AccountTransaction(account, transaction.amount, transaction.currency, transaction.unparsedUsage, transaction.bookingDate,
|
||||||
|
transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText,
|
||||||
|
transaction.valueDate, transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance,
|
||||||
|
transaction.endToEndReference, transaction.customerReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode,
|
||||||
|
transaction.compensationAmount, transaction.originalAmount, transaction.sepaUsage, transaction.deviantOriginator, transaction.deviantRecipient,
|
||||||
|
transaction.usageWithNoSpecialType, transaction.primaNotaNumber, transaction.textKeySupplement, transaction.currencyType, transaction.bookingKey,
|
||||||
|
transaction.referenceForTheAccountOwner, transaction.referenceOfTheAccountServicingInstitution, transaction.supplementaryDetails,
|
||||||
|
transaction.transactionReferenceNumber, transaction.relatedReferenceNumber)
|
||||||
|
|
||||||
|
mappedTransaction.technicalId = transaction.id
|
||||||
|
|
||||||
|
return mappedTransaction
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun mapTransactions(transactions: List<AccountTransaction>, @Context context: CycleAvoidingMappingContext): List<AccountTransactionEntity>
|
||||||
|
|
||||||
|
abstract fun mapTransactionEntities(transactions: List<AccountTransactionEntity>, @Context context: CycleAvoidingMappingContext): List<AccountTransaction>
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package net.dankito.banking.persistence.mapper
|
||||||
|
|
||||||
|
import org.mapstruct.BeforeMapping
|
||||||
|
import org.mapstruct.MappingTarget
|
||||||
|
import org.mapstruct.TargetType
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
open class CycleAvoidingMappingContext {
|
||||||
|
|
||||||
|
private val knownInstances: MutableMap<Any, Any> = IdentityHashMap()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an instance out of this context if it is already mapped.
|
||||||
|
*/
|
||||||
|
@BeforeMapping
|
||||||
|
open fun <T> getMappedInstance(source: Any, @TargetType targetType: Class<T>): T {
|
||||||
|
return targetType.cast(knownInstances[source])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts an instance into the cache, so that it can be remembered to avoid endless mapping.
|
||||||
|
*/
|
||||||
|
@BeforeMapping
|
||||||
|
open fun storeMappedInstance(source: Any, @MappingTarget target: Any) {
|
||||||
|
knownInstances[source] = target
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -107,7 +107,7 @@ class BankingPersistenceJsonTest {
|
||||||
createCustomer(2),
|
createCustomer(2),
|
||||||
createCustomer(3)
|
createCustomer(3)
|
||||||
)
|
)
|
||||||
val serializableCustomers = Mappers.getMapper(CustomerConverter::class.java).mapToEntities(customers, CycleAvoidingMappingContext())
|
val serializableCustomers = Mappers.getMapper(CustomerConverter::class.java).mapCustomers(customers, CycleAvoidingMappingContext())
|
||||||
|
|
||||||
serializer.serializeObject(serializableCustomers, file)
|
serializer.serializeObject(serializableCustomers, file)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue