How we use Kotlin with Exposed at TouK

Why Kotlin? At TouK, we try to early adopt technologies. We don’t have a starter project skeleton that is reused in every new project, we want to try something that fits the project needs, even if it’s not that popular yet. We tried Kotlin first it mid 2016, right after reaching 1.0.2 version

Why Kotlin?

At TouK, we try to early adopt technologies. We don’t have a starter project skeleton reused in every new project; we want to try something that fits the project’s needs, even if it’s not that popular yet. We tried Kotlin first in mid-2016, right after reaching the 1.0.2 version. It was getting really popular in Android development, but almost nobody used it on the backend, especially — with production deployment. After reading some “hello world” examples, including this great article by Sebastien Deleuze we decided to try Kotlin as main language for a new MVNO project. The project was mainly a backend for a mobile app, with some integrations with external services (chat, sms, payments) and background tasks regarding customer subscriptions. We felt that Kotlin would be something fresh and more pleasant for developers, but we also liked the “not reinventing the wheel” approach — reusing large parts of the Java/JVM ecosystem we were happy with for existing projects (Spring, Gradle, JUnit, Mockito).

Why Exposed?

We initially felt that Kotlin + JPA/Hibernate is not a perfect match. Kotlin’s functional nature with first-class immutability support was not something that could seemly integrate with full-blown ORM started in the pre-Java8 era. But Sebastien’s article led us to try Exposed — a SQL access library maintained by JetBrains. From the beginning, we really liked the main assumptions of Exposed:

  • not trying to be full ORM framework
  • two flavors — typesafe SQL DSL and DAO/ActiveRecord style
  • lightweight, no reflection
  • no code generation
  • Spring integration
  • no annotations on your domain classes (in SQL DSL flavor)
  • open for extension (e.g. PostGIS and new DB dialects)

TL;DR

If you want to see how we use Kotlin + Exposed duo in our projects, check out this Github repo. It’s a Spring Boot app exposing REST API with the implementation of Medium clone as specified in http://realworld.io (“The mother of all demo apps”).

Another nice example is this repo by Seb Schmidt.

SQL DSL

In our projects we decided to try the “typesafe SQL DSL” flavor of Exposed. In this approach you don’t have to add anything to your domain classes, just need to write a simple schema mapping using Kotlin in configuration-as-code manner:

data class User(
  val username: Username,
  val password: String,
  val email: String
)

object UserTable : Table("users") {
    val username = text("username")
    val email = text("email")
    val password = text("password")
}

And then you can write type/null-safe queries with direct mapping to your domain classes:

UserTable.select { UserTable.username eq username }?.toUser()

// or 

UserTable.select { UserTable.username like username }.map { it.toUser() }

fun ResultRow.toUser() = User(
       username = this[UserTable.username],
       email = this[UserTable.email],
       password = this[UserTable.password]
)

RefIds

We like type-safe RefIds in our domain code. This is particularly useful in DDD-ish architectures, where you can keep those RefIds in a shared domain and use them to communicate between contexts.

So we wrap plan ids (longs, strings) into simple wrapper classes (e.g. UserId, ArticleId, Username, Slug). Exposed allows to easily register your own column types or even generic WrapperColumnType implementation that you can find in our repo.

Using this technique you can rewrite this mapping to something like this:

sealed class UserId : RefId<Long>() {
  object New : UserId() {
    override val value: Long by IdNotPersistedDelegate<Long>()
  }
  data class Persisted(override val value: Long) : UserId() {
    override fun toString() = "UserId(value=$value)"
  }
}

data class User(
   val id: UserId = UserId.New,
   //...
)

fun Table.userId(name: String) = longWrapper<UserId>(name, UserId::Persisted, UserId::value)

object UserTable : Table("users") {
  val id = userId("id").primaryKey().autoIncrement()
//...
}

And now we can query by type-safe RefIds:

override fun findBy(userId: UserId) =
  UserTable.select { UserTable.id eq userId }?.toUser()

Relationship mapping

