Статьи

Spring MVC + Hibernate + Maven: пример работы CRUD

В этой статье я хочу рассмотреть пример использования Spring MVC + Hibernate + Maven . Этот набор технологий подразумевает базовые знания в области предметной области. Поэтому я постараюсь объяснить все существенные моменты в деталях. Другие вещи, которые не по теме, будут снабжены ссылками на более подробные источники. В конце поста я опубликую ссылку на GitHub.

Цель

Создание примера веб-приложения на основе Spring MVC, Hibernate, Maven. Интерфейс будет основан на HTML. Приложение будет поддерживать все операции CRUD: создавать, читать, обновлять, удалять . Как обычно, я буду использовать MySQL в качестве базы данных.

Приложение будет работать с футбольными клубами юридических лиц, поэтому будьте готовы, что учебник будет в спортивном направлении.

Весна-MVC-Hibernate-проект-структура

Препараты

Мне понадобится одна таблица в базе данных, и вот код для ее создания:

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>
...

И скриншот страницы «Список команд»:

Пружинный MVC-гибернации-CRUD-пример

Резюме

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