Intellij - Increasing Productivity Using Templates

One of the most underrated features of IntelliJ as well as all other editors based on said IDE (e.g. CLion, PyCharm, WebStorm, PhpStorm, etc.) is the ability to create custom templates. I use Spring Framework for several projects, so I create repositories, services, and controllers pretty often. Because they have a very similar structure, they are a perfect example of what to make a template for.

IntelliJ’s templates uses Apache Velocity, which is, as you’ve probably guessed, a template engine. It’s actually very powerful, and you can do much more than what this article shows, however, I don’t think that knowing every feature that comes with Apache Velocity is useful for productivity. If you end up spending more time creating templates than actually programming, doesn’t that defeat the purpose of using templates to increase your productivity?

Creating a template

Right-click on a project folder > New > Edit File Templates…

Creating a template using IntelliJ

A window should appear, and by clicking on the + button, you should be able to create a template:

Creating a template using IntelliJ

If we create a template with the following content:

Hello ${recipient_name}, 

I am currently not at work, but I will get back to you in ${how_many_days} days.

Best wishes,
${your_name}

And then create a new file with our template as type of file:

Creating a template using IntelliJ

The following dialog should appear:

Creating a template using IntelliJ

By using Letter, Ms. Doe, 5 and John Doe respectively, you would get a file called Letter.txt with the following content:

Hello Ms. Doe, 

I am currently not at work, but I will get back to you in 5 days.

Best wishes,
John Doe

Scope of this article

Though I’m going to focus on templates for Spring, you can use them for any project type, with any framework and any programming language.

For instance, a React component:

import React from 'react';

export default class ${NAME} extends React.Component {

	constructor(props) {
		super(props);
		this.state = {};
	}
    
	render() {
		return (
			<div>
				#[[$END$]]#
			</div>
		);
	}
}

Obviously, this isn’t all Velocity can do. There are many other instructions, such as #if, #foreach, etc. but we’ll focus on the most basic here.

NOTE: #[[$END$]]# is where the cursor will automatically be moved to when the template is created.

Model / Entity (Hibernate-ready)

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import javax.persistence.*;


@Entity
public class ${Resource_name} {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;


	public ${Resource_name}() {}


	public Long getId() {
		return id;
	}
	
	
	public void setId(Long id) {
		this.id = id;
	}
	
}

With the resource name Example, the template above would result in the following:

package org.twinnation.site.domain;

import javax.persistence.*;

@Entity
public class Example {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	public Example() {}
	
	public Long getId() {
		return id;
	}
	
	public void setId(Long id) {
		this.id = id;
	}
}

Jpa Repository

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;

@Repository
public interface ${Resource_name}Repository extends JpaRepository<${Resource_name}, Long> {

}

With the resource name Example, the template above would result in the following:

package org.twinnation.site.repository;

import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;

@Repository
public interface ExampleRepository extends JpaRepository<Example, Long> {

}

Service

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))

@Service
public class ${Resource_name}Service {

	@Autowired
	private ${Resource_name}Repository ${resourceFormatted}Repository;
}

With the resource name Example, the template above would result in the following:

package org.twinnation.site.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ExampleService {
	
	@Autowired
	private ExampleRepository exampleRepository;
}

Rest Controller

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))
#set($resourceLowerCase = $Resource_name.toLowerCase())

@RestController
@RequestMapping(value = "/api/v1/${resourceLowerCase}s", produces = "application/json")
public class ${Resource_name}Controller {

	@Autowired
	private ${Resource_name}Service ${resourceFormatted}Service;
}

With the resource name Example, the template above would result in the following:

package org.twinnation.site.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/api/v1/examples", produces = "application/json")
public class ExampleController {

	@Autowired
	private ExampleService exampleService;
}

That being said

Those are just basic templates, you can bring it much further if you want. Here’s an example with a service and a controller:

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))
#set($repo = $resourceFormatted + "Repository")

@Service
public class ${Resource_name}Service {

	@Autowired
	private ${Resource_name}Repository ${repo};
	
	public ${Resource_name} get${Resource_name}ById(Long id) {
	    ${Resource_name} $resourceFormatted = ${repo}.findById(id).orElse(null);
	    Assert.notNull($resourceFormatted, "Invalid ${Resource_name} id");
	    return $resourceFormatted;
	}
	
	public ${Resource_name} create${Resource_name}(${Resource_name} $resourceFormatted) {
	    Assert.notNull($resourceFormatted, "Cannot create null ${Resource_name}");
	    return ${repo}.save($resourceFormatted);
	}
	
	public Message delete${Resource_name}ById(Long id) {
	    ${Resource_name} $resourceFormatted = get${Resource_name}ById(id);
	    ${repo}.delete($resourceFormatted);
	    return new Message(false, "Deleted ${Resource_name} with id #" + id);
	}
}
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))
#set($resourcePath = $Resource_name.toLowerCase() + "s")

#set($service = $resourceFormatted + "Service")

@RestController
@RequestMapping(value = "/api/v1", produces = "application/json")
public class ${Resource_name}Controller {

	@Autowired
	private ${Resource_name}Service ${service};
	
	@GetMapping("/${resourcePath}")
	public List<${Resource_name}> getAll${Resource_name}s() {
	    return ${service}.getAll${Resource_name}s();
	}
	
	@GetMapping("/${resourcePath}/{id}")
	public ${Resource_name} get${Resource_name}ById(@PathVariable Long id) {
	    return ${service}.get${Resource_name}ById(id);
	}
	
	@DeleteMapping("/${resourcePath}/{id}")
	public Message delete${Resource_name}ById(@PathVariable Long id) {
	    return ${service}.delete${Resource_name}ById(id);
	}
}

With this in mind, you could make a template for unit tests based on both of those templates and whatnot.

Conclusion

IntelliJ IDEA is a very powerful IDE not only because of its near perfect autocompletion, highly customizable interface and many plugins,but also because of its many embedded tools - templates included. Unfortunately, templates are very underrated despite their ability to save developers a lot of valuable time wasted on writing code they already know.