Статьи

Учебник Spring Data JPA, часть первая: настройка

Spring Data JPA — это проект, целью которого является как упростить создание репозиториев на основе JPA, так и уменьшить объем кода, необходимого для взаимодействия с базой данных. Я использовал его некоторое время на своей работе и в моих личных проектах хобби, и это действительно сделало вещи намного проще и чище. Теперь пришло время поделиться с вами своими знаниями.

Это первая часть моего учебного пособия по Spring Data JPA, и в нем будет описано, как настроить Spring Data JPA при использовании Hibernate в качестве поставщика JPA. Прежде чем мы начнем, я хочу сделать одну вещь прямо: этот учебник не является учебником начального уровня для Hibernate, JPA или Spring. У вас должен быть некоторый опыт работы с этими технологиями, если вы хотите понять концепции, описанные в моем руководстве по Spring Data JPA.

Зависимости этого урока следующие:

  • BoneCP 0.7.1.RELEASE (можно использовать и другие реализации источника данных)
  • Hibernate 4.0.1.Final
  • Spring Framework 3.1.0.RELEASE
  • Spring Data JPA 1.0.2
  • Сервлет API 3.0

Кроме того, поскольку я использую Maven в качестве инструмента для сборки, вы должны установить его, если хотите запустить мой пример приложения.

Начиная

Время начинать. Вы можете настроить JPA Spring Data, выполнив следующие действия:

  • Вы должны получить необходимые зависимости.
  • Вы должны настроить необходимые bean-компоненты в конфигурации контекста приложения Spring. Компоненты, необходимые для Spring Data JPA: источник данных, менеджер транзакций и фабрика менеджера сущностей.
  • Вы должны настроить JPA Spring Data.

Эти шаги объясняются более подробно в следующем:

Получение необходимых зависимостей

Во-первых, вам необходимо получить необходимые зависимости. Вы можете сделать это, настроив необходимые зависимости в вашем файле pom.xml . Pom.xml моего примера выглядит следующим образом:

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
    <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>
            <url>http://www.apache.org/licenses/LICENSE-2.0</url>
        </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>

Настройка контекста приложения Spring

Во-вторых, вы должны настроить контекст приложения Spring. Как вы, возможно, помните, вам нужно настроить bean-компоненты источника данных, менеджера транзакций и менеджера сущностей. Если вы используете Spring 3.1 и Servlet 3.0, вы можете сделать это, реализовав класс конфигурации Java и загрузив этот класс конфигурации в инициализатор веб-приложения. Содержание моего класса конфигурации контекста приложения дано в следующем:

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;
    }
}

Мой инициализатор веб-приложения выглядит так:

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));
    }
}

Возможно, вы заметили, что я использовал аннотацию @PropertySource, чтобы указать расположение файла свойств, который содержит значения используемых параметров конфигурации. Содержимое моего файла application.properties следующее:

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

Конфигурирование Spring Data JPA

В-третьих, вы должны настроить JPA Spring Data. Если вы обратили внимание, вы могли заметить, что я использовал аннотацию @ImportResource в своем классе конфигурации контекста приложения для импорта дополнительной конфигурации из файла конфигурации XML. На данный момент Spring Data JPA не поддерживает настройку Java. Таким образом, единственный способ настроить это — использовать файл конфигурации XML. Мой файл pplicationContext.xml выглядит следующим образом:

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>

Вы сделали

Вот и все. Теперь я продемонстрировал вам, как вы можете настроить Spring Data JPA. Я также создал пример приложения, чтобы продемонстрировать, что эта конфигурация действительно работает. Вы можете самостоятельно протестировать конфигурацию, загрузив мой пример приложения из Github и запустив пример веб-приложения с помощью плагина Maven Jetty ( Примечание : не забудьте сначала создать модель и пакеты репозитория . Поскольку невозможно добавить пустые каталоги в Git область размещения , в хранилище Github их тоже нет).

Во второй части моего учебного руководства по Spring Data JPA описывается, как создать простое веб-приложение CRUD с помощью Spring Data JPA. Оставайтесь в курсе.