Вместо дисклеймера
На Хабре уже есть множество статей на тему работы с Hibernate, однако, как мне показалось, все они довольно сложные для новичков. Эта статья направлена на разъяснение основ работы с ORM и будет полезна в первую очередь тем, кто только начинает разрабатывать собственные приложения и имеет мало опыта работы с базами данных в общем, и с инструментами, вроде Hibernate, в частности. Матерые разработчики вряд ли найдут в статье для себя что-то новенькое; всех остальных прошу под кат.
Что такое ORM?
Ни одно современное веб-приложение не обходится без хранения огромного количества различной информации. Эту задачу обычно возлагают на специальные программы — Систему Управления Базами Данных СУБД. По схеме организации базы данных делятся на несколько видов, и так сложилось, что самым распространенным видом оказались реляционные.
В реляционных базах, данные организованны в виде сущностный (таблиц) и связей между ними. Программисты, работающие с объектно-ориентированными языками программирования, зачастую сталкиваются с задачей преобразования данных из формы, понятной СУБД в привычную объектную форму. Почти всегда решение этих задач занимает огромное количество времени и заставляет писать такие конструкции:
public ArrayList<BookBean> getBooksByGenreId (int genre_id)
{
ArrayList<BookBean> result = new ArrayList<>();
try
{
int i = 1;
String query = "SELECT * FROM books " +
"LEFT JOIN genres2books " +
"ON genres2books.book_id=books.id " +
"WHERE genre_id=? AND books.approved = 1 " +
"ORDER BY user_rating DESC LIMIT 250";
connection = getConnection();
ps = connection.prepareStatement(query);
ps.setInt(i++, genre_id);
resultSet = ps.executeQuery();
while (resultSet.next())
{
String name = resultSet.getString("name");
String summary = resultSet.getString("summary");
String cover_url = resultSet.getString("cover_url");
String cover_min_url = resultSet.getString("cover_min_url");
String example = resultSet.getString("example");
String isbn = resultSet.getString("isbn");
String foreign_id = resultSet.getString("foreign_id");
double rev_rating = resultSet.getDouble("rev_rating");
double usr_rating = resultSet.getDouble("user_rating");
int user_id = resultSet.getInt("user_id");
int id = resultSet.getInt("id");
int approved = resultSet.getInt("approved");
int top = resultSet.getInt("top");
int partner_id = resultSet.getInt("partner_id");
long sum_mark = resultSet.getLong("sum_mark");
long votes_num = resultSet.getLong("votes_num");
result.add( new BookBean(id, name, summary, cover_url, cover_min_url, example, rev_rating, usr_rating,
user_id, top, approved, sum_mark, votes_num, isbn, foreign_id, partner_id) );
}
} catch (SQLException | IllegalArgumentException e) {
e.printStackTrace();
} finally {
try {
if (ps!=null)
ps.close();
if (connection!=null)
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
И это только один SELECT, а ведь нужно еще и организовать правильное подключение к СУБД и обеспечить нормальную одновременную работу нескольких пользователей.
Облегчить жизнь программистам и освободить нас от рутины призвана технология Object-Relational Mapping (ORM), которую реализует популярная библиотека Hibernate. Hibernate берет на себя задачу преобразования данных их реляционного вида в объектный, для чтения, и из объектного вида в реляционный — для записи. Кроме того, библиотека позволяет легко настроить подключение к СУБД и с помощью нее очень легко управлять транзакциями.
Быстрый старт
Сейчас мы попробуем с помощью с помощью Hibernate создать отображение в объектную форму вот такой таблицы, которая используется для хранения сессий:
Создадим Bean SessionBean. Бин — это класс, у которого есть констурктор без параметров, конструктор со всеми параметрами и определены get- и set- методы для всех полей.
@Entity //аннотация, регистрирующая класс как сущность БД
@Table(name = "sessions") //связываем с конкретной таблицей по названию
public class SessionBean implements Serializable{
@Column(name = "username") //название таблицы в БД
private String username; //название поля класса
@Id //указывает на то, что следующее поле является ID и будет использоваться для поиска по умолчанию
@Column(name = "series")
private String series;
@Column(name = "token")
private String token;
@Column(name = "last_used")
private Timestamp lastUsed;
public SessionBean() {}
public SessionBean(String username, String series, String token, Timestamp lastUsed) {
this.username = username;
this.series = series;
this.token = token;
this.lastUsed = lastUsed;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSeries() {
return series;
}
public void setSeries(String series) {
this.series = series;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Timestamp getLastUsed() {
return lastUsed;
}
public void setLastUsed(Timestamp last_used) {
this.lastUsed = last_used;
}
}
Также создадим DAO (Data Access Object) — специальный класс, который будет обеспечивать для нас операции чтения и записи в базу данных
public class NEXEntityDAO<Bean>
{
protected final Class<Bean> typeParameterClass;
public NEXEntityDAO(Class<Bean> typeParameterClass)
{
this.typeParameterClass = typeParameterClass;
}
@Override
public void delete(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Bean del = (Bean) session.get(typeParameterClass, id);
session.delete(del);
session.getTransaction().commit();
if (session.isOpen()) {
session.close();
}
}
@Override
public ArrayList<Bean> getAll() {
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
String hql = String.format("from %s",typeParameterClass.getCanonicalName());
Query SQLQuery = session.createQuery(hql);
ArrayList<Bean> result = (ArrayList<Bean>) SQLQuery.list();
session.getTransaction().commit();
if (session.isOpen()) {
session.close();
}
return result;
}
public Bean getById(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Bean result = (Bean) session.get(typeParameterClass, id);
session.getTransaction().commit();
if (session.isOpen()) {
session.close();
}
return result;
}
@Override
public void update(Bean object) {
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.update(object);
session.getTransaction().commit();
if (session.isOpen()) {
session.close();
}
}
@Override
public void add(Bean object) {
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.save(object);
session.getTransaction().commit();
if (session.isOpen()) {
session.close();
}
}
@Deprecated
public void clear()
{
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
String hql = String.format("delete from %s",typeParameterClass.getCanonicalName());
Query query = session.createQuery(hql);
query.executeUpdate();
session.getTransaction().commit();
if (session.isOpen()) {
session.close();
}
}
}
После чего становится очень просто извлекать, редактировать и добавлять данные в БД.
NEXEntityDAO<SessionBean> sessionDAO = new NEXEntityDAO<>(SessionBean.class);
SessionBean session = sessionDAO.getById(5) //Получить сессию с ид = 5
ArrayList<SessionBean> allSessions = sessionDAO.getAll(); //получить список всех сессий
session.setToken(“21313”);
sessionDAO.update(session); //обновить существующую запись
SessionBean adding = new SessionBean(“st”,”ri”,”ng”,ts);
sessionDAO.add(adding); //добавить новую запись
На этом закончим знакомство с ORM. В следующих статьях мы рассмотрим наиболее интересные тонкости работы с этой технологией, а также приёмы тестирования баз данных.
Автор: x22a