One of the most significant selling points of ORMs is how easy it is to deal with relations. You just annotate the related field/collection with OneToOne or OneToMany and then can fetch the whole graph of objects at once. In theory — quite a nice idea, but in practice, things often go wrong. I’m not going to dig into details, instead, I recommend you read e.g. these fragments of “Opinionated JPA with Querydsl” book:

In Exposed SQL DSL approach you have to do relationship mapping by yourself — if you need to. Let’s consider Article and Tag case from our project’s domain. We have a many-to-many relation here, so we need additional “article_tags” table:

object ArticleTagTable : Table("article_tags") {
   val tagId = tagId("tag_id").references(TagTable.id)
   val articleId = articleId("article_id").references(ArticleTable.id)
}

When creating an Article, we have to attach all the associated Tags by populating Article’s generated id into ArticleTabTable entries:

override fun create(article: Article): Article {
   val savedArticle = ArticleTable.insert { it.from(article) }
           .getOrThrow(ArticleTable.id)
           .let { article.copy(id = it) }
   savedArticle.tags.forEach { tag ->
       ArticleTagTable.insert {
           it[ArticleTagTable.tagId] = tag.id
           it[ArticleTagTable.articleId] = savedArticle.id
       }
   }
   return savedArticle
}

The funny part is the mapping of Article with Tags in query methods — in API specification Tags are always returned with the Article — so we need to eagerly fetch tags by using leftJoin:

val ArticleWithTags = (ArticleTable leftJoin ArticleTagTable leftJoin TagTable)

override fun findBy(articleId: ArticleId) =
  ArticleWithTags
    .select { ArticleTable.id eq articleId }
    .toArticles()
    .singleOrNull()

After joining, we have then one ResultRow per one Article-Tag pair, so we have to group them by ArticleId, and build the correct Article object by adding Tags for each matching resultRow:

fun Iterable<ResultRow>.toArticles(): List<ResultRow> {
   return fold(mutableMapOf<ArticleId, Article>()) { map, resultRow ->
       val article = resultRow.toArticle()
       val tagId = resultRow.tryGet(ArticleTagTable.tagId)
       val tag = tagId?.let { resultRow.toTag() }
       val current = map.getOrDefault(article.id, article)
       map[article.id] = current.copy(tags = current.tags + listOfNotNull(tag))
       map
   }.values.toList()
}

This implementation allows us to solve all the possible cases:

  • no articles (fold just returns empty map)
  • articles with no tags (tag is null, so listOfNotNull(tag) is empty)
  • articles with many tags (an article with a single tag is inserted into the map, then other tags are added in copy method)

However, consider when you need to fetch the dependent structure with the root object? For tags it makes sense since you always want the tags with the article, and the count of tags for any article should not be that huge. What about the comments? You definitely don’t want all the comments each time you fetch the article, instead you’ll need some kind of paging or even making a parent-child hierarchy for comments for the article. That’s why we recommend having this relationship mapped indirectly — every Comment should have ArticleId property, and the CommentRepository could have methods like:

fun findAllBy(articleId: ArticleId): List<Comment>
// or
fun findAllByPaged(articleId: ArticleId, pageRequest: PageRequest): Page<Comment>

Extendibility

Exposed is by design open for extension, making it even easier with Kotlin’s support for extension methods. You can define your own column type or expressions, e.g. for PostGIS point type support as Sebastian showed in his article. We used similar PostGIS extension in our project too. We were also able to implement a simple support for Java8 DateTime column type — for now, Exposed has Joda-time support, a generic approach for various date/time libraries is planned in the roadmap.

The bigger thing was Oracle DB dialect — we were forced to migrate to Oracle at some time in our project. We submitted a pull-request with foundations of Oracle 12 support, being tested in production for a while (then, we moved back to PostgreSQL…). The implementation was rather straightforward, with DataType- and FunctionProvider interfaces to provide and just a few tweaks in batch insert support.

Final thoughts

Our developer experience with Kotlin+Exposed duo was really pleasant. If you don’t plan to map many relations directly, just use simple data classes, connected by RefIds, it works really well. The Exposed library itself may need more exhaustive documentation and removing some annoying details (e.g. transaction management via thread-local — which is already on the roadmap), but we definitely recommend you give it a try in your project!

You May Also Like

Atom Feeds with Spring MVC

