JPA 2.2带来一些备受期待的变更

\

本文要点

\\
  • JPA 2.2在去年夏天发布,交付了一些备受期待的功能改善:\\t
  • 对Java 8特性的更好支持,例如Date和Time API\\t
  • 新增对AttributeConverter的CDI支持\\t
  • 一些注解变得@Repeatable\\t
  • 你现在可以获取java.util.Stream形式的查询结果\\t
  • 准备针对Java 9的规范\
\\

去年夏天发布,JPA 2.2是Java EE 8中比较稳健的规范之一,可能是因为JPA 2.1已经是一个非常成熟的规范,提供了现代应用程序所需的大部分功能。

\\

尽管如此,JPA 2.2 带来了一些显著的变更。首先,它引入了2个小规模的变更:

\\
  • 增加对AttributeConverter的CDI支持\\t
  • 准备针对Java 9的规范\

其次,也更重要的是,JPA 2.2规范支持Java 8的一些特性:

\\
  • JPA 2.x当前版本支持Date和Time API的一些类(并没有支持所有的类,下面会详细介绍)\\t
  • 一些注解变得可重复\\t
  • 你现在可以获取java.util.Stream形式的的查询结果\

我们稍后会介绍更多关于这些内容的细节。

\\

一些背景信息:Java EE 7(包含JPA 2.1)在Java 8 之前发布,因此不能对Java 8 引入的任何数据类型和编程概念提供支持。因此,JPA 2.2及其对Java 8的支持备受期待。

\\

在你的项目中加入JPA 2.2

\\

像所有Java EE规范一样,JPA只定义API,允许开发者在不同的实现中选择适合自己的。

\\

下面的Maven坐标会增加JPA 2.2 API到你的项目中。

\\
\\u0026lt;dependency\u0026gt;\\t\u0026lt;groupId\u0026gt;javax.persistence\u0026lt;/groupId\u0026gt;\\t\u0026lt;artifactId\u0026gt;javax.persistence-api\u0026lt;/artifactId\u0026gt;\\t\u0026lt;version\u0026gt;2.2\u0026lt;/version\u0026gt;\\u0026lt;/dependency\u0026gt;\
\\

尽管JPA 2.1有一些不同的实现,但是只有EclipseLink提供了完整的JPA 2.2实现。你可以使用下面的Maven坐标将它增加到你的项目中。

\\
\\u0026lt;dependency\u0026gt;\    \u0026lt;groupId\u0026gt;org.eclipse.persistence\u0026lt;/groupId\u0026gt;\    \u0026lt;artifactId\u0026gt;eclipselink\u0026lt;/artifactId\u0026gt;\    \u0026lt;version\u0026gt;2.7.0\u0026lt;/version\u0026gt;\\u0026lt;/dependency\u0026gt;\
\\

在AttributeConverter中的CDI注入

\\

AttributeConverter为读取和写入自定义数据类型提供了一种易用的标准化方法。它通常用在JPA 2.1中,来增加对LocalDate和LocalDateTime的支持,或者来实现一个自定义的枚举映射

\\

实现一个converter非常简单;只需要实现AttributeConverter接口,为这个类添加@Converter注解,然后实现2个接口方法:

\\
  • convertToDatabaseColumn——将Java类转变为它的数据库表现形式\\t
  • convertToEntityAttribute——相反地,将数据库列转变回Java对象\

实现一个自定义枚举映射

\\

下面的代码片段展示了一个简单的例子:

