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.

Keep form field values and errors after redirection in Spring MVC controller

Ways for processing form submit

When you are processing a form submit using Spring MVC, if there are validation errors, you can:

  1. Return the errors in the response to the form submit request (POST request usually).
  2. Redirect the user to the form and return the result from this new request.

The first option has a drawback that if the user refreshes the page after we return with the response, the form will be resubmitted the same way as before (also he’ll see the confirmation popup asking if it is okay to do that). On one hand, most of the users won’t even understand the situation, on the other hand if the user decides to resubmit the form, sometimes unwanted situations might happen.

Also, another case to consider, if the user just wants to refresh the page to get an empty form, he’ll not succeed because he’ll still see the errors and filled values from the previous submission.

Because of these reasons the second option might be preferred, let’s see how to do that

Retaining filled values and errors upon redirect

First let’s see the whole code of a controller that implements the solution:

package com.devsphinx.web.controller.user;

import com.devsphinx.web.controller.BaseController;
import com.devsphinx.web.model.user.CreateAccountModel;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.validation.Valid;

@Controller
public class CreateAccountController extends BaseController {

    @RequestMapping(value = "/create-account", method = RequestMethod.GET)
    public String getCreateAccount(Model model) {

        if (!model.containsAttribute("createAccountModel")) {
            model.addAttribute("createAccountModel", new CreateAccountModel());
        }

        return "create-account/create-account";
    }

    @RequestMapping(value = "/create-account", method = RequestMethod.POST)
    public String postCreateAccount(
            @Valid CreateAccountModel createAccountModel,
            BindingResult result, RedirectAttributes redirectAttributes) {

        if (result.hasErrors()) {
            redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.createAccountModel", result);
            redirectAttributes.addFlashAttribute("createAccountModel", createAccountModel);
            return "redirect:/create-account";
        }
        
        // Success case omitted...
    }
}

You can see that we have here a standard spring controller with two methods. The first method serves the GET request for the account creation page and the second one serves the POST request.

What we wanted to achieve is if the POST request is performed and there are validation errors, then the request is redirected to the GET handler with the errors and form field values still populated.

In the POST handler method

if (result.hasErrors()) {
    redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.createAccountModel", result);
    redirectAttributes.addFlashAttribute("createAccountModel", createAccountModel);
    return "redirect:/create-account";
}

In this, we have to populate these to the RedirectAttributes instance.

  • For the BindingResult you have to specify the class with the full package name and contatenate the model’s name to the end.
  • The model must be added with the same name as you were using in this method.

In the GET handler method

@RequestMapping(value = "/create-account", method = RequestMethod.GET)
public String getCreateAccount(Model model) {

  if (!model.containsAttribute("createAccountModel")) {
    model.addAttribute("createAccountModel", new CreateAccountModel());
  } 

  return "create-account/create-account";
}

If we redirect from the POST handler, because we added the flash attributes, the model in the GET handler will be automatically populated with these.

However, if we reach the GET handler with just a regular page request, without anything added as flash attributes, we need to manually add the our form backing object (createAccountModel) to the model.

How to monitor your local Java web application through JMX

Java Management Extensions (JMX) is a Java technology that supplies tools for monitoring applications. Monitoring a locally running Java web application is really straightforward.

You do not need any extra configuration to set up, you only need a tool called JConsole, and it’ll be able to see all your locally running JVMs and monitor them.

JConsole can be found in the bin directory inside your JDK’s installation directory. For me it is: C:\Program Files\Java\jdk1.8.0_91\bin 

Start jconsole.exe and you should see your local processes:

On this example screenshot, the first process is a Java web application which I started in a standalone tomcat. The second one is the process of JConsole itself.

Even you are running your application using the Maven tomcat plugin and not in a standalone tomcat, it’ll still work.

Select one of the processes and double click on it. You should see the management interface open up with a slew of useful statistics and information:

Troubleshooting

If you do not see any processes in the “Local Processes” list, please check out this post for a possible solution.

JConsole is not showing any local processes

JConsole is a great tool to monitor Java applications using JMX. One common issue that can happen, is that when you start JConsole, you don’t see any process in the “Local Processes” list.

Normally, you should at least see the process for JConsole itself:

If you do not see any process in the list, not even JConsole’s, then you probably have a permission issue. Let’s see how can that happen.

When you are running a JVM, it generates a log file for each of your JVM processes. These log files contain performance related information, and are by default stored in a folder called hsperfdata_yourusername under your operating system’s temp folder.

JConsole is using these log files, to show you the list of local processes. So if the log files do not exist, you will see an empty list under “Local Processes”. The reason why there are no log files for your processes could be, that the JVM does not have enough permissions to create them.

The easiest solution to this is to just delete this log folder and let the JVM recreate it with the correct permissions.

To find it in it’s default location on Windows, you can just check the contents of your TMP environment variable:

> echo %TMP%
C:\Users\Username\AppData\Local\Temp

Look for a folder called hsperfdata_yourusername in this temp diretory and simply delete it.

Relaunch JConsole and your applications that need monitoring, and you should now see your local processes.

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