Spring Data JPA Tutorial Part One: Configuration
Spring Data JPA is a project which aims both to simplify the creation of JPA based repositories and to reduce the amount of code needed to communicate with a database. I have been using it for a while at my work and in my personal hobby projects and it has indeed make things a lot more simpler and cleaner. Now it is time to share my knowledge with you.
This is the first part of my Spring Data JPA tutorial and it will describe to you, how you can configure Spring Data JPA when you are using Hibernate as your JPA provider. Before we will get started, I want to make one thing straight: This tutorial is not an introductory level tutorial to Hibernate, JPA or Spring. You must have some experience about these technologies if you want to understand the concepts described in my Spring Data JPA tutorial.
The dependencies of this tutorial are following:
- BoneCP 0.7.1.RELEASE (You can use other data source implementations as well)
- Hibernate 4.0.1.Final
- Spring Framework 3.1.0.RELEASE
- Spring Data JPA 1.0.2
- Servlet API 3.0
Also, since I am using Maven as a build tool, you must install it if you want to run my example application.
Getting Started
It is time to get started. You can configure the Spring Data JPA by following these steps:
- You have to obtain the needed dependencies.
- You must configure the needed beans in your Spring application context configuration. The beans needed by the Spring Data JPA are: data source, transaction manager and entity manager factory.
- You have to configure the Spring Data JPA.
These steps are explained with more details in following:
Obtaining the Needed Depedencies
First, you need to obtain the needed dependencies. You can do this by configuring the needed dependencies in your pom.xml file. The pom.xml of my example looks following:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | < project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" < modelVersion >4.0.0</ modelVersion > < groupId >net.petrikainulainen.spring</ groupId > < artifactId >data-jpa-tutorial-part-one</ artifactId > < packaging >war</ packaging > < version >0.1</ version > < name >Spring Data JPA Tutorial Part One</ name > < description >Spring Data JPA Tutorial Part One</ description > < licenses > < license > < name >Apache License 2.0</ name > </ license > </ licenses > < repositories > < repository > < id >repository.jboss.org-public</ id > < name >JBoss repository</ name > </ repository > </ repositories > < properties > < hibernate.version >4.0.1.Final</ hibernate.version > < mysql.connector.version >5.1.18</ mysql.connector.version > < slf4j.version >1.6.1</ slf4j.version > < spring.version >3.1.0.RELEASE</ spring.version > < project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > </ properties > < dependencies > <!-- Spring Framework --> < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-beans</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-core</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context-support</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-jdbc</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-orm</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-tx</ artifactId > < version >${spring.version}</ version > </ dependency > <!-- Spring MVC --> < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-web</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-webmvc</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >cglib</ groupId > < artifactId >cglib</ artifactId > < version >2.2.2</ version > </ dependency > <!-- Spring Data JPA --> < dependency > < groupId >org.springframework.data</ groupId > < artifactId >spring-data-jpa</ artifactId > < version >1.0.2.RELEASE</ version > </ dependency > <!-- Hibernate --> < dependency > < groupId >org.hibernate</ groupId > < artifactId >hibernate-core</ artifactId > < version >${hibernate.version}</ version > </ dependency > < dependency > < groupId >org.hibernate</ groupId > < artifactId >hibernate-entitymanager</ artifactId > < version >${hibernate.version}</ version > </ dependency > <!-- H2 Database --> < dependency > < groupId >com.h2database</ groupId > < artifactId >h2</ artifactId > < version >1.3.160</ version > </ dependency > <!-- MySQL JDBC connector --> <!-- If you want to use MySQL, uncomment this dependency declation. --> <!-- <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> </dependency> --> <!-- PostgreSQL JDBC 4 --> <!-- If you don't want to use PostgreSQL, uncomment this dependency declaration. --> <!-- <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>9.0-801.jdbc4</version> </dependency> --> <!-- BoneCP --> < dependency > < groupId >com.jolbox</ groupId > < artifactId >bonecp</ artifactId > < version >0.7.1.RELEASE</ version > </ dependency > <!-- Servlet API 3.0 --> < dependency > < groupId >javax.servlet</ groupId > < artifactId >javax.servlet-api</ artifactId > < version >3.0.1</ version > < scope >provided</ scope > </ dependency > < dependency > < groupId >javax.servlet</ groupId > < artifactId >jstl</ artifactId > < version >1.2</ version > </ dependency > <!-- Logging dependencies --> < dependency > < groupId >org.slf4j</ groupId > < artifactId >slf4j-api</ artifactId > < version >${slf4j.version}</ version > </ dependency > < dependency > < groupId >org.slf4j</ groupId > < artifactId >slf4j-log4j12</ artifactId > < version >${slf4j.version}</ version > </ dependency > < dependency > < groupId >log4j</ groupId > < artifactId >log4j</ artifactId > < version >1.2.16</ version > </ dependency > <!-- Testing Dependencies --> < dependency > < groupId >junit</ groupId > < artifactId >junit</ artifactId > < version >4.9</ version > < scope >test</ scope > </ dependency > </ dependencies > < build > < finalName >data-jpa-tutorial-part-one</ finalName > < plugins > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-compiler-plugin</ artifactId > < version >2.3.2</ version > < configuration > < source >1.6</ source > < target >1.6</ target > </ configuration > </ plugin > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-war-plugin</ artifactId > < version >2.1.1</ version > < configuration > < failOnMissingWebXml >false</ failOnMissingWebXml > </ configuration > </ plugin > < plugin > < groupId >org.mortbay.jetty</ groupId > < artifactId >jetty-maven-plugin</ artifactId > < version >8.1.0.RC2</ version > < configuration > < scanIntervalSeconds >0</ scanIntervalSeconds > < webAppConfig > < defaultsDescriptor >src/main/resources/webdefault.xml</ defaultsDescriptor > </ webAppConfig > </ configuration > </ plugin > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-site-plugin</ artifactId > < version >3.0</ version > < configuration > < reportPlugins > <!-- Cobertura Plugin --> < plugin > < groupId >org.codehaus.mojo</ groupId > < artifactId >cobertura-maven-plugin</ artifactId > < version >2.5.1</ version > </ plugin > </ reportPlugins > </ configuration > </ plugin > </ plugins > </ build > </ project > |
Configuring the Spring Application Context
Second, you must configure the Spring application context. As you might remember, you need to configure the data source, transaction manager and entity manager factory beans. If you are using Spring 3.1 and Servlet 3.0, you can do this by implementing a Java configuration class and loading that configuration class in your web application initializer. The content of my application context configuration class is given in following:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | import com.jolbox.bonecp.BoneCPDataSource; import org.hibernate.ejb.HibernatePersistence; import org.springframework.context.MessageSource; import org.springframework.context.annotation.*; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.core.env.Environment; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; import javax.annotation.Resource; import javax.sql.DataSource; /** * An application context Java configuration class. The usage of Java configuration * requires Spring Framework 3.0 or higher with following exceptions: * <ul> * <li>@EnableWebMvc annotation requires Spring Framework 3.1</li> * </ul> * @author Petri Kainulainen */ @Configuration @ComponentScan (basePackages = { "net.petrikainulainen.spring.datajpa.controller" }) @EnableWebMvc @ImportResource ( "classpath:applicationContext.xml" ) @PropertySource ( "classpath:application.properties" ) public class ApplicationContext { private static final String VIEW_RESOLVER_PREFIX = "/WEB-INF/jsp/" ; private static final String VIEW_RESOLVER_SUFFIX = ".jsp" ; private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver" ; private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password" ; private static final String PROPERTY_NAME_DATABASE_URL = "db.url" ; private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username" ; private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect" ; private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql" ; private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy" ; private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql" ; private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan" ; private static final String PROPERTY_NAME_MESSAGESOURCE_BASENAME = "message.source.basename" ; private static final String PROPERTY_NAME_MESSAGESOURCE_USE_CODE_AS_DEFAULT_MESSAGE = "message.source.use.code.as.default.message" ; @Resource private Environment environment; @Bean public DataSource dataSource() { BoneCPDataSource dataSource = new BoneCPDataSource(); dataSource.setDriverClass(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER)); dataSource.setJdbcUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL)); dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME)); dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD)); return dataSource; } @Bean public JpaTransactionManager transactionManager() throws ClassNotFoundException { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject()); return transactionManager; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() throws ClassNotFoundException { LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); entityManagerFactoryBean.setDataSource(dataSource()); entityManagerFactoryBean.setPackagesToScan( environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN)); entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence. class ); Properties jpaProterties = new Properties(); jpaProterties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT)); jpaProterties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL)); jpaProterties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY)); jpaProterties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL)); entityManagerFactoryBean.setJpaProperties(jpaProterties); return entityManagerFactoryBean; } @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename( environment.getRequiredProperty(PROPERTY_NAME_MESSAGESOURCE_BASENAME)); messageSource.setUseCodeAsDefaultMessage( Boolean.parseBoolean( environment.getRequiredProperty(PROPERTY_NAME_MESSAGESOURCE_USE_CODE_AS_DEFAULT_MESSAGE))); return messageSource; } @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView. class ); viewResolver.setPrefix(VIEW_RESOLVER_PREFIX); viewResolver.setSuffix(VIEW_RESOLVER_SUFFIX); return viewResolver; } } |
My web application initializer looks like this:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.*; /** * Web application Java configuration class. The usage of web application * initializer requires Spring Framework 3.1 and Servlet 3.0. * @author Petri Kainulainen */ public class DataJPAExampleInitializer implements WebApplicationInitializer { private static final String DISPATCHER_SERVLET_NAME = "dispatcher" ; private static final String DISPATCHER_SERVLET_MAPPING = "/" ; @Override public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(ApplicationContext. class ); ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(rootContext)); dispatcher.setLoadOnStartup( 1 ); dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING); servletContext.addListener( new ContextLoaderListener(rootContext)); } } |
You might have noticed that I used the @PropertySource annotation to specify the location of a properties file which contains the values of used configuration parameters. The content of my application.properties file is following:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # The default database is H2 memory database but I have also # added configuration needed to use either MySQL and PostgreSQL. #Database Configuration db.driver=org.h2.Driver #db.driver=com.mysql.jdbc.Driver #db.driver=org.postgresql.Driver db.url=jdbc:h2:mem:datajpa #db.url=jdbc:mysql://localhost:3306/datajpa #db.url=jdbc:postgresql://localhost/datajpa db.username=sa db.password= #Hibernate Configuration hibernate.dialect=org.hibernate.dialect.H2Dialect #hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect #hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect hibernate.format_sql= true hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy hibernate.show_sql= true #MessageSource message. source . basename =i18n /messages message. source .use.code.as.default.message= true #EntityManager #Declares the base package of the entity classes entitymanager.packages.to.scan=net.petrikainulainen.spring.datajpa.model |
Configuring Spring Data JPA
Third, you must configure the Spring Data JPA. If you were paying attention, you might have noticed that I used the @ImportResource annotation in my application context configuration class to import additional configuration from a XML configuration file. At the moment Spring Data JPA does not support Java configuration. Thus, the only way to configure it is to use a XML configuration file. My applicationContext.xml file looks following:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation=" <!-- Configures the location of static resources such as css files. Requires Spring Framework 3.0 or higher. --> < mvc:resources mapping = "/static/**" location = "/static/" /> <!-- Ensures that dispatcher servlet can be mapped to '/' and static resources are still served by the containers default servlet. Requires Spring Framework 3.0 or higher. --> < mvc:default-servlet-handler /> <!-- Configures Spring Data JPA and sets the base package of my DAOs. --> < jpa:repositories base-package = "net.petrikainulainen.spring.datajpa.repository" /> </ beans > |
You Are Done
That is it. I have now demonstrated to you how you can configure the Spring Data JPA. I also have created an example application to demonstrate that this configuration is actually working. You can test the configuration yourself by getting my example application from Github and running the example web application by using the Maven Jetty plugin (Note: Remember to create the model and repository packages first. Since it is not possible to add empty directories to the Git staging area, the Github repository does not have them either).
The second part of my Spring Data JPA tutorial describes how you can create a simple CRUD web application by using Spring Data JPA. Stay tuned.
Hi Petri,
Pretty good article! Clear and consistent.
I use there a bit different approach to configure Spring Data JPA: annotations (@EnableJpaRepositories). Thanks to that you can fully get rid of XML configuration.
In case you are interested in quickly starting up with Spring MVC with JPA and Spring Data JPA project have a look at my Maven Archetype: https://github.com/kolorobot/spring-mvc-quickstart-archetype
Hi Rafal,
I am happy to hear that you liked the article.
By the way, I use Java configuration as well but when I wrote that blog post, I had to use XML configuration because Java configuration was not supported by the Spring Data JPA.
It was quite annoying. Luckily those days are over.
Hi Petri, great tutorial, it helped me alot
Congratulations, thanks man !
Thank you for good tutorial. Could you please provide code which uses XML mapping configuration and not annotation based ?
Thank you,
Geeta