Статьи

Руководство для начинающих по Hibernate Types

Основные картографические концепции

При изучении Hibernate многим нравится переходить к родительско-дочерним ассоциациям, не осваивая основы сопоставления объектных отношений. Очень важно понять основные правила отображения для отдельных сущностей перед началом моделирования связей сущностей.

Спящие типы

Тип Hibernate — это мост между типом SQL и примитивом Java / типом Object.

Это типы, которые Hibernate поддерживает по умолчанию:

Тип гибернации (org.hibernate.type) Тип JDBC Тип Java
StringType VARCHAR строка
MaterializedClob CLOB строка
TextType LONGVARCHAR строка
CharacterType CHAR символ или персонаж
BooleanType НЕМНОГО логическое или логическое
NumericBooleanType INTEGER (например, 0 = ложь и 1 = истина) логическое или логическое
YesNoType CHAR (например, «N» или «n» = false и «Y» или «y» = true) логическое или логическое
TrueFalseType CHAR (например, «F» или «f» = false и «T» или «t» = true) логическое или логическое
ByteType TINYINT байт или байт
ShortType SMALLINT короткий или короткий
IntegerType INTEGER int или Integer
LongType BIGINT длинный или длинный
FloatType FLOAT плавать или плавать
DoubleType DOUBLE двойной или двойной
BigIntegerType NUMERIC BigInteger
BigDecimalType NUMERIC BigDecimal
TimestampType TIMESTAMP java.sql.Timestamp или java.util.Date
TimeType ВРЕМЯ java.sql.Time
DateType ДАТА java.sql.Date
CalendarType TIMESTAMP java.util.Calendar или java.util.GregorianCalendar
CalendarType ДАТА java.util.Calendar или java.util.GregorianCalendar
Тип валюты VARCHAR java.util.Currency
LocaleType VARCHAR java.util.Locale
TimeZoneType VARCHAR java.util.TimeZone
UrlType VARCHAR java.net.URL
ClassType VARCHAR java.lang.Class
BlobType большой двоичный объект java.sql.Blob
ClobType CLOB java.sql.Clob
BinaryType VARBINARY байт [] или байт []
BinaryType большой двоичный объект байт [] или байт []
BinaryType LONGVARBINARY байт [] или байт []
BinaryType LONGVARBINARY байт [] или байт []
CharArrayType VARCHAR символ [] или символ []
UUIDBinaryType BINARY java.util.UUID
UUIDBinaryType CHAR или VARCHAR java.util.UUID
UUIDBinaryType PostgreSQL UUID java.util.UUID
SerializableType VARBINARY Сериализуемый

Вы всегда можете определить свои собственные пользовательские типы, как мы увидим в следующей статье.

Типы встраиваемых (он же компонент)

Вы можете сгруппировать несколько столбцов для определенного типа Java, который можно повторно использовать в модели вашего домена. Если сопоставленный Java-объект всегда зависит от некоторого внешнего объекта, вы можете выбрать тип Embeddable для такого сопоставления модели домена.

Встраиваемый объект может содержать как базовые типы, так и сопоставления ассоциаций, но он никогда не может содержать @Id. Объект Embeddable сохраняется / удаляется вместе со своим владельцем.

Предполагая, что у нас есть следующая таблица SQL:

1
2
3
4
5
6
7
8
CREATE TABLE entity_event
  (
     id           BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
     entity_class VARCHAR(255),
     entity_id    BIGINT,
     message      VARCHAR(255),
     PRIMARY KEY (id)
  );

Мы могли бы сгруппировать entity_class и entity_id в объект Embeddable, который мы будем использовать в двух разных сущностях-владельцах.

Объект Embeddable выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Embeddable
public class EntityIdentifier implements Serializable {
 
    @Column(name = "entity_id", nullable = true)
    private Long entityId;
 
    @Column(name = "entity_class", nullable = true)
    private Class entityClass;
 
    public EntityIdentifier() {
    }
 
    public EntityIdentifier(Class entityClass, Long entityId) {
        this.entityClass = entityClass;
        this.entityId = entityId;
    }
 
    public Class getEntityClass() { return entityClass; }
 
    public void setEntityClass(Class entityClass) { this.entityClass = entityClass; }
 
    public Long getEntityId() { return entityId; }
 
    public void setEntityId(Long entityId) { this.entityId = entityId; }
}

Связанная таблица сущностей будет наследовать связанные свойства столбцов Embeddable.

сущность

Entity — это Java-эквивалент строки таблицы SQL. Сущность должна содержать свойство @Id, сопоставляющее связанный первичный ключ таблицы.

Логика приложения вносит изменения в свойства сущностей и уведомляет об изменениях состояния постоянства контекста сущностей (сохраняются, объединяются, удаляются). Поэтому постоянный контекст будет переводить все изменения сущностей в операторы SQL.

