Рубрики
Без рубрики

Создание объектов Fluent API с помощью JPA и гибернации

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

Автор оригинала: Vlad Mihalcea.

Вступление

В этой статье мы рассмотрим, как мы можем создать объект в стиле API fluent при использовании JPA и гибернации.

Команда разработчиков JHipster хочет раскрыть Методы создания объектов Fluent Interface для их объектов JPA, поэтому они спросили меня, будет ли это работать с JPA и гибернацией. В то время как JPA довольно строго относится к получателям и установщикам сущностей, Hibernate более снисходителен в этом отношении.

Я был бы очень рад получить ваше понимание @vlad_mihalcea вкл.

Спецификация JPA

Спецификация JPA 2.1 делает следующее замечание в отношении свойств сущности:

Требуется, чтобы класс сущностей следовал соглашениям о сигнатурах методов для свойств чтения/записи JavaBeans (как определено классом JavaBeans Introspector) для постоянных свойств при использовании доступа к свойствам.

В этом случае для каждого постоянного свойства свойства типа T сущности существует метод getter, getProperty и метод setter setProperty . Для логических свойств является свойством может использоваться в качестве альтернативного имени метода получения.[2]

Для однозначных постоянных свойств эти сигнатуры метода являются:

• T получить собственность() • недействительное свойство setProperty(T t)

Причина, по которой мы получаем такое требование, заключается в том, что спецификация JPA не предполагает, как будут использоваться сущности. Придерживаясь спецификаций JavaBean, сущности могут быть подвергнуты самоанализу с помощью отражения Java средствами IDE или другими платформами, которые могут ожидать этой стандартной подписи геттера и сеттера.

Спецификация гибернации

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

Модель предметной области

Наша модель домена будет использовать две сущности: родительскую (например, Сообщение ) и дочернюю (например, Комментарий к сообщению ), обе с использованием методов в стиле Fluent Interface setter.

Объект Post выглядит следующим образом:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    public Post() {}

    public Post(String title) {
        this.title = title;
    }

    @OneToMany(
        cascade = CascadeType.ALL, 
        orphanRemoval = true, 
        mappedBy = "post"
    )
    private List comments = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public Post setId(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public List getComments() {
        return comments;
    }

    public Post addComment(PostComment comment) {
        comment.setPost(this);
        comments.add(comment);
        return this;
    }
}

И Комментарий к сообщению сущность выглядит следующим образом:

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    private Date createdOn;

    @ManyToOne
    private Post post;

    public Long getId() {
        return id;
    }

    public PostComment setId(Long id) {
        this.id = id;
        return this;
    }

    public String getReview() {
        return review;
    }

    public PostComment setReview(String review) {
        this.review = review;
        return this;
    }

    public Date getCreatedOn() {
        return createdOn;
    }

    public PostComment setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
        return this;
    }

    public Post getPost() {
        return post;
    }

    public PostComment setPost(Post post) {
        this.post = post;
        return this;
    }
}

Время Тестирования

С помощью API-интерфейса Fluent мы можем создать Сообщение сущность и три Комментария(комментариев)к сообщению вот так:

Post post = new Post()
.setId(1L)
.setTitle("High-Performance Java Persistence")
.addComment(
    new PostComment()
    .setReview("Awesome book")
    .setCreatedOn(Timestamp.from(
        LocalDateTime.now().minusDays(1)
            .toInstant(ZoneOffset.UTC))
    )
)
.addComment(
    new PostComment()
    .setReview("High-Performance Rocks!")
    .setCreatedOn(Timestamp.from(
        LocalDateTime.now().minusDays(2)
            .toInstant(ZoneOffset.UTC))
    )
)
.addComment(
    new PostComment()
    .setReview("Database essentials to the rescue!")
    .setCreatedOn(Timestamp.from(
        LocalDateTime.now().minusDays(3)
            .toInstant(ZoneOffset.UTC))
    )
);

entityManager.persist(post);

Извлечение Сообщения и Комментария к сообщению сущностей работает просто отлично:

Post post = entityManager.find(Post.class, 1L);

assertEquals(3, post.getComments().size());

Общая альтернатива JPA

Если вы беспокоитесь о переносимости JPA, вы можете просто добавить метод Fluent Interface вместе с настройщиками JavaBean:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    public Post() {}

    public Post(String title) {
        this.title = title;
    }

    @OneToMany(
        cascade = CascadeType.ALL, 
        orphanRemoval = true, 
        mappedBy = "post"
    )
    private List comments = new ArrayList<>();

    public Long getId() {
        return id;
    }

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

    public Post id(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Post title(String title) {
        this.title = title;
        return this;
    }

    public List getComments() {
        return comments;
    }

    public Post addComment(PostComment comment) {
        comments.add(comment.post(this));
        return this;
    }
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    private Date createdOn;

    @ManyToOne
    private Post post;

    public Long getId() {
        return id;
    }

    public PostComment setId(Long id) {
        this.id = id;
        return this;
    }

    public String getReview() {
        return review;
    }

    public void setReview(String review) {
        this.review = review;
    }

    public PostComment review(String review) {
        this.review = review;
        return this;
    }

    public Date getCreatedOn() {
        return createdOn;
    }

    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

    public PostComment createdOn(Date createdOn) {
        this.createdOn = createdOn;
        return this;
    }

    public Post getPost() {
        return post;
    }

    public void setPost(Post post) {
        this.post = post;
    }

    public PostComment post(Post post) {
        this.post = post;
        return this;
    }
}

Чтобы воспользоваться преимуществами API в стиле fluent, нам просто нужно использовать новые методы интерфейса Fluent, избегая при этом установщиков компонентов Java, которые могут использоваться некоторыми другими сторонними инструментами:

Post post = new Post()
.id(1L)
.title("High-Performance Java Persistence")
.addComment(new PostComment()
    .review("Awesome book")
    .createdOn(Timestamp.from(
        LocalDateTime.now().minusDays(1)
            .toInstant(ZoneOffset.UTC))
    )
)
.addComment(new PostComment()
    .review("High-Performance Rocks!")
    .createdOn(Timestamp.from(
        LocalDateTime.now().minusDays(2)
            .toInstant(ZoneOffset.UTC))
    )
)
.addComment(new PostComment()
    .review("Database essentials to the rescue!")
    .createdOn(Timestamp.from(
        LocalDateTime.now().minusDays(3)
            .toInstant(ZoneOffset.UTC))
    )
);

entityManager.persist(post);

На самом деле, именно так команда хипстеров подумала о добавлении объектов Fluent Interface .

Хотя эта универсальная альтернатива лучше с точки зрения переносимости, если ваше корпоративное приложение не использует сеттеры, совместимые с Java-компонентами, вам лучше изменить подпись сеттера в соответствии с требованиями шаблона интерфейса Fluent.

Вывод

Шаблон интерфейса Fluent отлично работает с Hibernate, поэтому еще одна причина считать его предпочтительным поставщиком JPA.

Код доступен на GitHub Code available on GitHub