вторник, 5 марта 2013 г.

Java Persistence API. Часть первая.

В данной статье мы познакомимся, как настроить свой проект для будущего использования JPA, а также, как при помощи аннотаций связать объекты с таблицами в базе данных.


Зависимости в Maven. 

Перед тем как приступать к связи с базой данных, необходимо настроить наш проект. Если вы используете Maven для сборки проекта, то вам понадобиться добавить следующие зависимости в pom.xml. Зависимость postgresql нужна только в случае, если вы используете PotgreSQL базу данных. Для других баз данных необходимы другие зависимости, которые можно найти в http://mvnrepository.com/.
	
    	org.hibernate
     	hibernate-validator
     	4.2.0.Final
 	
     
          org.hibernate
          hibernate-core
          3.6.10.Final
      
      
          org.hibernate
          hibernate-entitymanager
          3.6.10.Final
      
      
          mysql
          mysql-connector-java
          5.1.21
      
      
          postgresql
          postgresql
          9.1-901.jdbc3
      

Схема базы данных


Для начала разберемся в моделях. Допустим у нас есть класс Teacher с полями teacherType (тип учителя: TEACHER(0), DIRECTOR(1)), name, email. В таблицу teacher мы хотим запихнуть его тип в виде числа (teacher_type), его имя (name) и мыло (email).
Следующий класс - это Lesson. В нем есть поле teacher - учителя, который ведет этот урок и description - отдельный класс с описание данного урока.
В классе Description есть поля lessonCode, lessonName, lessonNotes.
В таблицу lesson мы хотим засунуть teacher_id учителя, который ведет этот предмет, а так же lesson_code и lesson_name из класса Description.
Для этого мы создаем foreign key от teacher_id каждого урока к id самого учителя.
В классе Lesson также есть поле version для optimistic locking. И в каждом классе есть автогенерируемый id, который генерируется, используя teacher_sequence для Teacher и lesson_sequence для Lesson.


SQL для создания базы данных:
CREATE TABLE teacher
 (
  id bigint NOT NULL primary key,
  teacher_type integer,
  name character varying(50),
  email character varying(40),
 );

CREATE TABLE lesson
(
  id bigint NOT NULL primary key,
  version bigint,
  teacher_id bigint,
  lesson_code character varying(20),
  lesson_name character varying(200),

  FOREIGN KEY (teacher_id) REFERENCES teacher (id),
  );

 CREATE SEQUENCE teacher_sequence
   INCREMENT 1
   MINVALUE 1
   MAXVALUE 9223372036854775807
   START 200000
   CACHE 1;


 CREATE SEQUENCE lesson_sequence

    INCREMENT 1
    MINVALUE 1
    MAXVALUE 9223372036854775807
    START 300000
    CACHE 1;


Entity классы

Для того, чтобы объект можно было связать с базой данных, первым делом он должен имплементировать интерфейс Serializable. Затем перед его декларацией должна стоять аннотация @Entity и @Table с именем таблицы, к которой будет привязан класс. Теперь о наиболее часто используемых аннотациях, примеры использования которых можно посмотреть ниже.
@Id - обязательное поле, которое будет использоваться как primary key после.
@Generated Value - создает автогенерацию id. В данном случае генератор использует teacher_sequence, которое объявляется в аннотации @SequenceGenerator.
@Enumerated - аннотация для enum полей класса. EnumType.Ordinal будет сохранять в базе данным только порядковое число енумерации.
@Column - если имя поля отличается от имени колонки в базе данных, следует указать с какой колонкой связать данное поле.
@Embedded - аннотация, чтобы вложить в одну таблицу какой-либо класс. При этом вкладываемый класс должен быть с аннотацией @Embeddable.
@Transient - аннотация, которую нужно указать для тех полей, которые мы не собираемся связывать с базой данных.
@OneToMany, @ManyToOne, @OneToOne, @ManyToMany - аннотации для соотвествующих связей между таблицами. 
@NamedQueries  - аннотация, в которой хранятся все @NamedQuery.
@NamedQuery - аннотация для создания запросов на JPQL, которые затем можно использовать при запросах в базу данных.
@Version - аннотация для оптимистичного локинга, которая не позволяет одновременно использовать данную Entity несколькими пользователями.

