В этой статье я хочу рассмотреть пример использования Spring MVC + Hibernate + Maven . Этот набор технологий подразумевает базовые знания в области предметной области. Поэтому я постараюсь объяснить все существенные моменты в деталях. Другие вещи, которые не по теме, будут снабжены ссылками на более подробные источники. В конце поста я опубликую ссылку на GitHub.
Цель
Создание примера веб-приложения на основе Spring MVC, Hibernate, Maven. Интерфейс будет основан на HTML. Приложение будет поддерживать все операции CRUD: создавать, читать, обновлять, удалять . Как обычно, я буду использовать MySQL в качестве базы данных.
Приложение будет работать с футбольными клубами юридических лиц, поэтому будьте готовы, что учебник будет в спортивном направлении.
Препараты
Мне понадобится одна таблица в базе данных, и вот код для ее создания:
| 
 1 
2 
3 
4 
5 
6 
 | 
CREATE TABLE `teams` (  `id` int(6) NOT NULL AUTO_INCREMENT,  `name` varchar(40) NOT NULL,  `rating` int(6) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; | 
Эта таблица будет представлена в приложении классом:
| 
 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 
31 
32 
 | 
@Entity@Table(name="teams")public class Team {    @Id    @GeneratedValue    private Integer id;    private String name;    private Integer rating;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getRating() {        return rating;    }    public void setRating(Integer rating) {        this.rating = rating;    }} | 
Затем мне нужно создать новый веб-проект Maven в IDE (я использую Eclipse). Я опущу детали создания, вы можете прочитать об этом в одной из моих статей о создании веб-проекта Maven . Вот ссылка на файл pom.xml . Первой важной остановкой является файл WebAppConfig.java, поэтому давайте рассмотрим:
| 
 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 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
 | 
@Configuration@ComponentScan("com.sprhib")@EnableWebMvc@EnableTransactionManagement@PropertySource("classpath:application.properties")public class WebAppConfig {    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_SHOW_SQL = "hibernate.show_sql";    private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";    @Resource    private Environment env;    @Bean    public DataSource dataSource() {        DriverManagerDataSource dataSource = new DriverManagerDataSource();        dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));        dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));        dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));        dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));        return dataSource;    }    @Bean    public LocalSessionFactoryBean sessionFactory() {        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();        sessionFactoryBean.setDataSource(dataSource());        sessionFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));        sessionFactoryBean.setHibernateProperties(hibProperties());        return sessionFactoryBean;    }    private Properties hibProperties() {        Properties properties = new Properties();        properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));        properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));        return properties;      }    @Bean    public HibernateTransactionManager transactionManager() {        HibernateTransactionManager transactionManager = new HibernateTransactionManager();        transactionManager.setSessionFactory(sessionFactory().getObject());        return transactionManager;    }    @Bean    public UrlBasedViewResolver setupViewResolver() {        UrlBasedViewResolver resolver = new UrlBasedViewResolver();        resolver.setPrefix("/WEB-INF/pages/");        resolver.setSuffix(".jsp");        resolver.setViewClass(JstlView.class);        return resolver;    }} | 
В начале файла вы можете увидеть @EnableTransactionManagement, он включает возможность управления транзакциями Spring на основе аннотаций. Аннотация @PropertySource («classpath: application.properties») — включает файл свойств, который находится в папке ресурсов. Обратите внимание на три бина: TransactionManager, sessionFactory, dataSource. Эти компоненты обеспечивают управление транзакциями. Для получения дополнительной информации прочитайте мою статью о функциональности Hibernate .
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
 | 
#DB properties:db.driver=com.mysql.jdbc.Driverdb.url=jdbc:mysql://localhost:3306/hibnatedbdb.username=hibuserdb.password=root#Hibernate Configuration:hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialecthibernate.show_sql=trueentitymanager.packages.to.scan=com.sprhib.model | 
Вот и все, что связано с подготовкой проекта. Далее я собираюсь показать вам DAO и уровни обслуживания.
Уровни DAO & Service
Вот интерфейсы и реализации DAO и Сервисов:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
 | 
public interface TeamDAO {    public void addTeam(Team team);    public void updateTeam(Team team);    public Team getTeam(int id);    public void deleteTeam(int id);    public List          getTeams();} | 
Реализация интерфейса DAO:
| 
 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 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
 | 
@Repositorypublic class TeamDAOImpl implements TeamDAO {    @Autowired    private SessionFactory sessionFactory;    private Session getCurrentSession() {        return sessionFactory.getCurrentSession();    }    public void addTeam(Team team) {        getCurrentSession().save(team);    }    public void updateTeam(Team team) {        Team teamToUpdate = getTeam(team.getId());        teamToUpdate.setName(team.getName());        teamToUpdate.setRating(team.getRating());        getCurrentSession().update(teamToUpdate);    }    public Team getTeam(int id) {        Team team = (Team) getCurrentSession().get(Team.class, id);        return team;    }    public void deleteTeam(int id) {        Team team = getTeam(id);        if (team != null)            getCurrentSession().delete(team);    }    @SuppressWarnings("unchecked")    public List          getTeams() {        return getCurrentSession().createQuery("from Team").list();    }} | 
Аннотация @Repository Указывает, что аннотированный класс является «DAO».
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
 | 
public interface TeamService {    public void addTeam(Team team);    public void updateTeam(Team team);    public Team getTeam(int id);    public void deleteTeam(int id);    public List          getTeams();} | 
Реализация интерфейса Сервиса:
| 
 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 
 | 
@Service@Transactionalpublic class TeamServiceImpl implements TeamService {    @Autowired    private TeamDAO teamDAO;    public void addTeam(Team team) {        teamDAO.addTeam(team);          }    public void updateTeam(Team team) {        teamDAO.updateTeam(team);    }    public Team getTeam(int id) {        return teamDAO.getTeam(id);    }    public void deleteTeam(int id) {        teamDAO.deleteTeam(id);    }    public List          getTeams() {        return teamDAO.getTeams();    }} | 
Аннотация @Service указывает, что аннотированный класс является «Сервисом». Аннотация @Transactional описывает атрибуты транзакции для метода или класса.
Контроллеры и JSP
Поскольку я собираюсь охватить все операции CRUD, эта глава будет немного длинной. Я начну с базового контроллера, он отвечает за домашнюю страницу:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
 | 
@Controllerpublic class LinkController {    @RequestMapping(value="/")    public ModelAndView mainPage() {        return new ModelAndView("home");    }    @RequestMapping(value="/index")    public ModelAndView indexPage() {        return new ModelAndView("home");    }} | 
Это достаточно просто, и вот его JSP-файл:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
 | 
...<h1>Home page</h1><p>${message}<br><a href="${pageContext.request.contextPath}/team/add.html">Add new team</a><br><a href="${pageContext.request.contextPath}/team/list.html">Team list</a><br></p>... | 
А вот класс-монстр, главный контроллер приложения:
| 
 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 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
 | 
@Controllerpublic class TeamController {    @Autowired    private TeamService teamService;    @RequestMapping(value="/team/add")    public ModelAndView addTeamPage() {        ModelAndView modelAndView = new ModelAndView("add-team-form");        modelAndView.addObject("team", new Team());        return modelAndView;    }    @RequestMapping(value="/team/add/process")    public ModelAndView addingTeam(@ModelAttribute Team team) {        ModelAndView modelAndView = new ModelAndView("home");        teamService.addTeam(team);        String message = "Team was successfully added.";        modelAndView.addObject("message", message);        return modelAndView;    }    @RequestMapping(value="/team/list")    public ModelAndView listOfTeams() {        ModelAndView modelAndView = new ModelAndView("list-of-teams");        List          teams = teamService.getTeams();        modelAndView.addObject("teams", teams);        return modelAndView;    }    @RequestMapping(value="/team/edit/{id}", method=RequestMethod.GET)    public ModelAndView editTeamPage(@PathVariable Integer id) {        ModelAndView modelAndView = new ModelAndView("edit-team-form");        Team team = teamService.getTeam(id);        modelAndView.addObject("team",team);        return modelAndView;    }    @RequestMapping(value="/team/edit/{id}", method=RequestMethod.POST)    public ModelAndView edditingTeam(@ModelAttribute Team team, @PathVariable Integer id) {        ModelAndView modelAndView = new ModelAndView("home");        teamService.updateTeam(team);        String message = "Team was successfully edited.";        modelAndView.addObject("message", message);        return modelAndView;    }    @RequestMapping(value="/team/delete/{id}", method=RequestMethod.GET)    public ModelAndView deleteTeam(@PathVariable Integer id) {        ModelAndView modelAndView = new ModelAndView("home");        teamService.deleteTeam(id);        String message = "Team was successfully deleted.";        modelAndView.addObject("message", message);        return modelAndView;    }} | 
Почти все методы и сопоставления запросов понятны. Но я хочу подчеркнуть, что @RequestMapping для методов editTeamPage () и edditingTeam () содержит различные значения для атрибута метода . И теперь пришло время увидеть JSP для этих сопоставлений:
Страница «Добавить новую команду»:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
 | 
...<h1>Add team page</h1><p>Here you can add a new team.</p><form:form method="POST" commandname="team" action="${pageContext.request.contextPath}/team/add/process.html"><table><tbody>    <tr>        <td>Name:</td>        <td><form:input path="name"></form:input></td>    </tr>    <tr>        <td>Rating:</td>        <td><form:input path="rating"></form:input></td>    </tr>    <tr>        <td><input value="Add" type="submit"></td>        <td></td>    </tr></tbody></table></form:form><p><a href="${pageContext.request.contextPath}/index.html">Home page</a></p>... | 
Страница «Список команд»:
| 
 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 
 | 
...<h1>List of teams</h1><p>Here you can see the list of the teams, edit them, remove or update.</p><c:foreach var="team" items="${teams}"></c:foreach><table border="1px" cellpadding="0" cellspacing="0"><thead><tr><th width="10%">id</th><th width="15%">name</th><th width="10%">rating</th><th width="10%">actions</th></tr></thead><tbody><tr>    <td>${team.id}</td>    <td>${team.name}</td>    <td>${team.rating}</td>    <td>    <a href="${pageContext.request.contextPath}/team/edit/${team.id}.html">Edit</a><br>    <a href="${pageContext.request.contextPath}/team/delete/${team.id}.html">Delete</a><br>    </td></tr></tbody></table><p><a href="${pageContext.request.contextPath}/index.html">Home page</a></p>... | 
Страница «Редактировать команду»:
| 
 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 
 | 
...<h1>Edit team page</h1><p>Here you can edit the existing team.</p><p>${message}</p><form:form method="POST" commandname="team" action="${pageContext.request.contextPath}/team/edit/${team.id}.html"><table><tbody>    <tr>        <td>Name:</td>        <td><form:input path="name"></form:input></td>    </tr>    <tr>        <td>Rating:</td>        <td><form:input path="rating"></form:input></td>    </tr>    <tr>        <td><input value="Edit" type="submit"></td>        <td></td>    </tr></tbody></table></form:form><p><a href="${pageContext.request.contextPath}/index.html">Home page</a></p>... | 
И скриншот страницы «Список команд»:
Резюме
Интеграция нескольких технологий обычно не простая работа, поэтому наберитесь терпения, чтобы добиться успеха в этом. В этом посте не так много источников, так что вы можете посетить мой GitHub для изучения классов, которые вас интересуют.

