How to initialize variables in parent abstract class of spring bean using Kotlin? - spring

I have the next structure of spring beans
abstract class GenericRepository<T> {
private val FIND_BY_ID_SQL = "SELECT * FROM ${this.getTableName()} WHERE id = ?"
abstract fun getTableName(): String
abstract fun jdbcTemplate(): JdbcTemplate
abstract fun getMapper(): RowMapper<T>
fun find(id: Long): T? {
return jdbcTemplate().queryForObject(FIND_BY_ID_SQL, arrayOf(id), getMapper())
}
}
User repository
#Repository
class UserRepository(
#Autowired
private val jdbcTemplate: JdbcTemplate
) : GenericRepository<User>() {
companion object {
private const val INSERT_SQL = "INSERT INTO \"user\"(name, age) VALUES (?,?)"
}
private class LogMapper : RowMapper<User> {
override fun mapRow(rs: ResultSet, rowNum: Int): User? {
return User(
id = rs.getLong("id"),
name = rs.getString("name"),
age = rs.getInt("operation")
)
}
}
override fun getTableName(): String {
return "user"
}
override fun jdbcTemplate(): JdbcTemplate {
return jdbcTemplate
}
override fun getMapper(): RowMapper<User> {
return LogMapper()
}
}
The problem when Spring creates proxy and creates bean of UserRepository it doesn't initialize FIND_BY_ID_SQL leaving it null.
The question: how usign abstract class make spring initialize FIND_BY_ID_SQL variable?
UPD
I used #Component instead of #Repository and the problem was solved. FIND_BY_ID_SQL is not null anymore.

You could work around the problem by making it lazy:
private val FIND_BY_ID_SQL by lazy { "SELECT * FROM ${this.getTableName()} WHERE id = ?" }
However, you should first be sure it's an actual problem (e.g. that when you call find you get an exception), because the proxy might simply delegate to a "real" UserRepository with non-null FIND_BY_ID_SQL (and jdbcTemplate etc.), depending on Spring's internal details.
In addition, you need to be careful when your superclass properties are initialized depending on subclass; I think your exact situation should work, but I'd prefer to write it as
abstract class GenericRepository<T>(val tableName: String) {
private val FIND_BY_ID_SQL = "SELECT * FROM ${tableName} WHERE id = ?"
abstract val jdbcTemplate: JdbcTemplate
abstract val mapper: RowMapper<T>
fun find(id: Long): T? {
return jdbcTemplate.queryForObject(FIND_BY_ID_SQL, arrayOf(id), mapper)
}
}
#Repository
class UserRepository(
#Autowired
override val jdbcTemplate: JdbcTemplate
) : GenericRepository<User>("user") { ... }

Related

How to initialize repository from service in Springboot Kotlin

#Service
public class AvailablePolicyService {
#Autowired
private var availablePolicyRepository : AvailablePolicyRepository = **AvailablePolicyRepository()**
fun saveAvailablePolicy(availablePolicy: AvailablePolicy): AvailablePolicy { return availablePolicyRepository.save(availablePolicy) }
fun getAllAvailablePolicy(): List<AvailablePolicy>{ return availablePolicyRepository.findAll() }
fun getAvailablePolicyByPolicyId(policyId: String?): AvailablePolicy? {
var availablePolicies: List<AvailablePolicy> = getAllAvailablePolicy()
for (availablePolicy in availablePolicies) {
if (availablePolicy.getPolicyId().equals(policyId)) {
return availablePolicy
}
}
return null
}
fun getAvailablePolicyByPolicyCategory(policyCategory: String?): ArrayList<AvailablePolicy> {
var availablePolicies: List<AvailablePolicy> = getAllAvailablePolicy()
var availablePolicyCategory = ArrayList<AvailablePolicy>()
for (availablePolicy in availablePolicies) {
if (availablePolicy.getPolicyCategory().equals(policyCategory)) {
availablePolicyCategory.add(availablePolicy)
}
}
return availablePolicyCategory
}
}
#Repository
interface AvailablePolicyRepository : MongoRepository<AvailablePolicy, String>
The bolded text shows where the error is showing and it reads "Interface AvailablePolicyRepository does not have constructors". How do I initialize repository from service?
How do I initialize repository from service?
That's the thing. You don't! Spring does it for you:
#Autowired
private lateinit var availablePolicyRepository: AvailablePolicyRepository
Field injection is rather obsolete and you should consider using contructor injection instead.
#Service
class AvailablePolicyService(private val availablePolicyRepository: AvailablePolicyRepository) {...}

