В этой статье я постараюсь максимально просто объяснить Вам что такое hibernate фреймворк:
- что он из себя представляет;
- как пользоваться hibernate;
- CRUD операции на примере простой таблицы.
Все вышеперечисленное закрепим примером. А для начала постараемся вникнуть в теорию.
Hibernate – это библиотека, которая предназначена для задач объектно-реляционного отображения. Примерно такое описание будет в википедии. Если простыми словами – hibernate позволяет разработчику работать с базой данных не напрямую, как мы это делали с помощью библиотеки JDBC в статье Работа с базой данных, а с помощью представления таблиц баз данных в виде классов java.
Чтобы до конца понять дебри терминов давайте разберем что такое JPA – Java Persistance API.
JPA – спецификация, которая дает возможность сохранять в удобном виде Java-объекты в базе данных. Hibernate – это одна из самых популярных реализаций этой спецификации. Вот такая вот драма. Данные термины даны только в ознакомительных целях так как очень часто запутывают и пугают начинающих разработчиков.
Как это работать с базой данных через классы java? Если Вы открыли эту статью, то наверняка уже подключали базу данных к своему приложению и работали с ней. Если нет – то настоятельно советую прочитать цикл статей Java WEB, в которых подробно описано как создать веб приложение на чистой Java. Также, советую ознакомитья с SQL. А если я прав, и Вы уже знакомы с подключением базы данных, то наверняка работали с ней таким образом: создавали объект, который отображает таблицу базы данных. Например таблицу users с полями:
- id;
- name;
- email;
- password.
Потом писали метод подключения к базе, возможно даже пул соединений. Далее был DAO класс в котором делался запрос в базу, доставались или создавались данные, распарсивали их и подавали в виде списка или объекта примерно вот таким образом:
Article article = null;
Connection connection = ConnectionPool.getInstance().getConnection();
try {
PreparedStatement pr = connection.prepareStatement("select id, title, body, "
+ "category_id, users_id from article where id=?");
pr.setInt(1, id);//то, что нужно вставить вместо первого знака вопроса
//можно было бы написать "select id, title, body, " + "category_id, users_id from article where id="+id в запросе
//но в целях безопасности советую подставлять значение через метод сет.
ResultSet rs = pr.executeQuery();
if (rs.next()){
article = new Article();
article.setId(rs.getInt(1));
article.setTitle(rs.getString(2));
article.setBody(rs.getString(3));
Category category = new Category();
category.setId(rs.getInt(4));
article.setCategory(category);
Users user = new Users();
user.setId(rs.getInt(5));
article.setUsers(user);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return article;
}
Hibernate дает Вашей разработке приложений новые инструменты: теперь вместо кучи ненужного кода нужно просто создать класс-сущность базы данных, пометить его специальным аннотациями и фреймворк все сделает за Вас.
Это называется ORM – Object Relational Mapping. Вам всего лишь нужно создать классы, которые соответствуют таблицам в базе данных, и написать методы, которые Вы хотите сделать с данными (получить, удалить, создать, обновить). Hibernate сам генерирует SQL запрос и выполняет его. Причем, ему не важно какую базу данных Вы используете. Это дает возможность переключать базы данных и не заботиться о том, что код не будет работать.
С теорией закончили. Теперь давайте узнаем как подключить Hibernate, мапить классы и делать простые запросы.
Начнем с нуля. Создадим простое Maven приложение. Если Вы используете Eclipse: File->New->Maven project.
Не забудьте проставить галочку возле Create a simple project. Даем нашему проекту название:
Далее нужно подключить библиотеку hibernate, JPA и драйвера к базе данных к нашему проекту. В данном примере я использую Postgresql, но разницы не будет никакой если Вы будете использовать другую базу данных. Я укажу место, где нужно будет сделать поправки.
После подключения зависимостей мой файл pom.xml выглядит так:
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.javamaster</groupId>
<artifactId>hibernate-tutorial</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/postgresql/postgresql -->
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901-1.jdbc4</version>
</dependency>
</dependencies>
</project>
Теперь нужно придумать с чем будет работать наше приложение. Предлагаю использовать пример, который я описал выше. В этой ознакомительной статье с Hibernate я покажу работу этого фреймворка только с одной таблицей. Создадим в нашей постгрес базу данных hibernate_tutorial и в этой базе создадим таблицу users.
С этим, думаю, проблем не будет.
Теперь создадим класс Users в котором будут поля, геттеры, сеттеры, методы equals, hashCode, toString.
private Integer id;
private String name;
private String email;
private String password;
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((password == null) ? 0 : password.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Users other = (Users) obj;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
return true;
}
@Override
public String toString() {
return "Users [id=" + id + ", name=" + name + ", email=" + email + ", password=" + password + "]";
}
}
Ничего особенного в этом классе нет: это простой объект, который Вы видели много раз. Если Вы когда-нибудь слышали или видели аббревиатуру POJO – Plain Old Java Object то это имелся ввиду именно такой класс. Старый простой объект java. Для того, чтобы этот класс стал сущностью базы данных нужно добавить к нему немного аннотаций.
Перед объявлением класса нужно добавить аннотацию @Entity – которая укажет, что данный класс является сущностью. Это часть спецификации JPA. Далее над классом нужно добавить еще одну аннотацию – @Table. Если Ваш класс совпадает с именем таблицы, то ничего добавлять не нужно. Но, в данной аннотации есть атрибут name в значении которого можно указать имя таблицы в базе данных, которую отображает класс. Поля класса нужно пометить аннотацией @Column в атрибут которой можно также поместить имя поля в таблице базы данных. Отдельного внимания заслуживает поле Id. Для него есть отдельная аннотация @Id. Для генерируемого значение важно указать аннотацию @GeneratedValue в атрибут которой нужно указать стратегию генерируемого значение. JPA поддерживает 3 стратегии генерации ключа. Вам же нужно выбрать стратегию в зависимости от того, как Вы генерируете айди в своей базе данных. Чтобы не усложнять и так запутанную тему мы выберем стратегию GenerationType.IDENTITY которая укажет, что мы генерируем это значение автоматически.
@Entity
@Table(name = "users")
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((password == null) ? 0 : password.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Users other = (Users) obj;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
return true;
}
@Override
public String toString() {
return "Users [id=" + id + ", name=" + name + ", email=" + email + ", password=" + password + "]";
}
}
Теперь на подобии коннектора подключений нужно указать настройки hibernate, чтобы библиотека знала с какой базой мы работаем, логин, пароль и тип базы. Создадим класс HibernateUtill и добавим в него немного магии.
import java.util.Map;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Environment;
public class HibernateUtil {
private static StandardServiceRegistry registry;
private static SessionFactory sessionFactory;
public static SessionFactory getSessionFactory() {
//реализация синглтона. Если объекта нет - создаем, если есть просто возвращаем
if (sessionFactory == null) {
try {
StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
//стандартные настройки для хибернат
//для тех, кто использует другую базу данных нужно заметить поле DRIVER, DIALECT и кусок URL легко гуглятся под любую базу
Map<String, String> settings = new HashMap<>();
settings.put(Environment.DRIVER, "org.postgresql.Driver");
settings.put(Environment.URL, "jdbc:postgresql://localhost:5432/hibernate_tutorial");
settings.put(Environment.USER, "postgres");
settings.put(Environment.PASS, "postgres");
settings.put(Environment.DIALECT, "org.hibernate.dialect.PostgreSQL9Dialect");
registryBuilder.applySettings(settings);
registry = registryBuilder.build();
MetadataSources sources = new MetadataSources(registry);
sources.addAnnotatedClass(Users.class);
Metadata metadata = sources.getMetadataBuilder().build();
sessionFactory = metadata.getSessionFactoryBuilder().build();
} catch (Exception e) {
e.printStackTrace();
if (registry != null) {
StandardServiceRegistryBuilder.destroy(registry);
}
}
}
return sessionFactory;
}
public static void close() {
if (registry != null) {
StandardServiceRegistryBuilder.destroy(registry);
}
}
}
Это конфигурационный файл, который позволяет настроить hibernate без xml. Я стараюсь не программировать с xml там, где это позволяют новые возможности.
Далее создадим простой класс-сервис по CRUD операциям с сущностью Users.
import javax.persistence.criteria.CriteriaQuery;
import javax.transaction.Transactional;
import org.hibernate.Session;
public class UsersCRUD {
public void save(Users users) {
Session session = HibernateUtil.getSessionFactory().openSession(); //открываем сессию
session.beginTransaction();
session.save(users); //пользуемся ее методами
session.flush();
session.close();
}
public void delete(Users users) {
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.delete(users);
session.flush();
session.close();
}
public List<Users> getAll(){
Session session = HibernateUtil.getSessionFactory().openSession();
return session.createCriteria(Users.class).list();
}
public Users getById(Integer id) {
Session session = HibernateUtil.getSessionFactory().openSession();
Users users = session.get(Users.class, id);
return users;
}
}
В данном примере я не буду использовать язык HQL дабы не усложнять материал. Только уточню, что с помощью данного фреймворка можно работать с базой данных очень большим количеством способов, выбор которых падает на программиста и предметную область.
Теперь осталось протестировать наши методы.
public static void main(String[] args) {
UsersCRUD usersCRUD = new UsersCRUD();
Users users1 = new Users();
users1.setName("John");
users1.setEmail("connor_john@gmail.com");
users1.setPassword("somepswd123");
usersCRUD.save(users1);
Users users2 = new Users();
users2.setName("Sara");
users2.setEmail("sarra_mother@gmail.com");
users2.setPassword("qwerty123");
usersCRUD.save(users2);
usersCRUD.getAll().forEach(it->System.out.println(it));
Users userWithId1 = usersCRUD.getById(5);
System.out.println(userWithId1);
usersCRUD.delete(userWithId1);
usersCRUD.getAll().forEach(it->System.out.println(it));
}
}
Ну и результат запуска приложения:
Надеюсь, Вы поняли что такое hibernate и JPA и какие возможности открывает данный фреймворк для программиста. Это лишь малая часть того, что умеет этот инструмент. Но для ознакомления и первого впечатления должно хватить.
Код с примера находится здесь: https://github.com/caligula95/hibernate-tutorial
Спасибо, очень познавательно!
А как тогда во все это вяжется Spring JPA?
Спасибо, очень помогло! Единственный пример, который у меня скомпилировался без ошибок. При создании таблицы у поля id в самой базе данных не забудьте поставить IDENTITY (в pgAdmin: кликаете на поле id правой клавишей-> properties
-> constraints -> Type -> IDENTITY)
спасибо! вы очень хорошо доносите суть, без усложнения – самое то для новичков! 🙂
Спасибо!
Мне очень понравился этот код, но из за того, что у меня таблица на MySQL а не PostgreSQL, пришлось потратить пол дня на изучение документации. А дело оказалось в том, что надо было убрать строку:
settings.put(Environment.DIALECT, “org.hibernate.dialect.PostgreSQL9Dialect”);
В этом диалекте создавался некорректный запрос на создание таблицы.
Так же ваша програма не закрывается из-за незакрытой sessionFactory. Я дописал в конце main() строку HibernateUtil.getSessionFactory().close();
💡 😉
У меня вот возник вопрос, а что делать если у нас несколько таблиц, как мы поступим с классом “HibernateUtil ” , в нем уже захардкожено, что аннатационый класс User.class (sources.addAnnotatedClass(Users.class)) не будем же мы создавать на каждую сущность по такому классу?
Здравствуйте! Я не уверено но думаю что можно еще раз вызвать sources.addAnnotatedClass и добавить еще один класс. И sources.addAnnotatedClass можно вызывать столько раз сколько у нас классов. 🙂
на этом сайте есть ответы практически на все вопросы, а что еще больше радует это то что хоть кто то объясняет на это на eclipse. Спасибо