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

Hibernate. Запись в виде XML-файлов

В этой части мы рассморим готовый пример с реализацией раличных видов отношений между сущностями. Советую еще раз посмотреть на структуру базы данных - Часть 15 - Новая структура данных
Как видите, мы будем использовать разные виды отношений, несмотря на невзрачность нашего проекта. Для начала мы приведем код классов, в которых будет хранится информация из таблиц. Они все имеют достаточно одинаковое строение - у них есть поля и методы set/get для обращения к этим полям.

Applicant.java

package students.entity;

import java.util.List;

public class Applicant {

    private Long applicantId;
    private Profession profession;
    private List<ApplicantResult> applicantResultList;
    private String firstName;
    private String lastName;
    private String middleName;
    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;
    }
}
        

В этом классе нам надо обратить внимание на два поля:

  • Первое поле Profession profession показывает нам, что у класса Applicant существует связь/отношение с классом Profession. И это отношение много-к одному. Т.е. существует много абитуриентов, у которых будет одна и та же специальность. Также такое отношение показывает, что абитуриент может поступать только на одну специальность. Я понимаю, что в реальности может быть и не так, но не забывайте, что это учебная задача.
  • Второе поле List<ApplicantResult> applicantResultList показывает другое отношение - один-ко-многим. Т.е. у одного абитуриента существует много сданных предметов. Точнее может быть 0 и больше.
Конечно же просто такая запись не дает нам информации, из какой таблицы надо брать данные для самого класса и как формируются отношения между объектами разных классов. Для выяснения этого вопроса нам надо посмотреть на файл Applicant.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="students.entity">
    <class name="Applicant" table="applicant">
        <id name="applicantId" column="applicant_id">
            <generator class="native"/>
        </id>
        <many-to-one name="profession" column="profession_id" class="students.entity.Profession"/>
        <bag name="applicantResultList" inverse="true" cascade="all-delete-orphan">
            <key column="APPLICANT_ID"></key>
            <one-to-many class="ApplicantResult"/>
        </bag>
        <property name="firstName" column="first_name"/>
        <property name="lastName" column="last_name"/>
        <property name="middleName" column="middle_name"/>
        <property name="entranceYear" column="entrance_year"/>
    </class>
</hibernate-mapping>
        

Здесь уже есть на что посмотреть. Понятно, что простые тэги property нам уже знакомы. Но появились новые.
Давайте сначала посмотрим на тэг <many-to-one name="profession" column="profession_id">. Как видите, он содержит несколько атрибутов, которые дают информацию о том, как осуществляется отношение класса Applicant с классом Profession. Атрибут name говорит о том, что за поле использутеся для ссылки на класс Profession. Атрибут column показывает поле в таблице APPLICANT, которое служит ссылкой на таблицу PROFESSION. Ну и наконец поле class говорит о том, объект какого класса будет присоеденен к нашему объекту. Как видите, это класс Profession. Думаю, что пока вопросов должно возникать не очень много.

Теперь мы можем посмотреть на более сложную запись - <bag name="applicantResultList" inverse="true" cascade="all-delete-orphan">. Как видите, здесь запись содержит несколько тэгов. Рассмотрим их подробнее:

  • name - имя поля в классе, которое содержит список сданных экзаменов
  • inverse - достаточно хитрый атрибут. В данном случае у нас связь двунаправленная - "родитель" знает о детях и каждый "ребенок" знает своего родителя. Чтобы эта свзяь была в одном экземпляре и существует атрибут inverse
  • cascade - данный атрибут показывает, что при удалении "родителя" надо удалять и всех "детей". т.е. если мы удалим абитуриента, то удалятся и все данные об его экзаменах
  • <key column="APPLICANT_ID"> - думаю, что вы уже догадались о назначении этого тэга. Он показывает, какое поле в таблице APPLICANT_RESULT используется для ссылки на "родителя" - таблицу APPLICANT
  • <one-to-many class="ApplicantResult"> - данная запись говорит о том, какой класс используется для списка.

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

ApplcantResult.java

package students.entity;

public class ApplicantResult {