Kotlin: How to solve the type casting problem?

I am trying to save data to a database. To do this, I choose a service using enum.
interface MyService<K: Serializable, V: Serializable> {
fun gerResource(): JpaRepository<K, V>
fun convert(list: List<String>) : List<K>
fun saveData(data: List<K>) = gerResource().saveAll(data) // signature saveAll: <S extends T> List<S> saveAll(Iterable<S> var1)
}
class DbData : Serializable
class KafkaData : Serializable
#Service
class FirstService( val repository : JpaRepository<DbData, Long>) : MyService<DbData, Long> {
override fun gerResource() = repository
override fun convert(list: List<String>) : List<DbData>{
return listOf(DbData())
}
}
#Service
class SecondService(val repository : JpaRepository<KafkaData, Long>) : MyService<KafkaData, Long> {
override fun gerResource() = repository
override fun convert(list: List<String>) : List<KafkaData>{
return listOf(KafkaData())
}
}
enum class ServiceType {
FIRST,
SECOND
}
#Service
class Cast(
private val firstService: MyService<DbData, Long>,
private val secondService: MyService<KafkaData, Long>
){
private fun saveMessage(type: ServiceType, list: List<String>) {
val myService = when (type) {
ServiceType.FIRST -> firstService
ServiceType.SECOND -> secondService
}
val convertList = myService.convert(list)
myService.saveData(convertList)
}
}
But I have a problem with cast in the code
val convertList = myService.convert(list)
myService.saveData(convertList)
Kotlin thinks that convert returns List<Serialazable> and I get an error in saveData:
Type mismatch.
Required: List<Nothing>
Found: List<Serializable>
if I change fun saveData(data: List<K>) to fun saveData(data: List<Serializable>), I get an error in saveAll(data)
Type mismatch.
Required:K!
Found: Serializable!
Since saveAll is a Spring Framework method, I can't change it.
How can I fix the error?

Any benefit of adding a read Query in a service class in Spring

There is a code where there are single read query in a service class , does this have any effect in terms of performance/internal way hibernate runs the query ? As it's easier to just write the query in a facade/handler layer than a call the service class in read only ops
Run with hibernate here and PersonRepo is the envers class for the Person class annotated with #Entity
For eg)
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
#Service
class PersonService(
private val personRepo: PersonRepo
) {
fun getPersonById(personId: Long): PersonEntity {
return this.personRepo.findById(personId)?: throw ValidationException("person $personId does not exist")
}
}
when writing this query in handler/non service layer when should we
call it from a service class
#Component
class PersonHandler (
private val personRepo: PersonRepo,
private val personService: personService,
private val someOtherHandler: SomeOtherHandler
) {
fun displayPersonAge(personId: Long) {
val person = this.personService.getPersonById(person)
someOtherHandler(person)
}
}
get the object directly
#Component
class PersonHandler (
private val personRepo: PersonRepo,
private val personService: personService,
private val someOtherHandler: SomeOtherHandler
) {
fun displayPersonAge(personId: Long) {
val person = this.personRepo.findById(personId)?: throw ValidationException("person $personId does not exist")
someOtherHandler(person)
}
}
since both are one line here in kotlin , does adding it to a serviceClass have any benefit to it ? if it is run in a transaction/not in run in a transaction are there any effects on other db transactions and query performance under load