How to add feeds (Atom) to your web application with just two classes?
How about Spring MVC?

Here are my assumptions:
  • you are using Spring framework
  • you have some entity, say “News”, that you want to publish in your feeds
  • your "News" entity has creationDate, title, and shortDescription
  • you have some repository/dao, say "NewsRepository", that will return the news from your database
  • you want to write as little as possible
  • you don't want to format Atom (xml) by hand
You actually do NOT need to use Spring MVC in your application already. If you do, skip to step 3.


Step 1: add Spring MVC dependency to your application
With maven that will be:
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>3.1.0.RELEASE</version>
</dependency>

Step 2: add Spring MVC DispatcherServlet
With web.xml that would be:
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/feed</url-pattern>
</servlet-mapping>
Notice, I set the url-pattern to “/feed” which means I don't want Spring MVC to handle any other urls in my app (I'm using a different web framework for the rest of the app). I also give it a brand new contextConfigLocation, where only the mvc configuration is kept.

Remember that, when you add a DispatcherServlet to an app that already has Spring (from ContextLoaderListener for example), your context is inherited from the global one, so you should not create beans that exist there again, or include xml that defines them. Watch out for Spring context getting up twice, and refer to spring or servlet documentation to understand what's happaning.

Step 3. add ROME – a library to handle Atom format
With maven that is:
<dependency>
    <groupId>net.java.dev.rome</groupId>
    <artifactId>rome</artifactId>
    <version>1.0.0</version>
</dependency>

Step 4. write your very simple controller
@Controller
public class FeedController {
    static final String LAST_UPDATE_VIEW_KEY = "lastUpdate";
    static final String NEWS_VIEW_KEY = "news";
    private NewsRepository newsRepository;
    private String viewName;

    protected FeedController() {} //required by cglib

    public FeedController(NewsRepository newsRepository, String viewName) {
        notNull(newsRepository); hasText(viewName);
        this.newsRepository = newsRepository;
        this.viewName = viewName;
    }

    @RequestMapping(value = "/feed", method = RequestMethod.GET)        
    @Transactional
    public ModelAndView feed() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName(viewName);
        List<News> news = newsRepository.fetchPublished();
        modelAndView.addObject(NEWS_VIEW_KEY, news);
        modelAndView.addObject(LAST_UPDATE_VIEW_KEY, getCreationDateOfTheLast(news));
        return modelAndView;
    }

    private Date getCreationDateOfTheLast(List<News> news) {
        if(news.size() > 0) {
            return news.get(0).getCreationDate();
        }
        return new Date(0);
    }
}
And here's a test for it, in case you want to copy&paste (who doesn't?):
@RunWith(MockitoJUnitRunner.class)
public class FeedControllerShould {
    @Mock private NewsRepository newsRepository;
    private Date FORMER_ENTRY_CREATION_DATE = new Date(1);
    private Date LATTER_ENTRY_CREATION_DATE = new Date(2);
    private ArrayList<News> newsList;
    private FeedController feedController;

    @Before
    public void prepareNewsList() {
        News news1 = new News().title("title1").creationDate(FORMER_ENTRY_CREATION_DATE);
        News news2 = new News().title("title2").creationDate(LATTER_ENTRY_CREATION_DATE);
        newsList = newArrayList(news2, news1);
    }

    @Before
    public void prepareFeedController() {
        feedController = new FeedController(newsRepository, "viewName");
    }

    @Test
    public void returnViewWithNews() {
        //given
        given(newsRepository.fetchPublished()).willReturn(newsList);
        
        //when
        ModelAndView modelAndView = feedController.feed();
        
        //then
        assertThat(modelAndView.getModel())
                .includes(entry(FeedController.NEWS_VIEW_KEY, newsList));
    }

    @Test
    public void returnViewWithLastUpdateTime() {
        //given
        given(newsRepository.fetchPublished()).willReturn(newsList);

        //when
        ModelAndView modelAndView = feedController.feed();

        //then
        assertThat(modelAndView.getModel())
                .includes(entry(FeedController.LAST_UPDATE_VIEW_KEY, LATTER_ENTRY_CREATION_DATE));
    }

