简体   繁体   English

playframework:在播放中将java8 java.time类型LocalDate与冬眠保持一致

[英]playframework: persist java8 java.time type LocalDate with hibernate in Play

I'm having trouble getting any kind of conversion or compatibility working for the new java.time.* types. 我无法为新的java.time。*类型进行任何类型的转换或兼容性。 Or at least, LocalDate. 或至少是LocalDate。

I see the exception: 我看到例外:

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[IllegalStateException: Error(s) binding form: {"dateOfBirth":["Invalid value"]}]]
    at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:265) ~[play_2.11-2.4.4.jar:2.4.4]

Caused by: java.lang.IllegalStateException: Error(s) binding form: {"dateOfBirth":["Invalid value"]}
    at play.data.Form.get(Form.java:592) ~[play-java_2.11-2.4.4.jar:2.4.4]
    at controllers.Application.addPatient(Application.java:49) ~[classes/:na]

There are several methods I've found, that should in principle work, with JPA hibernate, however, if this is a problem (again) of Play or what? 我发现有几种方法,原则上应该可以使JPA休眠,但是,如果这又是Play的问题,还是什么? I am not sure. 我不确定。

First method: 第一种方法:

Provide your own custom converter: 提供您自己的自定义转换器:

@Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> 
{
    @Override
    public Date convertToDatabaseColumn(LocalDate locDate) {
        return (locDate == null ? null : Date.valueOf(locDate));
    }

    @Override
    public LocalDate convertToEntityAttribute(Date sqlDate) {
        return (sqlDate == null ? null : sqlDate.toLocalDate());
    }
}

Then on the field one needs to use this: 然后,在现场需要使用此功能:

@Convert(converter = LocalDateAttributeConverter.class)
public LocalDate dateOfBirth;

http://www.thoughts-on-java.org/persist-localdate-localdatetime-jpa/ http://www.thoughts-on-java.org/persist-localdate-localdatetime-jpa/

Of course, as I understand it, in principle, I should not need the @Convert annotation since the converter itself is annotated with the @Converter(autoApply = true) annotation. 当然,据我所知,原则上,我不需要@Convert注释,因为转换器本身是使用@Converter(autoApply = true)注释进行注释的。 But as I can find no documentation about using successfully Converters with Play (nor any hits on google) I have tried with and without, WITHOUT any success. 但是,因为我找不到关于成功使用Play Converters的文档(也没有Google上的任何点击),因此我尝试了使用和不使用转化器都没有成功。

Next method: 下一个方法:

Well, actually, so far as I read, the newer java8 types should be now supported since hibernate 5 something.... and i've got 5.0.5 in use, and included the necessary library in my class path: 好吧,据我所知,从休眠5开始,现在应该支持较新的java8类型。...并且我已经使用了5.0.5,并且在类路径中包括了必要的库:

"org.hibernate" % "hibernate-java8" % "5.0.5.Final", 

https://hibernate.atlassian.net/browse/HHH-8844 https://hibernate.atlassian.net/browse/HHH-8844

That didn't help at all. 那根本没有帮助。 Same stacktrace. 相同的堆栈跟踪。

For good measure, I added the hibernate specific annotation 为了达到良好的效果,我添加了休眠特定注释

@Type(type="java.time.LocalDate")

to my field. 到我的领域。

That would have been ugly. 那将是丑陋的。 But i'd have put up with it, if it had've helped. 但是,如果它有所帮助,我会忍受的。 It didn't. 没有。 Same exception. 同样的例外。 Using that WITH the converter caused other errors. 与转换器一起使用会引起其他错误。 Which is interesting. 这很有趣。

I am using Play 2.4, with Hibernate 5.0.5. 我在Hibernate 5.0.5中使用Play 2.4。 Has anyone managed to achieve this? 有没有人设法做到这一点?

OK, so I finally figured it out. 好的,所以我终于知道了。 I should have looked more closely (slowly) at the stack-trace. 我应该更仔细地(慢慢地)看一下堆栈跟踪。

In fact I in the end was effected by three problems. 实际上,我最终受到三个问题的影响。 All solved in three different ways. 全部以三种不同方式解决。 All, however, related to 'pending' support of the newer java.time classes. 但是,所有这些都与对较新的java.time类的“挂起”支持有关。 To be fair, support from the underlying libraries has only recently been introduced. 公平地说,底层库的支持只是最近才引入的。

First problem: Form binding was failing validation. 第一个问题:表单绑定验证失败。

I debugged the Form binding work in my controller: 我在控制器中调试了表单绑定工作:

    Form<Patient> form = Form.form(Patient.class);
    form = form.bindFromRequest();
    System.out.println(form.toString());
    Patient patient = form.get();

And while the stacktrace was being caused at the call to form.get() , in fact the error had already occurred at the call to form.bindFromRequest() . 虽然在调用form.get()引起了form.get() ,但实际上在调用form.bindFromRequest()已经发生了错误。 Errors were being registered within the Form.errors map, which had come from org.springframework.validation.DataBinder . 错误已在Form.errors映射中注册,该映射来自org.springframework.validation.DataBinder

