IntelliJ reports that there is more than one bean of a repository, although app starts up fine

I’ve come across an issue recently in a project where I was using Spring Data JPA. I had a repository defined like this:

@Repository
public interface QuestionCategoryRepository extends JpaRepository<QuestionCategory, Long> {

}

I also had a service class where I injected this repository:

@Inject
private QuestionCategoryRepository questionCategoryRepository;

I noticed, that IntelliJ reported the following error: “There is more than one bean of QuestionCategoryRepository type”. It seemed wierd to me, so I went ahead and started my application. The context was built successfully and there were no problems with the startup at all.

It turned out that IntelliJ really had provided me a useful hint, because I noticed that I have an

@ComponentScan(basePackages = "...")

annotation on my class that is used for persistence related configuration. The scope of this annotation was involving the package where I had my repositories defined.

This is why IntelliJ believed, that there are two definitions.

Actually, there were three.

I also noticed that I have

@EnableJpaRepositories(basePackages = "...")

on my configuration class, pointing to the packes where my repositories reside.

It turned out that I can remove both the @Repository annotation and the @ComponentScan as well, becauses the @EnableJpaRepositories was enough to detect my repositories. Of course, @ComponentScan might need to be kept if it is for scanning some other classes.

ImprovedNamingStrategy does not work with Hibernate 5

Problem description

When you are trying to use the ImprovedNamingStrategy with Hibernate 5, it seems not to work. It does not resolve to underscore separated names, and because of that you get errors like this:

javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
...
org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [QuestionCategory]
@Entity
public class QuestionCategory extends IdentifiableEntity {

    private String name;

    // ...
}

It is looking for a table named “QuestionCategory”, but according to the ImprovedNamingStrategy it should be looking for “question_category”.

Why does it no longer work?

In Hibernate 4 you could use the following property to set the naming strategy that would map to names that only use lowercase letters and underscores as word separators:

jpaProperties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");

This naming strategy is pretty commonly used, because many developers like to use these kinds of names for the tables, columns etc. of their databases.

In Hibernate 5 however, the above mentioned hibernate.ejb.naming_strategy property is no longer available, because it has been split into two new properties to allow for deeper customization of the naming strategy.

What changed in Hibernate 5

Now, instead of one, there are two properties:

hibernate.implicit_naming_strategy
hibernate.physical_naming_strategy

Hibernate looks at name resolution as a 2 stage process:

  • When an entity does not explicitly name the database table that it maps to, we need to implicitly determine that table name. Or when a particular attribute does not explicitly name the database column that it maps to, we need to implicitly determine that column name. The first property is for defining this. 
  • Many organizations define rules around the naming of database objects (tables, columns, foreign-keys, etc). The idea of a PhysicalNamingStrategy is to help implement such naming rules without having to hard-code them into the mapping via explicit names. The second property is for this.

Solution

To resolve the issue and mimic the usage of ImprovedNamingStrategy, you can leave the implicit naming strategy as the default.

However, you need to create a custom physical naming strategy, because there is none that works the way we need it.

Here is how it’ll look like:

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import java.util.Locale;

public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl {


    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return new Identifier(addUnderscores(name.getText()), name.isQuoted());
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return new Identifier(addUnderscores(name.getText()), name.isQuoted());
    }


    protected static String addUnderscores(String name) {
        final StringBuilder buf = new StringBuilder(name.replace('.', '_'));
        for (int i = 1; i < buf.length() - 1; i++) {
            if (Character.isLowerCase(buf.charAt(i - 1)) &&
                Character.isUpperCase(buf.charAt(i)) &&
                Character.isLowerCase(buf.charAt(i + 1))) {
                buf.insert(i++, '_');
            }
        }
        return buf.toString().toLowerCase(Locale.ROOT);
    }
}

You need to set this up by specifying the corresponding property:

jpaProperties.put("hibernate.physical_naming_strategy", "com.test.persistence.config.PhysicalNamingStrategyImpl");

Sources

  • https://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/domain/naming.html
  • http://stackoverflow.com/questions/32437202/improvednamingstrategy-no-longer-working-in-hibernate-5

Thymeleaf Spring configuration class not found error

When configuring Thymeleaf for a Spring project, you need the  org.thymeleaf:thymeleaf-spring4 dependency that contains required libraries for Thymeleaf support in Spring 4.

