如何获取类型安全和直观的Hibernate / JPA查询


大部分Java数据库应用程序都在使用Hibernate / JPA来弥合Java和SQL之间的鸿沟。直到最近,我们还被迫将Java和JPQL混合使用,或者使用复杂的命令式标准构建器来创建数据库查询。这两种方法本质上既不是类型安全的,也不是非常直观的。新发布 的开源库JPAstreamer通过允许您使用Java Streams表示Hibernate / JPA查询来解决这些问题。这意味着我们可以避免JPQL / HQL与Java之间的任何阻抗不匹配,并获得完全的类型安全性。在本文中,我将向您展示如何使用JPAstreamer在您的应用程序中使用Java Stream查询。 简而言之,JPAstreamer 如前所述,JPAstreamer允许使用简短,简洁,类型安全的声明性结构将JPA查询表示为标准Java流。这使我们的代码更短,更简单,更易于阅读和维护。最重要的是,我们可以坚持只使用Java代码,而无需将其与SQL / JPQL或其他语言构造/ DSL混合使用。

简而言之,我们可以像这样查询数据库:

jpaStreamer.stream(Film.class)
    .sorted(Film$.length.reversed())
    .limit(15)
    .map(Film$.title)
    .forEach(System.out::println);

这将打印数据库中最长的15部电影的标题。

OSS许可证 JPAstreamer使用与Hibernate(LGPL)相同的许可证。这使得在现有的Hibernate项目中易于使用。JPAstreamer还可以与其他JPA提供程序一起使用,例如EclipseLink,OpenJPA,TopLink等。

安装 安装JPAstreamer只需要添加一个单独的依赖在你的Maven /摇篮配置文件作为说明在这里。例如,Maven用户添加以下依赖项:

<dependency> 
     <groupId>com.speedment.jpastreamer</groupId>
     <artifactId>jpastreamer-core</artifactId>
     <!-- Make sure to use the latest version -->
     <version>0.1.8</version>
 </dependency>

让我们看一下JPAstreamer如何适合现有应用程序。

示例数据库和JPA实体 在以下示例中,我们使用JPAstreamer查询“ Sakila”示例数据库,该数据库可直接从Oracle下载或作为Docker实例下载。

这是使用Docker安装和运行示例数据库的方式:

$ docker pull restsql / mysql-sakila
$ docker run -d  --publish  3306:3306 --name mysqld restsql / mysql-sakila

我们还将依赖JPA实体,例如部分显示在此处的Film类:

@Entity
@Table(name = "film", schema = "sakila")
public class Film implements Serializable {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "film_id", nullable = false, updatable = false, columnDefinition = "smallint(5)")
    private Integer filmId;

    @Column(name = "title", nullable = false, columnDefinition = "varchar(255)")
    private String title;

    @Column(name = "description", nullable = false, columnDefinition = "text")
    private String description;
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
        name = "film_actor",
        joinColumns = @JoinColumn(name = "film_id") ,
        inverseJoinColumns = @JoinColumn(name = "actor_id") 
    )
    private List<Actor>;

本文中的完整代码是开源的,可在此处获得。

JPAstreamer — Printing the Longest Films 这是一个完整的示例,说明如何使用JPAstreamer创建查询,以打印出数据库中15条最长的电影的长度和标题:

public class LongestFilms {

  public static void main(String[] args) {

        final JPAStreamer jpaStreamer = JPAStreamer.of("sakila");

        jpaStreamer.stream(Film.class)
            .sorted(Film$.length.reversed())
            .limit(15)
            .map(f -> String.format("%3d %s", f.getLength(), f.getTitle()))
            .forEach(System.out::println);

        jpaStreamer.close();
    }
}

这将打印:

185 SOLDIERS EVOLUTION
185 GANGS PRIDE
185 SWEET BROTHERHOOD
185 CHICAGO NORTH
185 HOME PITY
185 POND SEATTLE
185 CONTROL ANTHEM
185 DARN FORRESTER
185 WORST BANGER
184 SMOOCHY CONTROL
184 SONS INTERVIEW
184 SORORITY QUEEN
184 MOONWALKER FOOL
184 THEORY MERMAID

可以看出,查询是简单,简洁,完全类型安全的,并且遵循Java Stream标准API。无需学习新知识。

上面的代码将创建以下SQL(为简便起见,以下简称):

select
   film0_.film_id as film_id1_1_,
    film0_.length as length4_1_,
    film0_.title as title10_1_,
    /* more columns */
from
    film film0_ 
order by
    film0_.length desc limit ?

这意味着大多数Java流实际上是在数据库端执行的。仅在JVM中执行map()和forEach()操作(无法轻松转换为SQL)。这真是太酷了!

Pre-Joining Columns 为避免出现“ SELECT N + 1”问题,可以通过提供如下配置对象来将流配置为热切地加入列中:

StreamConfiguration configuration = StreamConfiguration.of(Film.class)
    .joining(Film$.actors)
    .joining(Film$.language);
jpaStreamer.stream(configuration) 
    .filter(Film$.rating.in("G", "PG"))
    .forEach(System.out::println);

这将在内部创建一个Hibernate联接,并且仅呈现一个SQL查询,该SQL查询将即时填充所有Film字段“ List <Artist> artists”“ Language language”

结论 在本文中,我展示了如何使用开源库JPAstreamer避免Hibernate / JPA中JPQL / HQL之间的阻抗不匹配。使用Stream API,您可以在不影响应用程序性能的情况下,用标准Java编写类型安全和可表达的数据库查询。

JPAStreamer的背景是,我们已经开发了基于流的ORM工具Speedment,并且遇到了许多想要使用Java流但受限于在其应用程序中使用Hibernate的开发人员。因此,我们现在已经开发了JPAstreamer,这是一个JPA / Hibernate扩展,可以处理Java Stream查询,而无需更改现有代码库。


原文链接:http://codingdict.com