О сайте Начало Java Студенческий отдел кадров Статьи Курсы по Java Вопросы/Ответы

Аннотации

Аннотации были введены в 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

Как видите здесь очень мало изменений. Мы просто изменили конфигуратор - теперь наш конфигуратор умеет работать с аннотациями.

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

<?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

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

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

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

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

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

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

  1. Постраничное получение данных. Основная идея в том, чтобы получать не полный список всех записей, а только ограниченное количество. Это крайне удобно при больших обхемах данных. Нередко пользователи вводят очень неудачный критерий поиска и размер данных становится просто огромный. При страничной организации вы отдаете только ограниченный набор данных и в качестве удобства общее количество. Если задать количество записей на странице, то путем несложных вычислений можно будет получить общее количество страниц и начальный номер записи для каждой страницы. Посмотрите внимательно на интерфейс org.hibernate.Query. У него есть методы setFirstResult и setMaxResults. Они и определяют то, что надо. Обратите внимание, что каждый SQL сервер имеет свои способы получения ограниченного количества записей. Так что оригинальный запрос SQL будет отличаться для ORACLE и для MySQL.
  2. HQL - Hibernate Query Language. Это язык, напоминающий обычный SQL, но для работы с объектами Hibernate. Язык достаточно мощный и вы не пожалеете время, которое потратили на его хорошее изучение. HQL предоставляет достаточно большое количетсво интересных и полезных функций, так что изучайте.
  3. Именованые запросы - NamedQuery. Когда вы создаете HQL запрос (а даже обычный запрос вроде FROM Subject уже является HQL-запросом), то Hibernate предпринимает некоторые действия для перевода HQL в SQL. Для запросов, которые можно заранее создать, удобно использовать NamedQuery. Такие запросы заранее "компилируются" в SQL и затраты времени сокращаются. Обратите внимание, что такие запросы могут использовать параметры. Т.е. запрос может быть "наполовину статическим"
  4. Наследование. Как я уже упоминал, Hibernate позволяет создавать иерархию классов, которая будет отображаться на одну или несколько таблиц. Для более подробного ознакомления обратитесь к документации. Интересным могут оказаться аннотации javax.persistence.Basic и javax.persistence.AttributeOverride. Это стандартные J2EE аннотации, которые позволяют определять разные названия столбцов в таблице для разных классов
  5. Аннотации. И еще раз об аннотациях - смотрите их список и изучайте. Они действительно достойны этого. Например Formula - позволяет для поля задать SQL-выражение, которое будет вычисляться. Или Filter, которая позволяет включать фильтр для отсечения неугодных записей. Подобное делает и Where. В общем читайте и старайтесь использовать их в своих проектах.
  6. Native SQL. Учтите, что Hibernate полностью заменить SQL не может. Он все-таки объектно ориентирован. Нередко встречаются задачи (особенно при создании крупных и сложных финансовых отчетов), которые требуют именно SQL. Hibernate позволяет вам использовать обычный SQL в этом случае. Это конечно сложнее, но если такая возможность есть - надо о ней знать
Собственно это все, о чем бы я хотел упомянуть в заключительной части по Hibernate. Теперь самое время приступить к изучению пакета, который помогает организовать бизнес-уровень. Встречайте - Часть 19 - Spring. Бизнес-уровень в действии.

Архив с исходными кодами: Исходный код