В качестве примера мы с вами разработаем простой телефонный справочник, который будет хранить список контактов в базе данных (PostgreSQL), при этом у каждого контакта будет ещё список телефонных номеров.
Создайте Maven-проект в любой удобной для вас IDE, лично я предпочитаю Idea IntelliJ, или же можно сгенерировать проект с помощью Maven командой:
-DarchetypeGroupId=org.apache.maven.archetypes \
-DgroupId=tz.com \
-DartifactId=phone-book
После того как проект создан, откройте файл 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>tz.com</groupId>
<artifactId>phone-book</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<hibernate.version>4.3.6.Final</hibernate.version>
<spring.version>4.1.1.RELEASE</spring.version>
<postgresql.version>9.1-901-1.jdbc4</postgresql.version>
</properties>
<dependencies>
<!-- for Hibernate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring ORM support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<mainClass>phone.book.main.Main</mainClass>
</configuration>
</plugin>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>phone.book.main.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>2.3.7</version>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Import-Package>
org.springframework.orm,
org.springframework.context,
javax.persistence,
org.hibernate,
*
</Import-Package>
<Export-Package>
</Export-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
В блоке <dependencies> у нас идёт список зависимостей для нашего проекта. В него я включил библиотеки для работы с Hibernate и Spring, а так же библиотеку для работы с базой данных PostgreSQL.
Так же хочу обратить внимание на exec-maven-plugin. Он позволяет запускать наш проект средствами Maven, что будет для нас крайне полезно. Для этого в директории проекта достаточно выполнить команду:
Обратите внимание что мы сделали ссылку на main-класс для exec-maven-plugin, который пока что не создали:
Теперь создайте в каталоге /src/main/resources/META-INF/sping файл beans.xml со следующим содержанием:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/test_db" />
<property name="username" value="postgres" />
<property name="password" value="postgres" />
</bean>
<!-- Hibernate 4 SessionFactory Bean definition -->
<bean id="hibernateSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>phone.book.model.Person</value>
<value>phone.book.model.Phone</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="personDAO" class="phone.book.dao.PersonDaoImpl">
<property name="sessionFactory" ref="hibernateSessionFactory" />
</bean>
</beans>
В beans.xml мы сконфигурировали DataSource, который содержит всю необходимую информацию для подключения к базе данных: класс JDBC-драйвера (в данном случае драйвер для PostgreSQL), URL, логин и пароль.
Затем мы создаём бин hibernateSessionFactory, используя класс LocalSessionFactoryBean из библиотеки Spring ORM. Этот класс позволяет нам сконфигурировать все настройки Hibernate в спринге, и инициализирует класс SessionFactory, с помощью которого осуществляется взаимодействие с базой данных.
Для бина hibernateSessionFactory мы задаём параметры, которые используются для инициализации SessionFactory:
- DataSource, без которого мы не сможем установить подключение к базе данных.
- Классы-сущности Hibernate, в параметре "annotatedClasses". Они представляют собой описание структуры таблиц базы данных в виде объекта. Эти классы мы скоро создадим.
- Настройки Hibernate в параметре "hibernateProperties". Тут 2 очень важных момента, а именно это SQLDialect (в данном случае PostrgeSQLDialect), который должен быть выбран или определён в соответствии с той базой данных, которую мы планируем использовать, и параметр "hibernate.hbm2ddl.auto" который определяет может ли Hibernate создавать таблицы или редактировать их структуру, если их нет или они отличаются о того что описано в классах-сущностях. У нас параметр "hibernate.hbm2ddl.auto" имеет значение "update", то есть в случае отсутствия необходимых таблиц в базе данных Hibernate их создаст.
Последним шагом мы конфигурируем в спринге класс PersonDaoImpl и передаём ему объект SessionFactory в качестве параметра.
Теперь в каталоге /src/main/java создайте пакет phone.book.dao и поместите туда интерфейс и класс, что описаны ниже:
Интерфейс PersonDao.java:
package phone.book.dao;
import phone.book.model.Person;
import java.util.List;
public interface PersonDao {
public void save(Person p);
public List<Person> getPersonList();
}
Класс PersonDaoImpl.java:
package phone.book.dao;
import phone.book.model.Person;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
public class PersonDaoImpl implements PersonDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public void save(Person person) {
Session session = this.sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.persist(person);
tx.commit();
session.close();
}
@Override
public List<Person> getPersonList() {
Session session = this.sessionFactory.openSession();
String hql = "from Person";
List<Person> personList = session.createQuery(hql).list();
session.close();
return personList;
}
}
Класс PersonDaoImpl умеет всего 2 вещи: сохранять объект Person в базу данных и получать из базы данных все сохранённые объекты Person. Класс Person описывает структуру таблицы PERSON из нашей базы данных. Забегая вперёд, скажу что у нас ещё будет таблица PHONES. Давайте создадим классы для этих таблиц в пакете phone.book.model
Класс Person.java:
package phone.book.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name="PERSON")
public class Person {
@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private List<Phone> phones;
public Person() {
super();
}
public Person(String name, List<Phone> phone) {
this.name = name;
this.phones = phone;
}
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;
}
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phone) {
this.phones = phone;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", phones=" + phones +
'}';
}
}
Класс Phone.java:
package phone.book.model;
import javax.persistence.*;
@Entity
@Table(name = "PHONES")
public class Phone {
@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String phoneNumber;
public Phone() {
super();
}
public Phone(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return "Phone{" +
"id=" + id +
", phoneNumber='" + phoneNumber + '\'' +
'}';
}
}
Классы-сущности Person и Phone заслуживают особого внимания, как я уже говорил, они являются объектным представлением таблиц базы данных для фреймворка Hibernate. Любой класс-сущность должен начинаться с аннотации @Entity. Далее в коде следует аннотация @Table, содержащая в качестве параметра название таблицы, которую описывает класс-сущность. Аннотация @Id указывает на то, что данное поле является индексом, аннотация @Column определяет соответствие переменной конкретному столбцу таблицы, а аннотация @GeneratedValue указывает на то что значение этого поля будет генерироваться автоматически.
У нас есть ещё один очень интересный момент. Дело в том что таблицы PERSON и PHONES имеют связь один ко многим, то есть у одного человека может быть несколько номеров телефона. Эта связь описана с помощью аннотации @OneToMany.
У нас всё готово для работы с базой данных, теперь осталось написать класс, который будет использовать всё что у нас уже есть. Создайте пакет phone.book.main и поместите туда главный класс для нашего приложения:
package phone.book.main;
import phone.book.dao.PersonDao;
import phone.book.model.Person;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import phone.book.model.Phone;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/beans.xml");
PersonDao personDAO = context.getBean(PersonDao.class);
ArrayList<Phone> personArrayList = new ArrayList<Phone>();
personArrayList.add(new Phone("89200868942"));
Person person = new Person("alexey", personArrayList);
personDAO.save(person);
System.out.println("Person::"+person);
List<Person> list = personDAO.getPersonList();
for(Person p : list){
System.out.println("Person List::"+p);
}
//close resources
context.close();
}
}
Тут всё довольно просто: мы получаем контекст спринга с помощью ClassPathXmlApplicationContext, достаём из контекста наш ДАО, инициализируем классы сущности с константными значениями и сохраняем информацию из этих классов в базе данных. Затем выводим на экран все контакты, которые сохранены в базе данных.
На всякий случай выкладываю скриншот с общей структурой нашего проекта:
Чтобы увидеть результат работы, соберите проект с помощью команды:
И запустите приложение с помощью команды:
Исходники проекта можно найти тут: https://github.com/AlexeyKutepov/phone-book/