Мапим enum

Как мапить примитивные типы данных мы уже разобрались: используем аннотации @Column и аннотацию @Type. Но не все случаи можно покрыть этими аннотациями. И самый частый случай – это enum.

Java-объекты типа enum могут храниться в базе в двух вариантах:

  • в виде числа
  • в виде строки

Давай напишем небольшой пример, где у пользователя будет любимый цвет, который задается с помощью enum.


enum Color {
   RED,
   ORANGE,
   YELLOW,
   GREEN,
   BLUE,
   VIOLET
}

И добавим поле с цветом в класс User:


@Entity
@Table(name="user")
class User
{
   @Column(name="id")
   public Integer id;
 
   @Column(name="favorite_color")
   public Color favoriteColor;
 
   @Column(name="created_date")
   public Date createdDate;
}

Если мы хотим, чтобы Hibernate сохранял тип Color в базу как числа, то нужно полю favoriteColor добавить аннотацию:


@Enumerated(EnumType.ORDINAL)

Если мы хотим, чтобы значения хранились как строки, то нужно добавить аннотацию:


@Enumerated(EnumType.STRING)

Пример:


@Entity
@Table(name="user")
class User
{
   @Column(name="id")
   public Integer id;
 
   @Enumerated(EnumType.ORDINAL) //значение будет сохранено в базу как число
   @Column(name="favorite_color")
   public Color favoriteColor;
 
   @Column(name="created_date")
   public Date createdDate;
}

Мапим Boolean

Второй полезный сценарий – это маппинг типа Boolean. Так уж исторически сложилось, что в SQL нет своего типа данных для Boolean и вместо него там используют все что угодно.

Три самых распространенных варианта:

  • 1 или 0
  • ‘F’ или ‘T’
  • ‘Y’ или ‘N’

В общем, если ты будешь проектировать свою базу, то лучше сразу пиши тип BIT. Ну а полный маппинг для него будет выглядеть так:


	@Column(name = "is_correct", columnDefinition = "BIT")
	@Type(type = "org.hibernate.type.NumericBooleanType")
    private Boolean isCorrect;

Ну, а если базу данных проектируешь не ты, то смотри таблицу выше и думай, как правильно замапить нужные тебе типы.

Вычисляемые поля

Иногда количество полей в Entity-классе и количество колонок в таблице не совпадают. У этого может быть несколько причин.

Самая распространенная – это когда в нашем Entity-классе есть какое-то поле, которое мы не хотим сохранять в базу данных. С этим все понятно – просто добавь такому полю аннотацию @Transient и Hibernate проигнорирует его при работе с базой данных.

Пример:


@Entity(name = "Square")
public class Square {
           	@Id
           	public Long id;
 
           	public Integer width;
 
           	public Integer height;
 
           	@Transient
           	public Integer total;
}

Это хороший вариант, но при чтении объекта из базы поле total будет null. А нам бы хотелось, чтобы оно содержало произведение width*height. Такое тоже можно сделать в Hibernate. Для этого есть специальная аннотация @Formula.


@Entity(name = "Square")
public class Square {
           	@Id
           	public Long id;
 
           	public Integer width;
 
           	public Integer height;
 
           	@Formula(value = " width * height ")
          	public Integer total;
}

Такое поле не будет сохраняться в базу данных, а при чтении объекта из базы в него будет записываться значение вычисленное по формуле.

SQL-запрос будет выглядеть примерно так:

 SELECT id, width, height, (width* height) AS total FROM Square;

@Embedded

Еще одна полезная аннотация – это @Embedded. Она позволяет рассматривать поля дочернего объекта как поля самого Entity-класса.

Допустим, у тебя есть класс User и ты решил добавить в него адрес:


@Entity
@Table(name="user")
class User
{
   @Column(name="id")
    public Integer id;
 
   @Column(name="user_address_country")
   public String country;
   @Column(name="user_address_city")
   public String city;
   @Column(name="user_address_street")
   public String street;
   @Column(name="user_address_home")
   public String home;
 
   @Column(name="created_date")
    public Date createdDate;
}

Все вроде бы хорошо, но с точки зрения Java было бы логично вынести адрес в отдельный класс. Адрес все-таки отдельная сущность. Но как это сделать, если в базе вся эта информация хранится именно в таблице user?

Нам поможет аннотация @Embedded. Сначала мы создадим класс UserAddress и вынесем в него всю информацию по адресу пользователя:


@Embeddable
class UserAddress
{
   @Column(name="user_address_country")
   public String country;
   @Column(name="user_address_city")
   public String city;
   @Column(name="user_address_street")
   public String street;
   @Column(name="user_address_home")
   public String home;
}

А затем используем поле этого класса в нашем классе User:


@Entity
@Table(name="user")
class User
{
   @Column(name="id")
   public Integer id;
 
   @Embedded
   public UserAddress address;
 
   @Column(name="created_date")
   public Date createdDate;
}

Благодаря аннотации @Embedded во время сохранения объекта Hibernate поймет, что поля класса UserAddress нужно обрабатывать как поля самого класса User.

Важно! Если ты решишь добавить в свой класс User два поля UserAddress, то использовать @Embedded уже не получится: у тебя будет дубликат полей и тебе нужно будет как-то их разделить. Делается это с помощью переопределения аннотаций: с помощью аннотации @AttributeOverrides.

Я хочу, чтоб ты это знал, но детально тут мы это все разбирать не будем. Думаю, тебе и этого хватит, чтобы голову поломать. Для любопытных могу оставить ссылку на официальную документацию.

undefined
1
Задача
Модуль 4. Работа с БД, 12 уровень, 1 лекция
Недоступна
Создаём Entity из класса
Если ранее не подключал зависимости, то подключи их. Для этого Alt + Ctrl + Shift + S (в Идее), вкладка Libraries. Зависимости можно скачать здесь: https://javarush.com/downloads/ide/javarush/hibernate.zip Архив распакуй, и каждую зависимость добавь к модулю. Эта часть задания не проверяется, но если ее не выполнить, ты не сможешь локально выполнять код.
undefined
1
Задача
Модуль 4. Работа с БД, 12 уровень, 1 лекция
Недоступна
Entity с вычислением
Если ранее не подключал зависимости, то подключи их. Для этого используй Alt + Ctrl + Shift + S (в Идее), вкладка Libraries. Зависимости можно скачать здесь: https://javarush.com/downloads/ide/javarush/hibernate.zip Архив распакуй, и каждую зависимость добавь к модулю. Эта часть задания не проверяется, но если ее не выполнить, ты не сможешь локально выполнять код.