Класс Teacher:
import java.io.Serializable;
import java.util.List;
import javax.persistence.*;

@NamedQueries({
        @NamedQuery(
                name = "GET_TEACHER_BY_NAME_AND_EMAIL",
                query = "select t from Teacher t where t.name = :name and t.email = :email")
})

@Entity
@Table(name = "teacher")
@SequenceGenerator(name = "teacher_sequence", sequenceName = "teacher_sequence")
public class Teacher implements Serializable {


    public static final String GET_TEACHER_BY_NAME_AND_EMAIL = "GET_TEACHER_BY_NAME_AND_EMAIL";

    private long id;
    private TeacherType teacherType;
    private String name;
    private String email;
    private List lessons;


    @Id
    @GeneratedValue(generator = "teacher_sequence")
    public final long getId() {
        return id;
    }

    public final void setId(final long id) {
        this.id = id;
    }

    @Enumerated(EnumType.ORDINAL)
    @Column(name = "teacher_type")
    public TeacherType getTeacherType() {
        return teacherType;
    }

    public void setTeacherType(final TeacherType teacherType) {
        this.teacherType = teacherType;
    }

    public String getName() {
       return name;
    }
    public void setName(final String name) {
       this.name = name;
    }

    public String getEmail() {
       return email;
    }

    public void setEmail(final String email) {
       this.email = email;
    }

    @OneToMany(mappedBy = "teacher")
    public List getLessons() {
        return lessons;
    }

    public void setLessons(final List lessons) {
        this.lessons = lessons;
    }
 
}

Enum Класс TeacherType:
public enum TeacherType {

    TEACHER, DIRECTOR;

}

Класс Lesson, связанный отношением ManyToOne с классом Teacher:
import javax.persistence.*;
import java.io.Serializable;

@NamedQueries({
        @NamedQuery(
                name = "GET_ALL_LESSONS", query = "select lesson from Lesson lesson")
})

@Entity
@Table(name = "lesson")
@SequenceGenerator(name = "lesson_sequence", sequenceName = "lesson_sequence")
public class Lesson implements Serializable {

    public static final String GET_ALL_LESSONS = "GET_ALL_LESSONS";

    private long id;
    private Teacher teacher;
    private Description description;
    private long version;

    @Version
    public long getVersion() {
        return version;
    }

    public void setVersion(final long version) {
        this.version = version;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "teacher_id")
    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Embedded
    public Description getDescription() {
        return description;
    }

    public void setDescription(final Description description) {
        this.description = description;
    }

    @Id
    @GeneratedValue(generator = "lesson_sequence")
    public final long getId() {
        return id;
    }

    public final void setId(final long id) {
        this.id = id;
    }
}


Класс Description, поля которого будут хранить в одной таблице с Lesson:
import java.io.Serializable;
import javax.persistence.*;

@Embeddable
public class Description implements Serializable {

 private String lessonCode;
 private String lessonName;
 private String lessonNotes;

 
    @Column(name = "lesson_code")
    public final String getLessonCode() {
       return lessonCode;
    }

    public final void setLessonCode(final String LessonCode) {
       this.lessonCode = lessonCode;
    }

    @Column(name = "lesson_name")
    public final String getLessonName() {
       return lessonName;
    }

    public final void setLessonName(final String lessonName) {
       this.lessonName = lessonName;
    }

    @Transient
    public final String getLessonNotes() {
       return lessonNotes;
    }

    public final void setLessonNotes(final String lessonNotes) {
       this.lessonNotes = lessonNotes;
    }

}


Автор статьи: Александр Картышев

Комментариев нет:

Отправить комментарий