    private Long applicantResultId;
    private Applicant applicant;
    private Subject subject;
    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 applicant - информация о том, кто сдавал данный экзамен
  • Subject subject - по какому предмету был экзамен
Посмотрим на описание нашего класса в виде файла XML - ApplicantResult.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="students.entity">
    <class name="ApplicantResult" table="applicant_result">
        <id name="applicantResultId" column="applicant_result_id">
            <generator class="native"/>
        </id>
        <many-to-one name="applicant" column="applicant_id" class="students.entity.Applicant"/>
        <many-to-one name="subject" column="subject_id" class="students.entity.Subject"/>
        <property name="mark" column="mark"/>
    </class>
</hibernate-mapping>
        

Думаю, что данный файл уже не нуждается в комментариях - можете разобраться сами.

Теперь нам осталось посмотреть на организацию классов Profession и Subject. Между ними тоже существует связь. Причем еще более сложная - много-ко-многим. Обычно такая связь организуется в виде дополнительной таблицы (мы ее рассматривали в Часть 15 - Новая структура данных.
Называется она SPECIALITY_SUBJECT.
Сначала я приведу код классов, в которых будет прописано наше отношение, а потом мы посмотрим как это описывается с помощью XML.

Profession.java

package students.entity;

import java.util.HashSet;
import java.util.Set;

public class Profession {

    private Long professionId;
    private String professionName;
    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;
    }
}
        

Subject.java

package students.entity;

import java.util.Set;

public class Subject {

    private Long subjectId;
    private String subjectName;
    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;
    }
}
        

Как видите отношение записывается как множество (Set). Если вы внимательно посмотрите на запись отношения один-ко-многим для класса Applicant, то увидите, что я использовал тэг bag и в классах исползовался List. В отношении много-ко-многим мы будем использовать другой тэг - set. Hibernate в этом случае более оптимально удаляет и вставляет записи об отношениях. Если хотите поэкспериментировать - поменяйте set на bag (соответственно Set на List - для инициализации можно использовать ArrayList. Помните, что Set и List всего лишь интерфейсы). И посмотрите какие запросы формирует Hibernate при обращении к базе данных. В случае List он просто удалит все записи из таблицы SPECIALITY-SUBJECT и заново вставит. При использовании Set это будет более умное решение.
Но давайте посмотрим на XML-файлы.

Profession.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="students.entity">
    <class name="Profession" table="profession">
        <id name="professionId" column="profession_id">
            <generator class="native"/>
        </id>
        <property name="professionName" column="profession_name"/>
        <set name="subjectList" table="speciality_subject">
            <key column="profession_id"></key>
            <many-to-many column="subject_id" class="students.entity.Subject"/>
        </set>
    </class>
</hibernate-mapping>
        

Subject.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="students.entity">
    <class name="Subject" table="subject">
        <id name="subjectId" column="subject_id">
            <generator class="native"/>
        </id>
        <property name="subjectName" column="subject_name"/>
        <set name="professionList" table="speciality_subject">
            <key column="subject_id"></key>
            <many-to-many column="profession_id" class="students.entity.Profession"/>
        </set>
    </class>
</hibernate-mapping>
        

Рассмотрим самое важное на примере кусочка XML для Profession

        <set name="subjectList" table="speciality_subject">
            <key column="profession_id"></key>
            <many-to-many column="subject_id" class="students.entity.Subject"/>
        </set>
        

Давайте подробно рассмотрим все составляющие данного XML.

  • <set - этот момент мы уже обсуждали
  • name - имя свойства, в котором храниться список предметов (Subject), которые надо сдать для поступления на данную специальность
  • table - это таблица, которая является связующей для наших двух классов. Мы о ней говорили выше
  • <key column="profession_id"> - это поле в таблице SPECIALITY_SUBJECT, которое указывает на класс Profession (таблицу PROFESSION)
  • <many-to-many column="subject_id" class="students.entity.Subject"/> - этот тэг содержит информацию о связи с классом Subject. Атрибут column указывает на поле в таблице SPECIALITY_SUBJECT к которому привязывается класс Subject (таблица SUBJECT). Ну а атрибут class показывает какого рода объекты находятся в списке subjectList

Вот мы и написали все необходимые XML-файлы и JAVA-файлы для работы с базой данных. Осталось совсем немного. Во-первых давайте посмотрим на обновленный файл 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 resource="students/entity/Profession.hbm.xml"/>
        <mapping resource="students/entity/Applicant.hbm.xml"/>
        <mapping resource="students/entity/Subject.hbm.xml"/>
        <mapping resource="students/entity/ApplicantResult.hbm.xml"/>

    </session-factory>

</hibernate-configuration>
        

Как видите он не сильно изменился - мы только добавили новые описания классов в самом конце.

