Повторяющийся набор полей в Hibernate

в 15:00, , рубрики: hibernate, java, Песочница, метки: ,

Повторяющийся набор полей в Hibernate
Проблему, решения которой я сегодня хотел бы описать — это повторяющийся набор полей в Hibernate сущностях. Конечно, её можно было бы решить с помощью нормализации БД, но это неудобно при выборках и влияет на быстродействие, лишние джойны ради нескольких колонок — никому не нужны.
Итак, представим, есть какая-то система учёта, в ней в любой сущности важно хранить историю, кто менял, кто создавал, когда были последние изменения, кем созданы. На самом деле в любом проекте можно найти подобные наборы и не один. В результате, когда программисты создают эти поля, в лучшем случае получается копипаст, а иногда рождаются новые названия для тех же полей.

Я хотел бы рассмотреть два способа решения этой задачи.

Первый способ

Эту задачу можно решить при помощи @Embeddable сущности:

package ru.kabit.entity.embeded;

import javax.persistence.Embeddable;
import java.util.Date;

@Embeddable
public class HistoryFields {
    private Long lastModifierId;
    private Long creatorId;
    private Date lastModifyDate;
    private Date createDate;

    /* getters and setters */
}

Убираем из класса выносимые поля, вставляем одно Embedded свойство. На практике пройтись по коду и добавить лишний геттер перед вызовом этих полей не составляет труда, а вот в XML, JSP найти все места бывает сложно.

package ru.kabit.entity;

import ru.kabit.entity.embeded.HistoryFields;

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Post {
    @Id
    @GeneratedValue
    private Long id;
    @Embedded
    private HistoryFields historyFields;

    /* getters and setters */
}

Если хоть одно поле из HistoryFields будет заполнено, то создастся объект HistoryFields и эти поля заполнятся, иначе вместо объекта будет лежать null, это кстати, очень удобно при написании логики. Имена полей в БД могут быть разными, чтобы их изменить используется аннотация @AttributeOverride.

Преимущества такого подхода:

  • Логическая группа полей выделена в отдельную сущность, поля всегда будут называться одинаково, никому не придёт в голову их написать «по правильному»
  • Выделенные данные выбираются без подзапросов, так как лежат в этой же таблице
  • Поисковые критерии по этим полям будут неизменными
  • Таких полей в сущности может быть несколько

Недостатки:

  • В некоторых случаях набор полей может быть излишним, убрать их не получится
  • При рефакторинге придётся изменять JSP руками, трудно это сделать в большом проекте, придётся всё перепроверять, либо сделать дополнительные геттеры, достающие данные из Embedded поля

Второй способ

Второе решение — использовать @MappedSuperclass аннотацию:

package ru.kabit.entity;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.util.Date;

@MappedSuperclass
public class HistoryEntity {
    @Id
    @GeneratedValue
    private Long id;
    private Long lastModifierId;
    private Long creatorId;
    private Date lastModifyDate;
    private Date createDate;

     /* getters and setters */
}

Теперь есть базовый класс с набором полей и когда мы видим похожий набор, можно просто отнаследоваться от него:

package ru.kabit.entity;

import javax.persistence.Entity;

@Entity
public class Table1 extends HistoryEntity {
    private Long otherFieldTable1;

    public Long getOtherFieldTable1() {
        return otherFieldTable1;
    }

    public void setOtherFieldTable1(Long otherFieldTable1) {
        this.otherFieldTable1 = otherFieldTable1;
    }
}

Преимущества такого подхода:

  • Множественного наследования в Java нет, поэтому если сущность подходит под два типа, то этот способ не подходит
  • Выделенные данные выбираются без подзапросов, так как лежат в этой же таблице
  • При вынесении полей на рабочем проекте, не придётся ничего рефакторить

Недостатки:

  • В некоторых случаях набор полей может быть излишним, убрать их не получится

Вывод

Я рассказал как избавиться от повторного описания набора полей в Hibernate сущностях, а также описал преимущества и недостатки каждого. Надеюсь, что эта статья окажется вам полезна.

Автор: kolegich

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js