This dependency will transitively load other dependencies as well. Among others, it loads the Thymeleaf dependency itself, for which as we will see, it is important to be in the correct version.

You can check out the dependencies of your Maven project by running the mvn dependency:tree command.

[INFO] \- org.thymeleaf:thymeleaf-spring4:jar:3.0.1.RELEASE:compile
[INFO]    +- org.thymeleaf:thymeleaf:jar:3.0.1.RELEASE:compile
[INFO]    |  +- org.attoparser:attoparser:jar:2.0.0.RELEASE:compile
[INFO]    |  \- org.unbescape:unbescape:jar:1.1.3.RELEASE:compile
[INFO]    \- org.slf4j:slf4j-api:jar:1.6.6:compilecons

This list of transitive dependencies actually contains the correct ones for the currently latest Thymeleaf for Spring 4. Note the 3.0.1.RELEASE version on the second line.

Confusing Maven by bringing in another Thymeleaf (without knowing…)

It can happen that if you also use some other dependency that loads the org.thymeleaf:thymeleaf, then there is a chance, that it will load an older version and overwrite the correct one. This happened in my case as well.

I used the Spring Platform BOM to get a whole lot of compatible dependency versions already set up in my project. However, the  org.thymeleaf:thymeleaf transitive dependency version coming from it caused a problem, because it was a much older version and did not contain all the necessary classes. The resulting dependency tree looked like this:

[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:compile
[INFO] \- org.thymeleaf:thymeleaf-spring4:jar:3.0.1.RELEASE:compile
[INFO]    +- org.thymeleaf:thymeleaf:jar:2.1.5.RELEASE:compile
[INFO]    |  \- org.unbescape:unbescape:jar:1.1.0.RELEASE:compile
[INFO]    \- org.slf4j:slf4j-api:jar:1.7.21:compile

Note the old 2.1.5.RELEASE version for org.thymeleaf:thymeleaf.

This old version was missing some classes that are present in the latest, because the API have been changed in the new version. Namely AbstractConfigurableTemplateResolver and TemplateResolver. Because these were not defined, I got class not found errors when I was trying to build the project.

Solution

So, the Platform BOM is unfortunately referencing an older version of Thymeleaf, we have two ways we can go about solving this.

1. Override the version by specifying it

If you are using the Platform BOM as a parent of your POM, you can just add a property to ovverride the version number to the correct one, like this:

<thymeleaf.version>3.0.1.RELEASE</thymeleaf.version>

If you are using it as a dependency, in your dependencyManagement section like:

<dependency>
    <groupId>io.spring.platform</groupId>
    <artifactId>platform-bom</artifactId>
    <version>2.0.7.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

Then you have to add the Thymeleaf as a dependency as well to override the version:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>${thymeleaf.version}</version>
</dependency>

I think these solutions are a bit fragile so I would recommend the next option I show you.

2. Switch to a lighter weight option

If you look at the contents and parents of the Platform BOM, you can see that it brings in a whole lot of dependencies, and some of them with outdated versions. For example Spring is used with a version that is a couple of releases older than the current one.

As you can see this can cause problems.

I think the best way to solve this, is to remove the Spring Platfom BOM and instead use the lighter weight Spring Framework BOM. It only contains dependency versions for the Spring Framework itself, and it references the latest from each module.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-framework-bom</artifactId>
    <version>${spring.framework.bom.version}</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

If you check it this way, the transitive dependency that caused the problem is not loaded.

Deeper look into the cause

If we take a look at where did this old dependency came from, it might be hard to find.

There is no sign of it in the Spring Platform BOM, however is you go up to it’s parent (org.springframework.boot:spring-boot-starter-parent), and then to it’s parent (org.springframework.boot:spring-boot-dependencies), there you can find this old Thymeleaf version.

You can check it out here if you would like.

So the takeaway from this, is if you are searching for a transitive dependency, always make sure to also check in the parent and it’s parent and so on.

Best way to validate decimal / double values with Spring

Spring’s validation support is widely used to validate objects. For example, it can validate models that are coming from user submitted forms. Validation is happening based on annotations that you can put on the fields in your model. These annotations are not part of Spring, external libraries must be used. The most common is the javax.validation package with the hibernate validator implementation.

The values to be validated could be decimal values. These can be represented by a number of different types. Probably the two most common types that pop into your mind are float and double. These could work okay, but because they are not exact numbers, just approximations, depending on the validation implementation, there could be some false results.

The best idea for validation is to use the BigDecimal type with the @DecimalMin and @DecimalMax validation annotations.

@DecimalMin(value = "0.1", inclusive = true)
@DecimalMax(value = "9.9", inclusive = true)
private BigDecimal measurement;

It might seem like a hassle to deal with values of this type, but actually it’s pretty straightforward. You just have to use method calls instead of numeric operators to add / subtract / etc. values.

 

Setting up a basic Spring Boot web application with Maven

In this tutorial I am going to show you, how to set up a basic web application using Spring Boot and Maven. The web application will have a single “home” page, rendered using a spring controller and a jsp file.

spring-by-pivotal

Technologies used in this tutorial:

  • Apache Tomcat 8.0.35
  • Apache Maven 3.3.9
  • Java 1.8.0_60

The structure of the project

Here you can see the file structure of the completed project:

structure-jtuts-2016-05-20-boot

Setting up the POM file

A basic Spring Boot web application requires very little configuration in the POM. We only have to include one dependency (apart from the servlet api needed for using jsp views). This is the spring-boot-starter-web, that transitively contains all other dependencies that are required for the spring controllers, logging etc.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jtuts</groupId>
    <artifactId>jtuts-2016-05-20-setting-up-basic-spring-boot-web-app-with-maven</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <name>Setting up a basic Spring Boot web application with Maven</name>

    <properties>

        <!-- Dependency versions -->

        <spring.boot.version>1.3.5.RELEASE</spring.boot.version>
        <servlet.api.version>3.1.0</servlet.api.version>

        <!-- Plugin versions -->

        <maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>

        <!-- Misc versions -->

        <java.version>1.8</java.version>

    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <!-- Provided dependencies -->
        
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.api.version}</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>



    <build>
        <finalName>boot-web-app</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

