Книга 1 - Начальные сведения
Книга 2 - Более профессиональный подход |
Студенческий отдел кадров Аннотации
Аннотации были введены в Java 5 и для них сразу нашлось куча применений. Ведь аннотация
позволяет вам получить дополнительную информацию о любом объекте языка - классе, методе, поле.
Конечно же сам создатель языка не мог пройти мимо и очень быстро был опубликован JPA -
Java Persistance API (Java API для хранения). И он содержал много удобных и полезных
аннотаций, которые взяли на вооружение многие популярные пакеты. Некоторую информацию по аннотациям вы можете найти в моей статье Что нового в Java SE 5.0 Само собой Hibernate тоже не остался в стороне. Теперь разработчик не должен создавать дополнительные XML-файлы - всю необходимую информацию он может описать прямо в файле с Entity. Что несомненно гораздо удобнее. Вы очень быстро привыкните - я привык к этому за 2-3 дня. Теперь описание в XML мне кажется просто кощунством :).
Зайдите на страницу Hibernate. Слева будет в меню,
в котором мы выбираем пункт Download. В списке реализаций выбираем "Hibernate Annotations".
В загруженном архиве будет список библиотек и документация, которую обязательно надо будет
читать и пробовать использовать. Для текущего проекта нам понадобятся следующие библиотеки: Изменения коснутся файлов с описаниями 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; } }
Давайте рассмотрим все аннотации, которые встретились нам в данном файле. Надеюсь, вы знаете, что
аннотации начинаются с симовола @.
Теперь можно посмотреть на реализацию класса 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; } } Здесь мы видим еще один вариант записи связи - много-ко-многим. Посмотрим на параметры:
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. Далее мы не будем рассматривать какие-либо аспекты этого пакета, хотя нам могут встречаться более сложные примеры или просто другие примеры использования данного пакета. Прежде чем закончить обозрение, я бы хотел перечислить те области, которые есть смысл проработать самим по документации
Архив с исходными кодами: Исходный код |