Предполагая, что у нас есть следующие таблицы SQL:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
CREATE TABLE entity_attribute
  (
     id           BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
     entity_class VARCHAR(255),
     entity_id    BIGINT,
     name         VARCHAR(255),
     VALUE        VARCHAR(255),
     PRIMARY KEY (id)
  );
 CREATE TABLE entity_event
  (
     id           BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
     entity_class VARCHAR(255),
     entity_id    BIGINT,
     message      VARCHAR(255),
     PRIMARY KEY (id)
  );

Мы можем использовать тип EntityIdentifier Embeddable, поскольку обе таблицы содержат столбцы entity_class и entity_id.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Entity
@Table(name = "entity_attribute")
public class EntityAttribute {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String name;
 
    private String value;
 
    private EntityIdentifier entityIdentifier;
 
    public Long getId() { return id; }
 
    public String getName() { return name; }
 
    public void setName(String name) { this.name = name; }
 
    public String getValue() { return value; }
 
    public void setValue(String value) { this.value = value; }
 
    public EntityIdentifier getEntityIdentifier() { return entityIdentifier; }
 
    public void setEntityIdentifier(EntityIdentifier entityIdentifier) { this.entityIdentifier = entityIdentifier; }
}
 
@Entity
@Table(name = "entity_event")
public class EntityEvent {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String message;
 
    private EntityIdentifier entityIdentifier;
 
    public Long getId() { return id; }
 
    public String getMessage() { return message; }
 
    public void setMessage(String message) { this.message = message; }
 
    public EntityIdentifier getEntityIdentifier() { return entityIdentifier; }
 
    public void setEntityIdentifier(EntityIdentifier entityIdentifier) { this.entityIdentifier = entityIdentifier; }
}

Время тестирования

Мы создадим один EntityEvent и один EntityAttribute для данного Продукта, чтобы увидеть, как встраиваемый объект сохраняется вместе с владельцами объектов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Test
public void testEntityIdentifier() {
    doInTransaction(new TransactionCallable<Void>() {
        @Override
        public Void execute(Session session) {
            Product product = new Product("LCD");
            session.persist(product);
            EntityEvent productEvent = new EntityEvent();
            productEvent.setMessage(String.format("Product %s added", product.getName()));
            productEvent.setEntityIdentifier(new EntityIdentifier(
                    product.getClass(),
                    product.getId()
            ));
            session.persist(productEvent);
            EntityAttribute productAttribute = new EntityAttribute();
            productAttribute.setName("AD_CAMPAIGN");
            productAttribute.setValue("LCD_Sales");
            productAttribute.setEntityIdentifier(new EntityIdentifier(
                    product.getClass(),
                    product.getId()
            ));
            session.persist(productAttribute);
            assertSame(1, session.createQuery("select ea from EntityAttribute ea where ea.entityIdentifier = :entityIdentifier")
                    .setParameter("entityIdentifier", new EntityIdentifier(product.getClass(), product.getId()))
                    .list().size());
            return null;
        }
    });
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Query:{[
INSERT INTO product
            (id,
             name)
VALUES      (DEFAULT,
             ?) 
][LCD]}
 
Query:{[
INSERT INTO entity_event
            (id,
             entity_class,
             entity_id,
             message)
VALUES      (DEFAULT,
             ?,
             ?,
             ?) 
][com.vladmihalcea.hibernate.masterclass.laboratory.entityidentifier.Product,1,Product LCD added]}
 
Query:{[
INSERT INTO entity_attribute
            (id,
             entity_class,
             entity_id,
             name,
             VALUE)
VALUES      (DEFAULT,
             ?,
             ?,
             ?,
             ?) 
][com.vladmihalcea.hibernate.masterclass.laboratory.entityidentifier.Product,1,AD_CAMPAIGN,LCD_Sales]}
 
Query:{[
SELECT entityattr0_.id           AS id1_0_,
       entityattr0_.entity_class AS entity_c2_0_,
       entityattr0_.entity_id    AS entity_i3_0_,
       entityattr0_.name         AS name4_0_,
       entityattr0_.VALUE        AS value5_0_
FROM   entity_attribute entityattr0_
WHERE  entityattr0_.entity_class = ?
       AND entityattr0_.entity_id = ? 
][com.vladmihalcea.hibernate.masterclass.laboratory.entityidentifier.Product,1]}

Вывод

Есть еще много понятий, которые мы должны охватить, прежде чем понять ассоциации сущностей. Прежде чем переходить к более сложным темам, вы всегда должны уделять время пониманию основных понятий. Мой следующий пост будет посвящен идентификаторам сущностей и всем доступным методам генерации.

  • Код доступен на GitHub .