In the plugin configuration there is nothing Boot specific. We just set the Java version using the maven-compiler-plugin and also set the failOnMissingWebXml to false, because we deliberately don’t have a web.xml file.

The application runner class

A spring boot application can be initialized by a very simple class containing a main method.

package com.jtuts.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.jtuts")
public class Application extends SpringBootServletInitializer {

    public static void main(final String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Note, that we have the @SpringBootApplication annotation, which is actually is a shorthand for the following ones:

  • @Configuration marks the class as a source of bean definitions for the application context.
  • @EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
  • @EnableWebMvc is automatically added when Boot sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.
  • @ComponentScan tells Spring to look for other components, configurations.

The  @Component annotation is automatically added, but by default it only looks for components in the current package, so we needed to override it, to look for components in the parent package. This way, it’ll see our controller also.

Extending the SpringBootServletInitializer is required to be able to deploy the application as a war file to a container (e.g. Tomcat). It is actually an implementation of the WebApplicationInitializer interface, that you would use when you want to create a web application without Boot.

That’s it for the application startup.

Creating the controller

Our controller is plain and simple, just returns a view without passing any parameters.

package com.jtuts.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HomeController {

    private static final String HOME = "home";

    @RequestMapping(value = HOME)
    public ModelAndView home() {
        return new ModelAndView(HOME);
    }
}

For this controller to work, we also have to specify a view resolver in a configuration class, that tells Spring where to find the view files based on their names.

package com.jtuts.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
public class ViewConfiguration {

    private static final String VIEW_PATH = "/WEB-INF/views/";
    private static final String JSP_EXTENSION = ".jsp";

    @Bean
    public ViewResolver setupViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();

        resolver.setPrefix(VIEW_PATH);
        resolver.setSuffix(JSP_EXTENSION);

        return resolver;
    }

}

Our view file

The view file is just a really simple one:

<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Home page</title>
</head>
<body>
    <h1>Welcome to the home page.</h1>
</body>
</html>

How to launch the app

To launch this application, you just have to copy the war file created during build to a Tomcat container.

Additional notes

As you can see, a logback.xml is also included. This is because Spring Boot by default expects the logback configuration and the app would fail to start without that. The exact logging configuration is not really relevant to this tutorial, but anyway, here is the content of that file:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <logger name="org.springframework.web" level="DEBUG"/>
</configuration>

Download

You can download the working example project here.