Автор оригинала: 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 Listcomments = 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 Listcomments = 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