\\
\@Converter(autoApply = true)\public class ContactInfoTypeConverter implements\\t\tAttributeConverter\u0026lt;ContactInfoType, String\u0026gt; {\\\t@Override\\tpublic String convertToDatabaseColumn(ContactInfoType type) {\\t\tswitch (type) {\\t\tcase PRIVATE:\\t\t\treturn \"P\";\\\t\tcase BUSINESS:\\t\t\treturn \"B\";\\\t\tdefault:\\t\t\tthrow new IllegalArgumentException(\"ContactInfoType [\"\                                + type.name() + \"] not supported.\");\\t\t}\\t}\\\t@Override\\tpublic ContactInfoType convertToEntityAttribute(String dbType) {\\t\tswitch (dbType) {\\t\tcase \"P\":\\t\t\treturn ContactInfoType.PRIVATE;\\\t\tcase \"B\":\\t\t\treturn ContactInfoType.BUSINESS;\\\t\tdefault:\\t\t\tthrow new IllegalArgumentException(\"ContactInfoType [\" \                                + dbType + \"] not supported.\");\\t\t}\\t}\}\
\\

AttributeConverter将我们的ContactInfoType枚举转变为一个String。现在你可能会疑惑,为什么你要实现自己的转换器,因为JPA已经提供了2种可选方案来持久化一个枚举:1)String表现形式;2)使用特定枚举值的序号值。但是如果你想改变枚举的时候,就会发现这两种方案都各有它们的缺点。当将一个枚举持久化为一个String时,你如果想要修改任何枚举值的名字,就需要更改你的数据库。另一方面,当将枚举持久化为序号值时,因为序号值代表了枚举值在枚举定义中的位置,因此,如果你更改了枚举值的顺序,或者你增加了新的枚举值并且不是放在最后,或者你删掉了除最后一个枚举值之外的其它任何枚举值,你就又需要更新你的持久化数据了。

\\

因此,在上述任何一种策略中,如果你使用JPA的标准映射,你的枚举的大部分变更都会需要数据库更新。否则,你的持久层框架会无法映射已存在的值,或者会把它们映射成错误的枚举值。

\\

你可以通过使用AttributeConverter实现一个自定义的映射来避免上述缺点,这样你就可以控制映射了,并且在需要重构枚举时,避免任何对已存在的映射的变更。

\\

使用CDI注入

\\

正如你在先前的代码片段中看到的那样,我用AttributeConverter实现了映射。如果你不需要复用这个转换实现,那么它是一个不错的方案。但是,如果你想要复用它,JPA 2.2 允许你选择使用CDI注入来将你的转换实现注入到你的AttributeConverter中。

\\
\@Converter(autoApply = true)\public class ContactInfoTypeCdiConverter implements\\t\tAttributeConverter\u0026lt;ContactInfoType, String\u0026gt; {\\\t// Conversion implementation class:\\t@Inject\\tContactInfoTypeHelper conversionHelper;\\t\\t@Override\\tpublic String convertToDatabaseColumn(ContactInfoType type) {\\t\treturn conversionHelper.convertToString(type);\\t}\\\t@Override\\tpublic ContactInfoType convertToEntityAttribute(String dbType) {\\t\treturn conversionHelper.convertToContactInfoType(dbType);\\t}\}\
\\

使用Date和Time类作为实体属性

\\

Java 8的Date和Time API备受期待,许多开发者想要使用这些新类作为实体属性。不幸的是,JPA 2.1在Java 8之前发布,因此不支持这些类。

\\

在JPA 2.2发布之前,你有两种方法来持久化dates和times:

\\

JPA 2.2 规范现在支持Date和Time API的一些类作为基础类型,因此你不再需要提供任何额外的映射注解,例如用于java.util.Date的@Temporal注解。相比于过去的java.util.Date,Date和Time API中的类将简单的日期和带时间的日期区别开来。因此,持久层框架拥有将这些类持久化成相应的SQL数据类型而需要的所有信息。

\\
\@Entity\public class DateAndTimeEntity {\\\t@Id\\t@GeneratedValue(strategy = GenerationType.SEQUENCE)\\tprivate Long id;\\\tprivate LocalDate date;\\\tprivate LocalDateTime dateTime;\\\tpublic LocalDate getDate() {\\t\treturn date;\\t}\\\tpublic void setDate(LocalDate date) {\\t\tthis.date = date;\\t}\\\tpublic LocalDateTime getDateTime() {\\t\treturn dateTime;\\t}\\\tpublic void setDateTime(LocalDateTime dateTime) {\\t\tthis.dateTime = dateTime;\\t}\\\tpublic Long getId() {\\t\treturn id;\\t}\}\
\\

尽管JPA 2.2 未支持全部Date和Time API,但是一些实现,例如Hibernate,会提供更多类的专门支持,例如java.time.Duration。如果可以在下一个发布版本中加入这些功能会非常棒,但是因为Oracle正将所有的规范移交给Eclipse基金会,因此我们需要拭目以待。

\\
\\t\t\t

Java Type

\\t\t\t
\\t\t\t

JDBC Type

\\t\t\t
\\t\t\t

java.time.LocalDate

\\t\t\t
\\t\t\t

DATE

\\t\t\t
\\t\t\t

java.time.LocalTime

\\t\t\t
\\t\t\t

TIME

\\t\t\t
\\t\t\t

java.time.LocalDateTime

\\t\t\t
\\t\t\t

TIMESTAMP

\\t\t\t
\\t\t\t

java.time.OffsetTime

\\t\t\t
\\t\t\t

TIME_WITH_TIMEZONE

\\t\t\t
\\t\t\t

java.time.OffsetDateTime

\\t\t\t
\\t\t\t

TIMESTAMP_WITH_TIMEZONE

\\t\t\t

一些JPA注解变得可重复

\\

可重复的注解允许你用相同的注解多次注解类、方法或属性。那意味着你不再需要用像@NamedQueries来包裹多个@NamedQuery那样来使用注解。对比过去的方式:

\\
\@NamedQueries({\  @NamedQuery(name = EntityWithNamedQueries.findByName, \   query = \"SELECT e FROM EntityWithNamedQueries e WHERE e.name = :name\"),\  @NamedQuery(name = EntityWithNamedQueries.findByContent, \   query=\"SELECT e FROM EntityWithNamedQueries e WHERE e.content = :content\")\ })\public class EntityWithNamedQueries { … }\
\\

对于使用多个注解的实体,使用新的注解方式提升了代码的可读性。

\\
\@NamedQuery(name = EntityWithMultipleNamedQuery.findByName, \ query = \"SELECT e FROM EntityWithNamedQueries e WHERE e.name = :name\")\@NamedQuery(name = EntityWithMultipleNamedQuery.findByContent, \ query = \"SELECT e FROM EntityWithNamedQueries e WHERE e.content = :content\")\public class EntityWithMultipleNamedQuery { … }\
\\

在JPA 2.2中,可以被包裹进一个容器注解中的所有注解都变得可重复。这些注解有:

\\
  • AssociationOverride\\t
  • AttributeOverride\\t
  • JoinColumn\\t
  • MapKeyJoinColumn\\t
  • NamedEntityGraph\\t
  • NamedNativeQuery\\t
  • NamedQuery\\t
  • NamedStoredProcedureQuery\\t
  • PersistenceContext\\t
  • PersistenceUnit\\t
  • PrimaryKeyJoinColumn\\t
  • SecondaryTable\\t
  • SqlResultSetMapping\\t
  • SequenceGenerator\\t
  • TableGenerator\

获得Stream形式的查询结果

\\

Stream API是Java 8引入的另外一个流行的特性,而且许多开发者想要使用它来处理他们的查询结果。

\\

JPA 2.1支持的唯一方式是调用Query接口的getResultList方法,来获取List形式的查询结果。然后,你可以调用List接口的stream方法来获取它的Stream形式:

\\
\TypedQuery\u0026lt;DateAndTimeEntity\u0026gt; q = em.createQuery(\    \"SELECT e FROM DateAndTimeEntity e\
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值