Мапим 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.
Я хочу, чтоб ты это знал, но детально тут мы это все разбирать не будем. Думаю, тебе и этого хватит, чтобы голову поломать. Для любопытных могу оставить ссылку на официальную документацию.