Oleg Atamanenko
Уже прошло несколько лет с тех пор, как появился JPA. Работа с EntityManager увлекательна, но разработчики пишут красивый API, а подробности работы с базой данных скрывают. При этом частая проблема - дублирование имплементации, когда из одного DAO в другой у нас плавно перекочёвывает один и тот же код, в лучшем случае этот код переносится в абстрактный базовый DAO. Spring Data коренным образом решает проблему - при его использовании остаётся только API на уровне интерфейсов, вся имплементация создаётся автоматически с использованием AOP.
Несмотря на то, что проект только недавно достиг версии 1.0, у него достаточно богатая история - раньше он развивался в рамках проекта Hades.
Итак, для начала нам необходимо объявить DAO-интерфейс, в котором мы будем объявлять методы для работы с сущностью.
public interface UserRepository extends CrudRepository<User, Long> {
}
Данного кода достаточно для обычного DAO с CRUD-методами.
Полный список методов, объявленный в CrudRepository можно посмотреть в javadoc.
В случае, если нам нужны не все методы, то есть возможность произвести наследование от интерфейса Repository и перенести в наследника только те методы из интерфейса CrudRepository, которые нужны.
Page<User> findAll(Pageable pageable);
Интерфейс Pageable инкапсулирует в себе сведения о номере запрашиваемой страницы, размере страницы, а также требуемой сортировке.
Например, нам нужен методы для поиска пользователя по логину и по его e-mail адресу:
User findByLogin(String login);
User findByEmail(String email);
Все просто.
В случае, если нужны более сложные условия для поиска, то и это тоже реализовано.
Spring Data поддерживает следующие операторы:
Если необходимо, чтобы в результатах поиска было несколько сущностей, то необходимо называть метод findAll
@Controller
@RequestMapping("/users")
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
userRepository = userRepository;
}
@RequestMapping("/{id}")
public String showUserForm(@PathVariable("id") Long id, Model model) {
// Do null check for id
User user = userRepository.findOne(id);
// Do null check for user
// Populate model
return "user";
}
}
Во-первых, вы объявляете зависимость на DAO, а во-вторых всегда вызываете метод findOne() для загрузки сущности. К счастью, Spring позволяет нам преобразовывать строковые значения из HTTP-запросов в любой нужный тип используя либо PropertyEditor, либо ConversionService.
Если вы используете Spring версии 3.0 и выше, то вам необходимо добавить следующую конфигурацию:
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="….context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="org.springframework.data.repository.support.DomainClassConverter">
<constructor-arg ref="conversionService" />
</bean>
</list>
</property>
</bean>
Если же вы используете Spring более старой версии, то вам необходима вот такая конфигурация:
<bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="….web.bind.support.ConfigurableWebBindingInitializer">
<property name="propertyEditorRegistrars">
<bean class="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" />
</property>
</bean>
</property>
</bean>
После данных изменений в конфигурации можно переписать контроллер следующим образом:
@Controller
@RequestMapping("/users")
public class UserController {
@RequestMapping("/{id}")
public String showUserForm(@PathVariable("id") User user, Model model) {
// Do null check for user
// Populate model
return "userForm";
}
}
Обратите внимание на то, как упростился код и как мы красиво избавились от его дублирования.