Аннотации
Аннотации были введены в Java 5 и для них сразу нашлось куча применений. Ведь аннотация позволяет вам получить дополнительную информацию о любом объекте языка — классе, методе, поле. Конечно же сам создатель языка не мог пройти мимо и очень быстро был опубликован JPA — Java Persistance API (Java API для хранения). И он содержал много удобных и полезных аннотаций, которые взяли на вооружение многие популярные пакеты.
Не думаю, что нам потребуется для знакомства с аннтоациями описывать весь язык Java — думаю более важно, чтобы вы ухватили суть. Аннотация (и ее параметры) может быть прочитана в момент исполнения. Следовательно необходимая информация может быть записана прямо в файле.
Некоторую информацию по аннотациям вы можете найти в моей статье Что нового в Java SE 5.0
Само собой Hibernate тоже не остался в стороне. Теперь разработчик не должен создавать дополнительные XML-файлы — всю необходимую информацию он может описать прямо в файле с Entity. Что несомненно гораздо удобнее. Вы очень быстро привыкните — я привык к этому за 2-3 дня. Теперь описание в XML мне кажется просто кощунством :).
Зайдите на страницу Hibernate. Слева будет в меню, в котором мы выбираем пункт Download. В списке реализаций выбираем «Hibernate Annotations». В загруженном архиве будет список библиотек и документация, которую обязательно надо будет читать и пробовать использовать. Для текущего проекта нам понадобятся следующие библиотеки:
antlr-2.7.6.jar
commons-collections-3.1.jar
commons-logging.jar
dom4j-1.6.1.jar
ejb3-persistence.jar
hibernate-annotations.jar
hibernate-commons-annotations.jar
hibernate3.jar
javaee.jar
javassist-3.4.GA.jar
jta-1.1.jar
log4j-1.2.15.jar
mysqlJDBC-3.1.13.jar
slf4j-api-1.5.3.jar
slf4j-log4j12-1.5.3.jar
Изменения коснутся файлов с описаниями Entity — Profession, Subject, Applicant, ApplicantResult. Несколько изменится файлHibernateUtil.java. И наконец придется внести изменения в файл hibernate.cfg.xml. Это будет теперь единственный XML-файл в нашем проекте (до некоторого момента).
hibernateUtil.java
Как видите здесь очень мало изменений. Мы просто изменили конфигуратор — теперь наш конфигуратор умеет работать с аннотациями.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package students.utils; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } } |
Думаю, что здесь комменарии вряд ли потребуются — у нас произошла замена класса Configuration на AnnotaionConfiguration. В общем это все.
Теперь посмотрим файл hibernate.cfg.xml
1 2 3 4 5 6 7 8 9 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 |
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://127.0.0.1:3306/db_applicant</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Mapping files --> <mapping class="students.entity.Profession"/> <mapping class="students.entity.Applicant"/> <mapping class="students.entity.Subject"/> <mapping class="students.entity.ApplicantResult"/> </session-factory> </hibernate-configuration> |
Здесь мы видим небольшое изменение в форме записи зарегистрированных Entity — теперь мы пишем не XML-файлы, а сами классы.
И наконец начинается самое важное и интересное — мы посмотрим, как делать аннотации в классах. Для начала рассмотрим файл Applicant.java
1 2 3 4 5 6 7 8 9 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
package students.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name="applicant") public class Applicant { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="applicant_id") private Long applicantId; @ManyToOne(cascade= {CascadeType.REFRESH}, fetch=FetchType.LAZY) @JoinColumn(name="profession_id") private Profession profession; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy="applicant") private List<ApplicantResult> applicantResultList; @Column(name="first_name") private String firstName; @Column(name="last_name") private String lastName; @Column(name="middle_name") private String middleName; @Column(name="entrance_year") private Integer entranceYear; public Long getApplicantId() { return applicantId; } public void setApplicantId(Long applicantId) { this.applicantId = applicantId; } public List<ApplicantResult> getApplicantResultList() { return applicantResultList; } public void setApplicantResultList(List<ApplicantResult> applicantResultList) { this.applicantResultList = applicantResultList; } public Integer getEntranceYear() { return entranceYear; } public void setEntranceYear(Integer entranceYear) { this.entranceYear = entranceYear; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getMiddleName() { return middleName; } public void setMiddleName(String middleName) { this.middleName = middleName; } public Profession getProfession() { return profession; } public void setProfession(Profession profession) { this.profession = profession; } } |
Давайте рассмотрим все аннотации, которые встретились нам в данном файле. Надеюсь, вы знаете, что аннотации начинаются с симовола @.
Для начала рассмотрим аннотации для класса целиком.
- @Entity — говорит о том, что данный класс представляет собой сущность, которую надо сохранять в базе данных
- @Table — показывает, какая таблица используется для хранения
Теперь пройдем по всему списку полей и посмотрим их назначение. Сначала мы рассмотрим аннотации для первичного ключа — applicantId
- @Id — говорит о том, что данное поле является идентификатором
- @GeneratedValue — аннотация дает указание, что данная величина будет генериться, причем указаниеstrategy=GenerationType.IDENTITY говорит о том, что значение будет создано с помощью базы данных — точнее значение столбца, который связан с полем будет генерится с помощью базы данных.
- @Column — достаточно очевидная аннотация для указания имени столбца, с которым связано поле класса.
Посмотрим на аннотации для поля profession
- @ManyToOne — аннотация указывает, что поле указывает на другой класс, который связан с текущим классом связью многие-к-одному. Здесь также указывается правило каскада cascade= {CascadeType.REFRESH} — в упрощенном виде оно гласит «что сделали с тобой, то сделай и со связью». В нашем случае единственная операция, которая будет передаваться на класс Profession — это перечитывание. Все остальные операции не будут распространяться на связанный класс. Запись fetch=FetchType.LAZY указывает, что загрузка данного поля будет только в случае обращения к данному полю. Пока программа этого не делает, поле будет пустым.
- @JoinColumn — данная анотация по сути похожа на @Column. Разве что она указывает, что колонка к тому же является связующей
Ну и наконец поле applicantResultList
- @OneToMany — такая запись говорит о том, что поле служить для связи один-ко-многим (потому и список). Единственный параметр, который мы еще не рассмотрели — mappedBy=»applicant». Он указывает имя поля в классе ApplicantResult, которое будет указывать на «родителя» — на класс Applicant
Теперь можно посмотреть на реализацию класса ApplicantResult
1 2 3 4 5 6 7 8 9 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
package students.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "applicant_result") public class ApplicantResult { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "applicant_result_id") private Long applicantResultId; @ManyToOne(cascade = {CascadeType.REFRESH}, fetch = FetchType.LAZY) @JoinColumn(name = "applicant_id") private Applicant applicant; @ManyToOne(cascade = {CascadeType.REFRESH}, fetch = FetchType.LAZY) @JoinColumn(name = "subject_id") private Subject subject; @Column(name = "mark") private Integer mark; public Applicant getApplicant() { return applicant; } public void setApplicant(Applicant applicant) { this.applicant = applicant; } public Long getApplicantResultId() { return applicantResultId; } public void setApplicantResultId(Long applicantResultId) { this.applicantResultId = applicantResultId; } public Integer getMark() { return mark; } public void setMark(Integer mark) { this.mark = mark; } public Subject getSubject() { return subject; } public void setSubject(Subject subject) { this.subject = subject; } } |
Думаю, что здесь вы не встретите того, что мы не рассматривали ранее для класса Applicant. Осталось совсем немного — рассмотреть два классаProfession и Subject.
Начнем с класса Subject
1 2 3 4 5 6 7 8 9 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 52 53 54 55 56 57 |
package students.entity; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; @Entity @Table(name = "subject") public class Subject { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "subject_id") private Long subjectId; @Column(name = "subject_name") private String subjectName; @ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY) @JoinTable(name = "SPECIALITY_SUBJECT", joinColumns = @JoinColumn(name = "SUBJECT_ID"), inverseJoinColumns = @JoinColumn(name = "PROFESSION_ID")) private Set<Profession> professionList; public Set<Profession> getProfessionList() { return professionList; } public void setProfessionList(Set<Profession> professionList) { this.professionList = professionList; } public Long getSubjectId() { return subjectId; } public void setSubjectId(Long subjectId) { this.subjectId = subjectId; } public String getSubjectName() { return subjectName; } public void setSubjectName(String subjectName) { this.subjectName = subjectName; } } |
Здесь мы видим еще один вариант записи связи — много-ко-многим. Посмотрим на параметры:
- @ManyToMany — эта аннотация говорит о том, что поле будет использовано для связи много-ко-многим.
- @JoinTable — указывает имя таблицы, которая используется для организации связи много-ко-многим. Параметр joinColumns указывает название столбца, которые явялется ссылкой на текущий класс, т.е. на Subject (точнее ссылкой на таблицу SUBJECT). ПараметрinverseJoinColumns указывает на колонку, в которй находится ссылка на класс с другой стороны отношения — в данном случае этоProfession. Думаю, что при внимательном рассмотрении вы поймете, что и как
Ну а теперь мы можем посмотреть файл Profession.java
1 2 3 4 5 6 7 8 9 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 52 53 54 55 56 57 58 |
package students.entity; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; @Entity @Table(name = "profession") public class Profession { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "profession_id") private Long professionId; @Column(name = "profession_name") private String professionName; @ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY) @JoinTable(name = "SPECIALITY_SUBJECT", joinColumns = @JoinColumn(name = "PROFESSION_ID"), inverseJoinColumns = @JoinColumn(name = "SUBJECT_ID")) private Set<Subject> subjectList = new HashSet<Subject>(); public Long getProfessionId() { return professionId; } public void setProfessionId(Long professionId) { this.professionId = professionId; } public String getProfessionName() { return professionName; } public void setProfessionName(String professionName) { this.professionName = professionName; } public Set<Subject> getSubjectList() { return subjectList; } public void setSubjectList(Set<Subject> subjectList) { this.subjectList = subjectList; } } |
Здесь тоже приводится вариант отношения много-ко-многим. Предлагаю вам разобрать его самостоятельно.
Само собой мы не рассмотрели еще очень большое количество аннотаций, которые предлагает JPA и сам Hibernate — они заслуживают вашего внимания. Но подробное рассмотрение всех аннотаций не входит в планы автора. Напоследок я приведу код файлов, которые мы не меняли -StudentDAO.java и Main.java
StudentDAO.java
1 2 3 4 5 6 7 8 9 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
package students.dao; import java.util.List; import org.hibernate.Hibernate; import org.hibernate.Session; import students.entity.Applicant; import students.entity.Profession; import students.entity.Subject; import students.utils.HibernateUtil; public class StudentDAO { public Long addApplicant(Applicant applicant) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Long result = (Long) session.save(applicant); session.getTransaction().commit(); return result; } public void updateApplicant(Applicant applicant) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); session.saveOrUpdate(applicant); session.getTransaction().commit(); } public Applicant getApplicant(Long applicantId) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Applicant result = (Applicant) session.load(Applicant.class, applicantId); // Насильная инициализация списка. Не очень хорошая практика так делать Hibernate.initialize(result.getApplicantResultList()); session.getTransaction().commit(); return result; } public List<Applicant> findApplicant() { // Если поменять первую строку на вторую, то исключение о неинициализированной коллекции // в классе Main уйдет. Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); List<Applicant> result = session.createQuery("from Applicant order by lastName, firstName").list(); // Насильная инициализация списка. Не очень хорошая практика так делать for (Applicant a : result) { Hibernate.initialize(a.getProfession()); } session.getTransaction().commit(); return result; } public Long addProfession(Profession profession) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Long result = (Long) session.save(profession); session.getTransaction().commit(); return result; } public void updateProfession(Profession profession) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); session.saveOrUpdate(profession); session.getTransaction().commit(); } public Profession getProfession(Long professionId) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Profession result = (Profession) session.load(Profession.class, professionId); // Насильная инициализация списка. Не очень хорошая практика так делать Hibernate.initialize(result.getSubjectList()); session.getTransaction().commit(); return result; } public List<Profession> findProfession() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List<Profession> result = session.createQuery("from Profession order by professionName").list(); // Насильная инициализация списка. Не очень хорошая практика так делать for (Profession a : result) { Hibernate.initialize(a.getSubjectList()); } session.getTransaction().commit(); return result; } public Long addSubject(Subject subject) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Long result = (Long) session.save(subject); session.getTransaction().commit(); return result; } public void updateSubject(Subject subject) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); session.saveOrUpdate(subject); session.getTransaction().commit(); } public Subject getSubject(Long subjectId) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Subject result = (Subject) session.load(Subject.class, subjectId); // Насильная инициализация списка. Не очень хорошая практика так делать Hibernate.initialize(result.getProfessionList()); session.getTransaction().commit(); return result; } public List<Subject> findSubject() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List<Subject> result = session.createQuery("from Subject order by subjectName").list(); // Насильная инициализация списка. Не очень хорошая практика так делать for (Subject a : result) { Hibernate.initialize(a.getProfessionList()); } session.getTransaction().commit(); return result; } } |
Main.java
1 2 3 4 5 6 7 8 9 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
package students; import students.entity.Profession; import java.util.List; import students.dao.StudentDAO; import students.entity.Applicant; import students.entity.Subject; public class Main { public static void main(String[] args) { StudentDAO dao = new StudentDAO(); // Добавление новых предметов Subject subject = new Subject(); subject.setSubjectName("Mathematics"); dao.addSubject(subject); subject = new Subject(); subject.setSubjectName("Chemistry"); dao.addSubject(subject); subject = new Subject(); subject.setSubjectName("Logic"); dao.addSubject(subject); System.out.println("List of SUBJECTS"); System.out.println("----------------"); List<Subject> sbList = dao.findSubject(); // В списке вы увидите, что предметы пока не привязаны к профессиям - количество = 0 for (Subject a : sbList) { System.out.println(a.getSubjectId() + ":" + a.getSubjectName() + ". Number of profession:" + a.getProfessionList().size()); } // Теперь добавим профессии Profession profession = new Profession(); profession.setProfessionName("Programmer"); // Список предметов, которые надо сдавать для этой профессии // Обратите внимание, что в классе Profession мы создаем пустой список // чтобы не было NullPointerException profession.getSubjectList().add(sbList.get(0)); profession.getSubjectList().add(sbList.get(2)); dao.addProfession(profession); profession = new Profession(); profession.setProfessionName("Biologist"); profession.getSubjectList().add(sbList.get(1)); profession.getSubjectList().add(sbList.get(2)); // Получим профессию по ID и добавим еще один предмет для сдачи Long id = dao.addProfession(profession); profession = dao.getProfession(id); profession.getSubjectList().add(sbList.get(0)); dao.updateProfession(profession); // Смотрим список профессий System.out.println(); System.out.println("List of PROFESSIONS"); System.out.println("-------------------"); List<Profession> prList = dao.findProfession(); for (Profession a : prList) { System.out.println(a.getProfessionId() + ":" + a.getProfessionName()); } System.out.println(); System.out.println("List of SUBJECTS"); System.out.println("----------------"); sbList = dao.findSubject(); // В списке вы увидите, что предметы теперь привязаны к профессиям - количество > 0 for (Subject a : sbList) { System.out.println(a.getSubjectId() + ":" + a.getSubjectName() + ". Number of profession:" + a.getProfessionList().size()); } // А теперь создадим новых абитуриентов Applicant applicant = new Applicant(); applicant.setFirstName("John"); applicant.setMiddleName("M"); applicant.setLastName("Danny"); // Задаем профессию applicant.setProfession(prList.get(0)); applicant.setEntranceYear(2009); dao.addApplicant(applicant); applicant = new Applicant(); applicant.setFirstName("Poul"); applicant.setMiddleName("H"); applicant.setLastName("Tride"); // Задаем профессию applicant.setProfession(prList.get(1)); applicant.setEntranceYear(2009); dao.addApplicant(applicant); System.out.println(); System.out.println("List of APPLICANTS"); System.out.println("------------------"); List<Applicant> apList = dao.findApplicant(); for (Applicant a : apList) { System.out.println(a.getFirstName() + ":" + a.getLastName() + " - " + a.getProfession().getProfessionName()); // Если убрать комментарий, то получим сообщене об ошибке - коллекция не инициализирована // Но еще можно посмотреть комментарий в StudentDAO (метод findApplicant()). //System.out.println(a.getProfession().getSubjectList().size()); } } } |
Как видите, теперь вам не надо создавать кучу XML-файлов. Кроме того, вы сразу можете видеть, какое поле с каким столбцом в какой таблице связано, что очень упрощает разработку. Вот в принципе и все, что я хотел рассказать об аннотациях Hibernate.
Что дальше ?
Конечно же мы рассмотрели очень немного из того, что предоставляет программисту Hibernate. Далее мы не будем рассматривать какие-либо аспекты этого пакета, хотя нам могут встречаться более сложные примеры или просто другие примеры использования данного пакета. Прежде чем закончить обозрение, я бы хотел перечислить те области, которые есть смысл проработать самим по документации
- Постраничное получение данных. Основная идея в том, чтобы получать не полный список всех записей, а только ограниченное количество. Это крайне удобно при больших обхемах данных. Нередко пользователи вводят очень неудачный критерий поиска и размер данных становится просто огромный. При страничной организации вы отдаете только ограниченный набор данных и в качестве удобства общее количество. Если задать количество записей на странице, то путем несложных вычислений можно будет получить общее количество страниц и начальный номер записи для каждой страницы. Посмотрите внимательно на интерфейс org.hibernate.Query. У него есть методы setFirstResult иsetMaxResults. Они и определяют то, что надо. Обратите внимание, что каждый SQL сервер имеет свои способы получения ограниченного количества записей. Так что оригинальный запрос SQL будет отличаться для ORACLE и для MySQL.
- HQL — Hibernate Query Language. Это язык, напоминающий обычный SQL, но для работы с объектами Hibernate. Язык достаточно мощный и вы не пожалеете время, которое потратили на его хорошее изучение. HQL предоставляет достаточно большое количетсво интересных и полезных функций, так что изучайте.
- Именованые запросы — NamedQuery. Когда вы создаете HQL запрос (а даже обычный запрос вроде FROM Subject уже является HQL-запросом), то Hibernate предпринимает некоторые действия для перевода HQL в SQL. Для запросов, которые можно заранее создать, удобно использовать NamedQuery. Такие запросы заранее «компилируются» в SQL и затраты времени сокращаются. Обратите внимание, что такие запросы могут использовать параметры. Т.е. запрос может быть «наполовину статическим»
- Наследование. Как я уже упоминал, Hibernate позволяет создавать иерархию классов, которая будет отображаться на одну или несколько таблиц. Для более подробного ознакомления обратитесь к документации. Интересным могут оказаться аннотации javax.persistence.Basic иjavax.persistence.AttributeOverride. Это стандартные J2EE аннотации, которые позволяют определять разные названия столбцов в таблице для разных классов
- Аннотации. И еще раз об аннотациях — смотрите их список и изучайте. Они действительно достойны этого. Например Formula — позволяет для поля задать SQL-выражение, которое будет вычисляться. Или Filter, которая позволяет включать фильтр для отсечения неугодных записей. Подобное делает и Where. В общем читайте и старайтесь использовать их в своих проектах.
- Native SQL. Учтите, что Hibernate полностью заменить SQL не может. Он все-таки объектно ориентирован. Нередко встречаются задачи (особенно при создании крупных и сложных финансовых отчетов), которые требуют именно SQL. Hibernate позволяет вам использовать обычный SQL в этом случае. Это конечно сложнее, но если такая возможность есть — надо о ней знать
Собственно это все, о чем бы я хотел упомянуть в заключительной части по Hibernate. Теперь самое время приступить к изучению пакета, который помогает организовать бизнес-уровень. Встречайте — Часть 19 — Spring. Бизнес-уровень в действии.
Архив с исходными кодами: Исходный код