lateinit property S has not been initialized

I want inject a singleton in another class by kotlin in spring boot.
S.kt
#Singleton
#Component
class S(
private val userService: UserService,
val companyRepo: CompanyRepo
)
WorkingGroup.kt
class WorkingGroup(
override val name: String = "",
override val desc: String = ""
) : Csv() {
fun isCompatible(ct2: WorkingGroup): Boolean = this == ct2
companion object : ICsvEnumCompanion<WorkingGroup> {
#Inject
private lateinit var s: S
override val VALUES: List<WorkingGroup>
by lazy {
val details = s.user().company.details ?: CompanyDetails()
details.workingGroups.map { WorkingGroup(it.name, it.desc) }
}
}
}
By this code, I get below error:
Caused by: org.zalando.problem.DefaultProblem: Internal Server Error: lateinit property s has not been initialized
I search for this error and found some result like this, but the problem not solved.
How can I inject service in companion object in kotlin?
In order for Spring to inject into a companion object you will need to create a setter for the field outside of the companion object. WorkingGroup will need to be a Spring managed bean in order for Spring to autowire it (inject dependencies).
#Component
class WorkingGroup(
override val name: String = "",
override val desc: String = ""
) : Csv() {
fun isCompatible(ct2: WorkingGroup): Boolean = this == ct2
companion object : ICsvEnumCompanion<WorkingGroup> {
private lateinit var s: S
override val VALUES: List<WorkingGroup>
by lazy {
val details = s.user().company.details ?: CompanyDetails()
details.workingGroups.map { WorkingGroup(it.name, it.desc) }
}
}
#Autowired
fun setS(value: S) {
s = value;
}
}

Neo4J repository not wired everywhere

I've defined a Neo4j repository as follow (code is in Kotlin but it's very close to Java) :
#Repository
interface UserRepository : GraphRepository<User> {
fun findByEmail(email: String): User?
#Query("match (n:User)-[:IS_AUTH]->(:Permission {name: {0}}) where id(n) = {1} return n")
fun authorizedUser(permission: String, userId: Long): User?
}
In a controller, I've written:
#Controller
#RequestMapping("/company")
open class CreateCompanyController {
#Autowired private lateinit var userRepo: UserRepository
#RequestMapping(method = arrayOf(RequestMethod.POST))
#ResponseBody
fun createCompany(#RequestParam(value = "name", required = true) name: String,
#RequestParam(value = "siret", required = true) siret: Long) : ResponseEntity<String> {
val connectedUser = SecurityContextHolder.getContext().authentication.principal as User
val testUser = userRepo.findByEmail("test#company.com")
if (!PermissionManager().hasAuthorizationFor(PermissionManager.ADMIN_ALL, connectedUser))
return ResponseEntity("You're not authorized to create a company", HttpStatus.FORBIDDEN)
return ResponseEntity(HttpStatus.OK)
}
}
In the code above, the userRepo property is set well (aka. its value is not null).
I want to have this UserRepository in a custom service. So, I've coded:
#Service
open class PermissionManager {
companion object {
val ADMIN_ALL = "ADMIN_ALL"
}
#Autowired private lateinit var userRepo: UserRepository
fun hasAuthorizationFor(permissionName: String, user: User): Boolean {
return userRepo.authorizedUser(permissionName, user.id!!) != null
}
}
But in this case, the userRepo property is not initialized.
I've written the PersistenceContext class as:
#Configuration
#ComponentScan("persistence")
#EnableNeo4jRepositories("persistence.repositories")
#EnableTransactionManagement
open class PersistenceContext : Neo4jConfiguration() {
#Bean override fun getSessionFactory(): SessionFactory {
return SessionFactory("persistence.domain")
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
open override fun getSession(): Session {
return super.getSession()
}
}
What I don't understand is why is the userRepo set in my controller but not in my service ?

Resources