    @Test
    public void returnTheBeginningOfTimeAsLastUpdateInViewWhenListIsEmpty() {
        //given
        given(newsRepository.fetchPublished()).willReturn(new ArrayList<News>());

        //when
        ModelAndView modelAndView = feedController.feed();

        //then
        assertThat(modelAndView.getModel())
                .includes(entry(FeedController.LAST_UPDATE_VIEW_KEY, new Date(0)));
    }
}
Notice: here, I'm using fest-assert and mockito. The dependencies are:
<dependency>
 <groupId>org.easytesting</groupId>
 <artifactId>fest-assert</artifactId>
 <version>1.4</version>
 <scope>test</scope>
</dependency>
<dependency>
 <groupId>org.mockito</groupId>
 <artifactId>mockito-all</artifactId>
 <version>1.8.5</version>
 <scope>test</scope>
</dependency>

Step 5. write your very simple view
Here's where all the magic formatting happens. Be sure to take a look at all the methods of Entry class, as there is quite a lot you may want to use/fill.
import org.springframework.web.servlet.view.feed.AbstractAtomFeedView;
[...]

public class AtomFeedView extends AbstractAtomFeedView {
    private String feedId = "tag:yourFantastiSiteName";
    private String title = "yourFantastiSiteName: news";
    private String newsAbsoluteUrl = "http://yourfanstasticsiteUrl.com/news/"; 

    @Override
    protected void buildFeedMetadata(Map<String, Object> model, Feed feed, HttpServletRequest request) {
        feed.setId(feedId);
        feed.setTitle(title);
        setUpdatedIfNeeded(model, feed);
    }

    private void setUpdatedIfNeeded(Map<String, Object> model, Feed feed) {
        @SuppressWarnings("unchecked")
        Date lastUpdate = (Date)model.get(FeedController.LAST_UPDATE_VIEW_KEY);
        if (feed.getUpdated() == null || lastUpdate != null || lastUpdate.compareTo(feed.getUpdated()) > 0) {
            feed.setUpdated(lastUpdate);
        }
    }

    @Override
    protected List<Entry> buildFeedEntries(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        @SuppressWarnings("unchecked")
        List<News> newsList = (List<News>)model.get(FeedController.NEWS_VIEW_KEY);
        List<Entry> entries = new ArrayList<Entry>();
        for (News news : newsList) {
            addEntry(entries, news);
        }
        return entries;
    }

    private void addEntry(List<Entry> entries, News news) {
        Entry entry = new Entry();
        entry.setId(feedId + ", " + news.getId());
        entry.setTitle(news.getTitle());
        entry.setUpdated(news.getCreationDate());
        entry = setSummary(news, entry);
        entry = setLink(news, entry);
        entries.add(entry);
    }

    private Entry setSummary(News news, Entry entry) {
        Content summary = new Content();
        summary.setValue(news.getShortDescription());
        entry.setSummary(summary);
        return entry;
    }

    private Entry setLink(News news, Entry entry) {
        Link link = new Link();
        link.setType("text/html");
        link.setHref(newsAbsoluteUrl + news.getId()); //because I have a different controller to show news at http://yourfanstasticsiteUrl.com/news/ID
        entry.setAlternateLinks(newArrayList(link));
        return entry;
    }

}

Step 6. add your classes to your Spring context
I'm using xml approach. because I'm old and I love xml. No, seriously, I use xml because I may want to declare FeedController a few times with different views (RSS 1.0, RSS 2.0, etc.).

So this is the forementioned spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="atom" value="application/atom+xml"/>
                <entry key="html" value="text/html"/>
            </map>
        </property>
        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
            </list>
        </property>
    </bean>

    <bean class="eu.margiel.pages.confitura.feed.FeedController">
        <constructor-arg index="0" ref="newsRepository"/>
        <constructor-arg index="1" value="atomFeedView"/>
    </bean>

    <bean id="atomFeedView" class="eu.margiel.pages.confitura.feed.AtomFeedView"/>
</beans>

And you are done.

I've been asked a few times before to put all the working code in some public repo, so this time it's the other way around. I've describe things that I had already published, and you can grab the commit from the bitbucket.

Hope that helps.