→ Spring Data JPA. Пишем DAO и Services. Часть 2
→ Spring Data JPA. JUnit тесты для Services. Часть 3
Spring – достаточно многофункциональный framework, и если вы надумали делать enterprise проект, то вам не обойтись без возможности работать с базами данных. В этой серии уроков, мы научимся работать с БД используя возможности Spring Data.
Шаг 0. Постановка задачи
Все проект начинаются именно с постановки задачи. Вы должны определится с тем какая главная цель проект, и какие проблемы либо задачи будет решать ваш проект.
Нашей задачей будет: написать банковскую систему управления клиентами и персоналом. Может звучит немного и сложно, но на самом деле все довольно просто.
Давайте посмотри на рисунок ниже:
Примерно так выглядит облегченная модель банковской системы.
Шаг 1. Моделируем ERD
Теперь нужно на основании построенной модели выше на изображении, смоделировать ERD диаграмму.
ERD (Entity-Relationship Diagram) – это отображение модели сущностей в базе данных. Попросту мы моделируем сущности (таблицы) базы данных для определенной модели.
Вот такая ERD получилась у меня для нашей модели Банковской Системы.
Для построения ERD я использую сервис lucidchart.com
Теперь немного разберем нашу ERD:
worker – таблица, которая будет хранить все сотрудников определенного банка.
bank – таблица, которая будет хранить банки системы.
client – таблица клиентов банка, или нескольких банков.
bank_of_account – таблица, которая будет хранить счета клиентов банка.
Как вы видите между таблицами есть связи, о них вы можете почитать тут – Как связать Entity в JPA?
Шаг 2. Создание проекта и добавление Dependencies
Создаем новый проект и называем его com.devcolibri.dataexam.
И pom.xml будет иметь следующие зависимости:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.devcolibri.dataexam</groupId> <artifactId>com.devcolibri.dataexam</artifactId> <version>1.0</version> <packaging>war</packaging> <properties> <java.version>1.7</java.version> <spring.mvc>4.0.3.RELEASE</spring.mvc> <spring.data>1.3.4.RELEASE</spring.data> <javax.servlet>3.0.1</javax.servlet> <mysql.version>5.1.29</mysql.version> <hb.manager>4.2.5.Final</hb.manager> <spring.test>3.2.4.RELEASE</spring.test> <junit.version>4.11</junit.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.mvc}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${javax.servlet}</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring.data}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hb.manager}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.test}</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>devcolibri-dataexam</finalName> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> </project>
Как вы уже могли заметить использую я MySQL поэтому и зависимость подключил именно для этой БД.
Если вы захотите использовать другую БД, то ничего вам не мешает поменять зависимость и данные в файле свойств.
Шаг 3. Конфигурация проекта
Теперь нам нужно сконфигурировать проект, а именно Spring Configuration.
Для начало посмотрите на текущую структуру проекта ниже и создайте себе такую же.
В resources есть файл app.properties в нем хранятся все свойства, необходимые проекта, в нашем случае это доступ к БД и т.п.
#DB properties: db.driver=com.mysql.jdbc.Driver db.url=jdbc:mysql://localhost:3306/testdb db.username=root db.password=root #Hibernate Configuration: db.hibernate.dialect=org.hibernate.dialect.MySQLDialect db.hibernate.show_sql=true db.entitymanager.packages.to.scan=com.devcolibri.dataexam.entity db.hibernate.hbm2ddl.auto = create
Данные свойства мы будем использовать в конфигурации EntityManager ниже.
Теперь обратите своё внимание на пакет configuration, в класс AppInitializer добавим следующее содержимое:
package com.devcolibri.dataexam.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[]{ DataConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[0]; } @Override protected String[] getServletMappings() { return new String[0]; } }
Я выделил 9-ю строку, так как в ней мы регистрируем конфигурацию необходимых нам бинов для работы с БД.
А теперь давайте создадим саму конфигурацию DataConfig, которая будет инлдектить JPA бины:
package com.devcolibri.dataexam.config; import org.hibernate.ejb.HibernatePersistence; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Properties; @Configuration @EnableTransactionManagement @ComponentScan("com.devcolibri.dataexam") @PropertySource("classpath:app.properties") @EnableJpaRepositories("com.devcolibri.dataexam.repository") public class DataConfig { private static final String PROP_DATABASE_DRIVER = "db.driver"; private static final String PROP_DATABASE_PASSWORD = "db.password"; private static final String PROP_DATABASE_URL = "db.url"; private static final String PROP_DATABASE_USERNAME = "db.username"; private static final String PROP_HIBERNATE_DIALECT = "db.hibernate.dialect"; private static final String PROP_HIBERNATE_SHOW_SQL = "db.hibernate.show_sql"; private static final String PROP_ENTITYMANAGER_PACKAGES_TO_SCAN = "db.entitymanager.packages.to.scan"; private static final String PROP_HIBERNATE_HBM2DDL_AUTO = "db.hibernate.hbm2ddl.auto"; @Resource private Environment env; @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getRequiredProperty(PROP_DATABASE_DRIVER)); dataSource.setUrl(env.getRequiredProperty(PROP_DATABASE_URL)); dataSource.setUsername(env.getRequiredProperty(PROP_DATABASE_USERNAME)); dataSource.setPassword(env.getRequiredProperty(PROP_DATABASE_PASSWORD)); return dataSource; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); entityManagerFactoryBean.setDataSource(dataSource()); entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class); entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN)); entityManagerFactoryBean.setJpaProperties(getHibernateProperties()); return entityManagerFactoryBean; } @Bean public JpaTransactionManager transactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return transactionManager; } private Properties getHibernateProperties() { Properties properties = new Properties(); properties.put(PROP_HIBERNATE_DIALECT, env.getRequiredProperty(PROP_HIBERNATE_DIALECT)); properties.put(PROP_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROP_HIBERNATE_SHOW_SQL)); properties.put(PROP_HIBERNATE_HBM2DDL_AUTO, env.getRequiredProperty(PROP_HIBERNATE_HBM2DDL_AUTO)); return properties; } }
В дальнейшем я еще буду ссылаться на конфигурацию выше, для разъяснения некоторых вещей с этой конфигурации.
Разберем аннотации:
@Configuration – говорит, что данный класс является Spring конфигурацией;
@EnableTransactionManagement – включает TransactionManager для управления транзакциями БД;
@ComponentScan(“com.devcolibri.dataexam”) – указываем Spring где нужно искать Entity, DAO, Service и т.п.;
@PropertySource(“classpath:app.properties”) – подключаем файл свойств созданный выше;
@EnableJpaRepositories(“com.devcolibri.dataexam.repository”) – включаем возможность использования JPARepository и говорим, где их искать. (будем рассматривать позже детальней).
private Environment env;
В нашем случае нужен для возможности получать свойства из property файла.
Шаг 4. Создаем Entity на основе нашей ERD диаграммы
Начнем с Банка (Bank):
package com.devcolibri.dataexam.entity; import org.hibernate.annotations.GenericGenerator; import javax.persistence.*; @Entity @Table(name = "bank") public class Bank { @Id @GeneratedValue(generator = "increment") @GenericGenerator(name= "increment", strategy= "increment") @Column(name = "id", length = 6, nullable = false) private long id; @Column(name = "name") private String name; public Bank() { } public Bank(String name) { this.name = name; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Подробней о JPA можно почитать тут.
Теперь создаем BankAccount:
package com.devcolibri.dataexam.entity; import org.hibernate.annotations.GenericGenerator; import javax.persistence.*; @Entity @Table(name = "bank_account") public class BankAccount { @Id @GeneratedValue(generator = "increment") @GenericGenerator(name= "increment", strategy= "increment") @Column(name = "id", length = 6, nullable = false) private long id; @Column(name = "currency") private double currency; @Column(name = "amount") private double amount; @Column(name = "amount_of_credit") private double amountOfCredit; @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST}) @JoinColumn(name = "client_id", nullable = false) private Client client; public BankAccount() { } public BankAccount(double currency, double amount, double amountOfCredit, Client client) { this.currency = currency; this.amount = amount; this.amountOfCredit = amountOfCredit; this.client = client; } public long getId() { return id; } public void setId(long id) { this.id = id; } public double getCurrency() { return currency; } public void setCurrency(double currency) { this.currency = currency; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public double getAmountOfCredit() { return amountOfCredit; } public void setAmountOfCredit(double amountOfCredit) { this.amountOfCredit = amountOfCredit; } public Client getClient() { return client; } public void setClient(Client client) { this.client = client; } }
Как вы уже заметили BankAccount использует Client, поэтому создаем Client entity:
package com.devcolibri.dataexam.entity; import org.hibernate.annotations.GenericGenerator; import javax.persistence.*; @Entity @Table(name = "client") public class Client { @Id @GeneratedValue(generator = "increment") @GenericGenerator(name= "increment", strategy= "increment") @Column(name = "id", length = 6, nullable = false) private long id; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "phone_number") private String phoneNumber; @Column(name = "address") private String address; @Column(name = "email") private String email; @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST}) @JoinColumn(name = "bank_id", nullable = false) private Bank bank; public Client(String firstName, String lastName, String phoneNumber, String address, String email, Bank bank) { this.firstName = firstName; this.lastName = lastName; this.phoneNumber = phoneNumber; this.address = address; this.email = email; this.bank = bank; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Bank getBank() { return bank; } public void setBank(Bank bank) { this.bank = bank; } }
Ну и рабочие (Worker):
package com.devcolibri.dataexam.entity; import com.devcolibri.dataexam.entity.enums.WorkerStatus; import org.hibernate.annotations.GenericGenerator; import javax.persistence.*; @Entity @Table(name = "worker") public class Worker { @Id @GeneratedValue(generator = "increment") @GenericGenerator(name= "increment", strategy= "increment") @Column(name = "id", length = 6, nullable = false) private long id; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "status") private WorkerStatus status; @Column(name = "phone_number") private String phoneNumber; @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST}) @JoinColumn(name = "bank_id", nullable = false) private Bank bank; public Worker() { } public Worker(String firstName, String lastName, WorkerStatus status, String phoneNumber, Bank bank) { this.firstName = firstName; this.lastName = lastName; this.status = status; this.phoneNumber = phoneNumber; this.bank = bank; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public WorkerStatus getStatus() { return status; } public void setStatus(WorkerStatus status) { this.status = status; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public Bank getBank() { return bank; } public void setBank(Bank bank) { this.bank = bank; } }
У каждого рабочего есть своя должность, эти должности мы положим в enum WorkerStatus:
package com.devcolibri.dataexam.entity.enums; public enum WorkerStatus { MANAGER; }
Как работать с Enums в JPA я показывал тут.

Дальше мы будем создавать DAO и Сервисы.
→ Spring Data JPA. Пишем DAO и Services. Часть 2
→ Spring Data JPA. JUnit тесты для Services. Часть 3
Также читайте: «Быстрый старт в Spring Security»
ПОХОЖИЕ ПУБЛИКАЦИИ
- None Found
18 комментариев к статье "Spring Data JPA. Работа с БД. Часть 1"