Как я уже говорил выше, по умолчанию имя
таблицы совпадает с именем сущности.
Если вам нужно, переопределить имя
таблицы или схему базы данных, то нужно использовать аннотацию @Table(name = "Persons", schema="test") , атрибут name описывает имя таблицы, атрибут schema задает имя схемы базы данных. Каждое поле таблицы можно соотнести с полем сущности, при
этом используется аннотация @Column().
Рассмотрим основные атрибуты этой
аннотации. Атрибут name - задает имя
колонки в Таблице базы данных, атрибут unique
, если ему присвоить значение true, то поле будет уникальным, и по этому полю будет
создан уникальный индекс, если атрибуту nullable выставить значение в false,
то поле станет обязательно для ввода,
т.е. создается как поле со значением not
null, атрибут length задает размерность поля, допустим для строки, количество символов
равным 500 символов, смотрим на листинг:
@Column(name = "MyName", unique = true, nullable = false, length
= 500)
private String name;
предыдущий код hibernate транслирует в следующий sql:
create table test.Prersons (… , MyName varchar(500) not null unique, …).
Хотелось бы затронуть еще один очень
интересный атрибут columnDefinition, если ему присвоить значение, то это значение будет перенесено в sql
после имени поля без каких либо изменений. При создании таблицы, этим атрибутом вы можете присвоить полю
специфический тип данных. Предыдущий листинг примера можно переписать так:
@Column(name = "MyName", unique = true, nullable = false,
length = 500, columnDefinition = "varchar(40)")
private String name;
при этом hibernate сгенерирует следующий sql код создания таблицы:
create table test.Prersons (… , MyName varchar(40) not null unique,
…)
И так, мы подошли к вопросу, а какие
типы данных можно использовать в описании полей сущности? В документации написано, что можно
использовать все примитивные типы языка java: byte,
int, short, long,
boolean, char, float,
double, их обертки плюс к ним два типа BigInteger, BigDecimal, массивы байтов и символов: byte[], Byte[], char[], Character[],
cтроки String, временные типы данных:
java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp, перечисления enum, и любые сериализуемые объекты, реализующие
интерфейс Serializable. Допустим,
есть у нас большой массив данных, например картинка, её, нужно сохранять как массив байтов, для этого есть у нас дополнительная аннотация @Lob
, которая говорит нам, что поле сущности
является большим бинарным объектом (LOB). И предположим, что у нашей картинки размер в
несколько мегабайт. Что бы постоянно при считывании не подгружать картинку в
сущность, придумали такое понятие как ленивая загрузка. Если к полю применена аннотация @Basic(fetch
= FetchType.LAZY), то LOB-объект загружается, тогда, когда он
непосредственно запрашивается методом доступа .get , при этом hibernate выполняет отдельный sql запрос к базе данных, который подгружает нам значение поля сущности. Будьте осторожны, применяя отложенную загрузку (Lazy) т.к. при этом
hibernate
будет генерировать очень много sql запросов к базе данных. На самом деле ленивую загрузку поля сущности hibernate по умолчанию игнорирует, и нужно сделать несколько
дополнительных настроек, для того чтобы включить её. Первое прописать дополнительные
свойства в файле настроек hibernate.cfg.xml как сказано в
документации по hibernate:
<property
name="hibernate.bytecode.provider">javassist</property>
<property name="hibernate.bytecode.use_reflection_optimizer">true</property>
Первым свойством мы описываем, какая библиотека будет нам создавать прокси
объекты. Что бы поле грузилось
лениво, hibernate надо
как-то перехватывать обращения к нему.
Это делается через прокси, двумя
способами. Либо через Java Proxy, если у класса определен интерфейс, либо через javassist,
который создает прокси, модифицируя байткод объекта. Мы пойдем вторым путем. Так вы можете
спросить, зачем нам создавать прокси
объекты? Задача Hibernate загрузить LOB поле только тогда когда кто-то вызовет
геттер. Геттеры нужны для тупого
возвращения значения поля. Но hibernate нужно в геттер метод впихнуть логику по загрузке этого поля из
базы данных. Значит, он должен
переопределить геттер метод сущности, и
значит, он нам возвратит не чистый объект сущности, а его фантом, заглушку - которая называется proxy объектом.
Вот тут на помощь и приходит javassist, который может модифицировать байткод объекта
сущности. В документации hibernate сказано, что бы работала ленивая загрузка
поля, нужно выполнить задачу сборщика ant - instrument к классам
сущности. Поясню, кто не знает что такое ant - это инструмент сборки проектов, появился задолго до появления maven. Для
выполнения этой инструкции при сборке проекта maven, применим специальный
плагин maven-antrun-plugin который может
вызывать задачи ant, в
нем задаем, что при компиляции классов к
ним нужно применить специальную сборку, в виде задачи ant – instrument, смотрите листинг:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<tasks>
<taskdef name="instrument"
classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath>
<path
refid="maven.runtime.classpath"/>
<path
refid="maven.plugin.classpath"/>
<path
refid="maven.compile.classpath"/>
</classpath>
</taskdef>
<instrument verbose="true">
<fileset
dir="${project.build.outputDirectory}">
<include
name="**/**.class"/>
<exclude
name="**/I**.class"/>
</fileset>
</instrument>
</tasks>
</configuration>
</plugin>
Хочу рассказать об ошибке,
которую часто совершают программисты, они в своих сущностях применяют к LOB полям метод
доступа, называемый property access.
Я повторюсь, это когда аннотации применяются к методам геттера или сеттера поля, при
этом hibernate следуя
заложенной логике, тупо использует геттеры и сеттеры сущности, и переопределение метода геттера не
происходит, и из чего следует что, hibernate не может создать proxy объект для вас. Так если вы применяете,
метод доступа field access к LOB
полю, у вас будет переопределён метод .getText() и будет работать ленивая загрузка по полю, смотрите листинг:
@Entity
public class Person implements Serializable {
…..
@Basic(fetch =
FetchType.LAZY) @Column(name =
"MyText",length = 20000,nullable = false)
@Lob private byte[] text;
public byte[] getText() {
return
text;
}
……
}
А если примените метод
доступа property access, то метод
.getText() не будет переопределен, и
ленивая загрузка не будет работать, смотрим листинг:
@Entity
public class Person implements Serializable {
…..
private byte[] text;
@Basic(fetch =
FetchType.LAZY) @Column(name =
"MyText",length = 20000,nullable = false)
@Lob
public byte[] getText() {
return text;
}
……
}
Давайте перейдем к временным типам,
как же правильно их маппить/описывать в
сущности. Как я говорил выше hibernate может, работать с временными типами данных ( java.util.Date, java.util.Calendar,
java.sql.Date, java.sql.Time, java.sql.Timestamp), но JDBC
драйвер
имеет дело только с тремя
типами дат: java.sql.Date, java.sql.Time, java.sql.Timestamp. Поэтому
если вы используете один из типов
даты java.util.Date или java.util.Calendar, вам
желательно дать подсказку hibernate, в какой
тип данных JDBC
транслировать дату, при взаимодействии с базой
данных посредством JDBC
драйвера.
Помогает нам в этом аннотация @Temporal с параметром value. Этот параметр может принимать одно из трех значений
перечисления TemporalType: DATE, TIME, TIMESTAMP, которые
соответствуют java.sql типам:
…
@Temporal(value = TemporalType. DATE)
private Calendar birthDate;
…
Хочу отметить, что hibernate
не выполняет приведение типа дата автоматически, т.е. в нашем случае birthDate хранит и
дату и время, и только после сохранения и повторной загрузки время будет
усечено. Осталась одна важная вещь, про
которую необходимо упомянуть в этой части – это ”временные” поля, т.е. поля которые не мапятся и не сохраняются
в базе данных. Допустим, нужно нам
загрузить в поле, строковой ресурс в
зависимости от выбранной локали, хранить
такие поля лучше всего во временном поле.
Объявить поле временным можно
двумя способами. С помощью аннотации
@Transient() или с помощью конструкции
языка java, служебного слова transient.
…
@Transient()
private String name;
…
Добрый день!
ОтветитьУдалитьПодскажите, а если необходимо создать таблицу, в которой определенное поле должно быть индексируемым, т.е. что должно быть написано в классе сущности, чтобы это было аналогично sql выражению -->
alter table [name_table] add key name_index ([name_field](512));