2

I'm just getting started with Spring Boot + Kotlin and I was trying out the PagingAndSortingRepository interface for JPA so I wrote the following interface:

interface CustomerRepository : PagingAndSortingRepository<Customer, Long>

The model for Customer is below:

@Entity
data class Customer(
    @Id @GeneratedValue var id: Long,
    var name: String
)

Now I'm trying to hook it up with a CustomerService which looks like this:

@Service
class CustomerService(
    private val customerRepository: CustomerRepository
) {
    fun getAllCustomers(): Collection<Customer> = customerRepository.findAll().toList()
    fun addCustomer(customer: Customer) = customerRepository.save(customer)
    fun deleteCustomer(customer: Customer) = customerRepository.delete(customer)
    fun updateCustomer(customer: Customer) = customerRepository.save(customer)
}

And the Application looks like this:

@SpringBootApplication
@Configuration
@EnableAutoConfiguration
@EnableJpaRepositories
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

I've added the required dependencies I believe, which are shown below:

plugins {
    id("org.springframework.boot") version "2.5.0-SNAPSHOT"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
    kotlin("jvm") version "1.4.30"
    kotlin("plugin.spring") version "1.4.30"
}

implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.apache.derby:derby:10.15.2.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

Spring Boot is not able to find a bean which sort of makes sense as I haven't defined one. However reading the documentation, it looks like one should be generated by Spring Boot here: Spring Boot Data Repositories

Application.properties is

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

The error message I get is:

Description:
Parameter 0 of constructor in com.ubiquifydigital.crm.service.CustomerService required a bean named 'entityManagerFactory' that could not be found.

Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.

I saw a few different posts regarding this and have tried adding the Configuration, AutoConfiguration and EnableJpaRepositories annotations however that has only changed the error to entityManagerFactory not found instead of the CustomerRepository not found.

asm
  • 837
  • 1
  • 16
  • 41
  • Can you please follow https://stackoverflow.com/questions/44411950/configuring-embedded-derby-in-spring-boot-app – silentsudo Mar 11 '21 at 04:06
  • The question you linked talks about setting up derby. I'm not having trouble with that. I've removed the part I talked about derby, which was a side note, not the issue I'm facing – asm Mar 11 '21 at 04:07
  • please show application.properties – silentsudo Mar 11 '21 at 04:08
  • The question I posted contains both my application.properties and the dependencies I have in my gradle.kts. I'm not using maven – asm Mar 11 '21 at 04:09
  • I do not see application.properties;s file content – silentsudo Mar 11 '21 at 04:10
  • Sorry, i accidentally deleted it as part of a previous edit. I've added it again, good catch! – asm Mar 11 '21 at 04:10
  • Please tell `org.springframework.boot` version – silentsudo Mar 11 '21 at 04:17
  • "2.5.0-SNAPSHOT" – asm Mar 11 '21 at 04:19
  • 1
    Why do you disable `DataSourceAutoConfiguration` autoconfiguration? – Hopey One Mar 11 '21 at 04:49
  • When I remove that line I get the following: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class – asm Mar 11 '21 at 04:53
  • Everything is working for me properly in new project, you need to tell project structure now if configured – silentsudo Mar 11 '21 at 05:04
  • Reason: Failed to determine a suitable driver class- for this you need to add runtime apache derby dependency thats how it will know what you are using – silentsudo Mar 11 '21 at 05:04
  • Is this not it? implementation("org.apache.derby:derby:10.15.2.0") – asm Mar 11 '21 at 05:09
  • Please read https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.html and followed by https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/EnableAutoConfiguration.html, you want to use derby, so provide application with necessary details and it will construct the beans which you can use in application, i have provided asample solution as an answer please take a look – silentsudo Mar 11 '21 at 05:15

1 Answers1

1

When using default in-memory db you must define

spring.jpa.hibernate.ddl-auto=update

in application.properties as informed here. You are also missing @Autowired annotation. entityManagerFactory is missing because the default auo configuration is turned off, in that case application is expecting you to do all the necessary configuration which again you are not doing. So keep the default configuration on and change what you need.

This code is assumed in a single file.

If you are having multiple packages then you may need to add as mentioned in this link Working code:

package com.example.demo

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import lombok.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
import org.springframework.web.bind.annotation.*
import java.util.*
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.Table

@SpringBootApplication
open class SpringBootDerbyAppApplication

fun main(args: Array<String>) {
    runApplication<SpringBootDerbyAppApplication>(*args)
}


@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "applog")
internal class AppLog {
    @Id
    @GeneratedValue
    private val id: Long = 0

    @JsonProperty
    private val name: String? = null
}

@Configuration
open class ObjectMapperConfiguration {
    @Bean
    @Primary
    open fun objectMapper() = ObjectMapper().apply {
        registerModule(KotlinModule())
    }
}

@RestController
@RequestMapping(path = ["/logs"])
internal class LogController @Autowired constructor(private val appLogRepository: AppLogRepository) {
    @GetMapping(path = ["/"])
    fun logs(): MutableIterable<AppLog> {
        return appLogRepository.findAll()
    }

    @PostMapping(path = ["/"])
    fun add(@RequestBody appLog: AppLog): AppLog {
        appLogRepository.save(appLog)
        return appLog
    }

}

@Repository
internal interface AppLogRepository : CrudRepository<AppLog, Long>

gradle file

plugins {
    id 'org.springframework.boot' version '2.4.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    id 'org.jetbrains.kotlin.jvm' version '1.5.0-M1'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
    maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'org.apache.derby:derby'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

}

test {
    useJUnitPlatform()
}
compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

silentsudo
  • 6,730
  • 6
  • 39
  • 81
  • Thanks, I'll try this and let you know. @Autowired is not required for Kotlin with the new Spring boot changes. I have been able to create mock classes which work perfectly fine without the autowired annotation. – asm Mar 11 '21 at 05:18
  • Looks like just using the runtime derby and updating the application config fixed it. Thank you! – asm Mar 11 '21 at 05:28