Spring Boot JPA Kotlin

Spring Boot provides a ready made solution to working with Java Persistence API (JPA). The post discusses how to make a basic web application that reads and writes employees to a database. We can also count how many employees have a certain name. This will all be done using Spring’s JPA features and the Kotlin programming language.

Spring Boot provides us with a data source on its own with very little configuration. Of course, we are free to connect the application to remote databases as well. To get started, we need to fill out some properties in the application.properties file found in src/main/resources

application.properties

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
spring.datasource.driver-class-name=org.hsqldb.jdbcDriver

The first property, spring.jpa.hibernate.ddl-auto=create-drop tells the application to scan for all classes annotated with @Entity and create database tables for us. The persistence provider does the work of generating database definition language (DDL) and creating our database schema for us.

The second property configures Hibernate to act as our persistence provider. This is required because JPA is a specification. It requires a 3rd party library (Hibernate, EclipseLink, etc) to actually implement the specification. Finally, we need to tell the application what JDBC driver to use. This should match our database. In this case, we are using HSQLDB so we load their JDBC driver.

Database Entity

We need to create one or more persistant objects that are mapped to the database. In this case, we only have 1, an Employee Class, that we are using to map to a database table.

//This class maps to a table in the database
//that will get created for us
@Entity
data class Employee(
       @field: Id @field: GeneratedValue var Id : Long = 0, //Primary Key
       var name : String = "", //Column
       var position : String = "") //Column

Kotlin provides data classes for these sort of situations. The first property is annotated with Id and serves as the Primary Key. The GeneratedValue annotation tells the persistence provider to generate primary key values for us. The other two properties, name and position, end up becoming columns in the database table. When persistence provider scans this class, it will issue the correct commands to the database and generate an employee table with an primary key columna and two VARCHAR columns. Each instance of the Employee class that we store will become a record in the table.

Automatic Repositories

Spring is capable of generating @Repository classes for use when working with JPA. These repositories come fully loaded with 18 methods that handle all of our CRUD (create, read, update, and delete) methods and provide container managed transactions. We can even create our own custom queries using a naming convention and Spring will infer what needs to be done.

However, before we can have Spring generate our repositories for us, we need to tell it to do so. That’s pretty easy because all we need is a small configuration class.

Config

@Configuration
//The next line tells Spring Generate our JPA Repositories
@EnableJpaRepositories(basePackages = arrayOf("com.stonesoupprogramming.jpa"))
class Config

The package passed to the @EnableJpaReposities tells Spring where to look for repository interfaces. In order to make a Repository for the Employee class, we only need to declare an interface that extends JpaRepository.

EmployeeRepository

//The Implementation for this class is generated
//by Spring Data!
interface EmployeeRepository : JpaRepository <Employee, Long>{

    //Define a custom query using Spring Data
    fun countByNameContainingIgnoringCase(name : String) : Long
}

At no point will we ever write an implementation for this interface. When Spring sees this interface, it will generate an implementation class that is fully loaded and ready for our application to use. Technically, this interface could be empty, but we do have one method countByNameContainingIgnoringCase(String). Let’s discuss it.

Spring JPA Repositories are capable of defining queries on our persiteted objects provided that we follow the proper naming convention. Let’s take apart countByNameContainingIgnoringCase and discuss what each part means.

  • count — We are defining a count query
  • ByName — The syntax here is By[Property]. Our Employee class has a Name property, so we write ByName. If we wanted to use Position instead, it would be ByPosition
  • ContainingIgnoringCase — This is the predicate of the query. We are looking for anything containing a string value (in this case) and we are ignoring the case.

So in the end countByNameContainingIgnoringCase defines a query that means what it says. We are going to get a count of all records where the name contains a certain name and the name is not case sensitive. Spring is able to parse this name and create the correct query for us.

Put it in Action!

I wrote an MVC application that demonstrates how to use these concepts in a web application. Here is the code for the controller.

@Controller
@RequestMapping("/")
class IndexController(@Autowired private val employeeRepository: EmployeeRepository) {

    @RequestMapping(method = arrayOf(RequestMethod.GET))
    fun doGet(model : Model) : String {
        model.apply {
            addAttribute("employee", Employee())
            addAttribute("showName", false)
            addAttribute("employees", employeeRepository.findAll().toList())
        }
        return "index"
    }

    @RequestMapping("/employee_save", method = arrayOf(RequestMethod.POST))
    fun doEmployeeSave(employee: Employee,
                       model : Model) : String {
        employeeRepository.save(employee)
        model.apply {
            addAttribute("employee", Employee())
            addAttribute("showName", false)
            addAttribute("employees", employeeRepository.findAll().toList())
        }
        return "index"
    }

    @RequestMapping("/employee_count", method = arrayOf(RequestMethod.POST))
    fun doEmployeeCount(@RequestParam("name") name : String,
                        model : Model) : String {
        val count = employeeRepository.countByNameContainingIgnoringCase(name)
        model.apply {
            addAttribute("employee", Employee())
            addAttribute("showName", true)
            addAttribute("count", "Number of employees having name $name: $count")
            addAttribute("employees", employeeRepository.findAll().toList())
        }
        return "index"
    }
}

Even though we never wrote an implementation for EmployeeRepository, we can safely inject an instance of EmployeeRepository into our controller class. From this point, we have an HTTP GET method and two POST methods. The doEmployeeSave calls employeeRepository.save() and saves the incoming Employee object to the database. It also calls employeeRepository.findAll() and sends all employee records back to the view.

The doEmployeeCount calls our custom employeeRepository.countByNameContainingIgnoringCase method and returns a count of how many employee records contain the given name. We can pass this number back to the view. Once again, we are using employeeRepository.findAll().

This is the HTML code that works with the IndexController class.
indexhtml1indexcontroller2

Conclusion

The JPA cababilities provided by Spring Boot make developing ORM applications a breeze and it’s worth while to leverage them. For one thing, we only have to write a fraction of the code that we might have to write otherwise, but we are also less likely to introduce bugs into the application because we can trust the implementation of the JPA Repository classes and the persistence provided SQL generating capabilities.

Here are some screen shots of the finished application.


You can download the code at my GitHub page here or visit the YouTube tutorial.

Advertisements

1 thought on “Spring Boot JPA Kotlin”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s