Что нам еще надо сделать ? Наверно пора написать какой-то класс для реализации DAO. Он не будет у нас пока выполнять все функции, но кое-что он уже сможет сделать. Так что встречайте

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;
    }
}
        

Я настоятельно рекомендую прочитать комментарии к данному файлу. Здесь рассматрвиается одна важная особенность Hibernate - "ленивая" инициализация (lazy). Смысл ее в том, что когда вы загружаете объект с помощью того же вызова session.load() это сосвсем не означает, что Hibernate сразу же загрузит все данные. Можно сказать, что Hibernate "зарегистрирует" Ваше желание загрузить данные, но реальное обращение к базе данных может произойти только при попытке получить значение какого-то поля. еще более заметно это становится при работе с объектами, которые связаны с основным какими-либо отношениями.

Здесь приводится код класса 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());
        }

    }
}
        

Еще раз призываю вас внимательно почитать код и комментарии к нему. В самом конце приводится очень характерный код для Hibernate

        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());
        }
        

Посмотрите, как мы обращаемся к профессии, которую выбрал абитуриент. Мы нигде не пишем специальный SQL, который получал бы данные о специальности. Мы просто просим дать нам поле profession. Hibernate сам все сделает за вас. Это очень удобно. Думаю, что именно эта простота сделала Hibernate (да и вооще ORM) столь популярным инструментом. Ведь мы можем сделать очень длинные цепочки. Можно начать с какой-нибудь оценки на экзамене и протащить полную цепочку начиная с абитуриента и заканчивая полным списком предметов для специальности. Если предположить, что ar указывает на какую-то оценку, то выглядит вот так:

ar.getApplicant().getProfession().getSubjectList().size()

Мы получили количество предметов, которые надо здать абитуриенту, который получил данную оценку. И для этого нам не потребовалось писать ни одной строчки SQL. Такое не может не радовать. Особенно когда вам необходимо делать много запросов для реализации какой-то достаточно сложной логики.

Еще раз возвращаясь в понятию lazy (ленивый). Посмотрите, что я в некоторых случаях делаю вызов Hibernate.initialize(). Этот вызов насильно загружает данные для поля. Особенно это бывает актуально для отношений один-ко-многим, многие-ко-многим. Такие коллекции конечно же делаются "ленивыми" и не инициализируются. Попробуйте закомментировать где-нибудь этот вызов - получите ошибку. Например в методе StudentDAO.findSubject. В классе Main нам хочется узнать размер коллекции специальностей, для которых надо сдавать этот предмет. И вы увидите последствия.

Если углубляться в данную проблему, то в коде я оставил вариант вызова openSession вместо getCurrentSession.
При работе с базой данных создается объект, который реализует интерфейс CurrentSessionContext. Hibernate включает три реализации этого интерфеса - JTASessionContext, ManagedSessionContext, ThreadLocalSessionContext. Никто не мешает написать свой :). Задача контекста - описать как и когда сессия открывается и закрывается
Первый контекст управляется транзакциями, которыми в свою очередь управляет AplpicationServer (J2EE). Второй позволяет управлять транзакциями внешним пакетам. И в этом случае разработчик отвечает за открытие и закрытие (уничтожение) контекста. И наконец третий - локальный контекст. Так вот особенность вызова getCurrentSession в том, что он позволяет создать контекст автоматически при начале транзакции (обратите внимание на вызов session.beginTransaction() - если его убрать, то получим ошибку - createQuery is not valid without active transaction). И что достаточно удобно - при закрытии транзакции контекст "закрывается" тоже автоматически. Если же мы воспользуемся методом openSesion, то все будет хорошо. Вы можете даже убрать вызовы для транзакций. Но возникает другая проблема - в случае такого открытия сессии ее надо закрыть самому. И делать это придется в нашем случае там, где мы ее создали - доступна она нам только внутри метода. И если мы ее там закроем, то получим исеключение LazyInitializationException. Т.е. надо вытаскивать session наружу метода, что не хотелось бы.

Думаю, что для понимания основ работы с Hibernate этого материала будет достаточно. Опять же - призываю вас экспериментировать, искать, пробовать и читать документацию. Это здорово помогает понять многие тонкости использования Hibernate.

Если вы подумали о том, что количество файлов увеличивается в два раза (для каждого Entity необходимо написать свой XML), то вы "верной дорогой идете, товарищи". Ибо нам предстоит узнать более простой вариант описания - аннотации. Добро пожаловать в Часть 18 - Hibernate. Аннотации вместо XML.

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