В этой статье я хочу рассмотреть пример использования 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.Driver db.url=jdbc:mysql: //localhost:3306/hibnatedb db.username=hibuser db.password=root #Hibernate Configuration: hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect hibernate.show_sql= true entitymanager.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
|
@Repository public 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 @Transactional public 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
|
@Controller public 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
|
@Controller public 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 для изучения классов, которые вас интересуют.