A google search further, and I came across this: 谷歌搜索进一步,我碰到了这个:

https://groups.google.com/forum/#!topic/play-framework/Wl_ip56111c https://groups.google.com/forum/#!topic/play-framework/Wl_ip56111c

Implementing that fixed my first problem, and the Form binding then worked. 实施解决了我的第一个问题,然后进行了表单绑定。

Second problem: Persistence 第二个问题:持久性

I'm' using Hibernate for persistence of the objects. 我正在使用Hibernate来保持对象的持久性。 Adding hibernate-java8 to my classpath enables persistence of the new datatype. 将hibernate-java8添加到我的类路径中可启用新数据类型的持久性。 This, for play 2, goes in your build.sbt file. 对于播放2,这将放入您的build.sbt文件中。

libraryDependencies ++= Seq(
  javaJdbc,
  cache,
  javaWs,
  javaJpa,
  "org.hibernate" % "hibernate-entitymanager" % "5.0.5.Final",
  "org.hibernate" % "hibernate-java8" % "5.0.5.Final", 
)

https://hibernate.atlassian.net/browse/HHH-8844 https://hibernate.atlassian.net/browse/HHH-8844

Third problem: Json support 第三个问题:Json支持

I was unable to render my model instantiation as a Json object. 我无法将模型实例化呈现为Json对象。 It should be supported, but somehow, in play, it's not setup. 应该支持它,但是在某种程度上,它不是安装程序。

I found this post which showed me how to work-around that: 我发现这篇文章向我展示了如何解决:

    // http://stackoverflow.com/questions/32872474/how-to-use-java-time-localdate-on-a-play-framework-json-rest/32891177#comment56814598_32891177
    // https://groups.google.com/forum/?hl=en.#!searchin/play-framework/java.time.localdate/play-framework/Dv-IpvBqWgo/l6NTp3e0BQAJ

The fix I post here for your convenience: 为了方便起见,我在此处发布了此修复程序:

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Jdk8Module());
    mapper.registerModule(new JSR310Module());
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    Json.setObjectMapper(mapper);

The first and third fixes need to be executed at startup of the application. 在应用程序启动时需要执行第一和第三修复程序。 For play 2.4 the GlobalSettings object has been deprecated, so I'm not sure how they expect you to do this kind of initialization (someone?). 对于游戏2.4,GlobalSettings对象已被弃用,因此我不确定他们希望您如何进行这种初始化(有人吗?)。 However, luckily it will still work. 但是,幸运的是它仍然可以工作。 I of course didn't have a Globals class (no longer created by default), so I created the class and pointed my app to it (from the application.conf file) with the following key: 我当然没有Globals类(默认情况下不再创建),所以我创建了该类,并使用以下键(通过application.conf文件)将我的应用指向该类:

# Minimal global settings to fix form binding and json support of java.time.LocalDate
application.global=GlobalFixes

For your convenience, I post the whole class here, for those who are still learning their way around Playframework 为了方便起见,我将整个课程发布在这里,供那些仍在学习Playframework方式的人使用

import play.*;
import play.data.format.Formatters;
import play.libs.Json;

import java.time.LocalDate;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;

import java.text.ParseException;

/**
 * Fixes for play application. TODO remove this once Play has fixed support for LocalDate
 * @author Sean van Buggenum
 *
 */
public class GlobalFixes extends GlobalSettings
{   // One can see from the javadoc of java.time.LocalDate that the toString guarantees this format, regardless of locale!
    private static final Pattern datePattern = Pattern.compile("(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)");

    public void onStart(Application app)
    {
        //https://groups.google.com/forum/#!topic/play-framework/Wl_ip56111c
        Formatters.register(LocalDate.class, new Formatters.SimpleFormatter<LocalDate>()
        {
            @Override
            public LocalDate parse(String input, Locale l) throws ParseException
            {
                Matcher m = datePattern.matcher(input);
                if (!m.matches())
                    throw new ParseException("No valid Input for date text: " + input, 0);
                return LocalDate.of(Integer.valueOf(m.group(1)), Integer.valueOf(m.group(2)), Integer.valueOf(m.group(3)));
            }

            @Override
            public String print(LocalDate localTime, Locale l)
            {
                return localTime.toString();
            }
        });

        // Add Json's java 8 support
        // http://stackoverflow.com/questions/32872474/how-to-use-java-time-localdate-on-a-play-framework-json-rest/32891177#comment56814598_32891177
        // https://groups.google.com/forum/?hl=en.#!searchin/play-framework/java.time.localdate/play-framework/Dv-IpvBqWgo/l6NTp3e0BQAJ
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Jdk8Module());
        mapper.registerModule(new JSR310Module());
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        Json.setObjectMapper(mapper);
    }
}

I hope that helps some people some time. 我希望这可以帮助一些人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM