【问题标题】:Proper way of creating new record via Spring + Hibernate通过 Spring + Hibernate 创建新记录的正确方法
【发布时间】:2019-05-19 18:22:31
【问题描述】:

我正在尝试创建新对象并将其保存在数据库中。在项目中,我有 Spring-boot、Hibernate 和 Thymeleaf。

为了提供数据,我使用这种形式:

<body>  
    <form th:object="${malt}" th:action="@{/malt/}" method="post">
        <input type="hidden" th:field="*{id}" />
            <label>Malt name:</label>

            <input type="text" class="form-control" th:field="*{maltName}" />
                <label>Producer:</label>
                <input type="text" class="form-control"
                    th:field="*{producer.producerName}" />

                <label>Country:</label>
                <select class="form-control" th:field="*{country.id}">
                    <option value="0">Select country</option>
                    <option th:each="country : ${countries}"
                        th:value="${country?.id}"
                        th:text="${country?.countryName}">
                        </option>
                </select>

                <label>Malt filling:</label>
                <input type="text" class="form-control"
                    th:field="*{maltFilling}" />

                <label>Malt usage:</label>
                <input type="text" class="form-control"
                    th:field="*{maltUsage}" />

                <label>Malt EBC:</label>
                <input type="number" class="form-control"
                    th:field="*{maltEbc}" />


                <button class="submit-button" type="submit">Submit</button>
    </form>
</body>

我要持久化的对象:

@Setter
@Getter
@NoArgsConstructor
@Entity
@ToString
@Table(name="malt")
public class Malt extends BaseEntity {

    @Column(name="malt_name")
    private String maltName;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="producer_id")
    private Producer producer;

    @Column(name="malt_filling")
    private int maltFilling;

    @Column(name="malt_ebc")
    private int maltEbc;

    @Column(name="malt_usage")
    private String maltUsage;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="country_id")
    private Country country;

    @ManyToMany(mappedBy="malts")
    private Set<Batch> batches;

    @Builder
    public Malt(Long id, String maltName, String producerName, Country country, Producer producer, int maltFilling, int maltEbc, String maltUsage) {
        super(id);
        this.maltName = maltName;
        this.producer = producer;
        this.maltFilling = maltFilling;
        this.maltEbc = maltEbc;
        this.maltUsage = maltUsage;
        this.country = country;
}

基础实体:

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public class BaseEntity implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    public boolean isNew() {
        return this.id == null;
    }
}

MaltController 的一部分:

@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
    dataBinder.setDisallowedFields("id");
}

@GetMapping("/new")
public String initCreationForm(Model model) {

    Malt malt = new Malt();
    model.addAttribute("malt", malt);

    return VIEWS_MALT_CREATE_OR_UPDATE_FORM;
}

@PostMapping("/new")
public String  processCreationForm(@Valid Malt malt, BindingResult result, ModelMap model) {

    if (StringUtils.hasLength(malt.getMaltName()) && malt.isNew()) {
        result.rejectValue("maltName", "duplicate", "already exists");
    }

    if (result.hasErrors()) {
        model.put("malt", malt);
        return VIEWS_MALT_CREATE_OR_UPDATE_FORM;
    } else {
        Malt savedMalt = maltService.save(malt);
        return "redirect:/malt/" + savedMalt.getId();
    }
}

@GetMapping("/{maltId}")
public ModelAndView showMalt(@PathVariable("maltId") Long maltId) {
    ModelAndView mav = new ModelAndView("malt/malt-show");
    mav.addObject(maltService.findById(maltId));
    return mav;
}

当我点击Submit 按钮时,我收到以下错误:

There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "null"
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "null" 
Caused by: java.lang.NumberFormatException: For input string: "null"

在 DEBUG 日志级别,有这样的日志:

Field [id] has been removed from PropertyValues and will not be bound, because it has not been found in the list of allowed fields

Start processing with input [id=&maltName=Dupa&producer.id=1&country.id=1&maltFilling=12&maltUsage=csd&maltEbc=34]

完整的堆栈跟踪:

There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "null"
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "null"
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:132)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:126)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:166)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NumberFormatException: For input string: "null"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Long.parseLong(Long.java:589)
    at java.lang.Long.valueOf(Long.java:803)
    at org.springframework.util.NumberUtils.parseNumber(NumberUtils.java:214)
    at org.springframework.beans.propertyeditors.CustomNumberEditor.setAsText(CustomNumberEditor.java:115)
    at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:429)
    at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:402)
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:155)
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73)
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:53)
    at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:693)
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:124)
    ... 51 more

我可以假设发生了这个错误,因为没有生成ID。这很奇怪,因为在BaseEntity 我有这个:

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

我无法在谷歌中找到任何解决方案,这就是我在这里发布问题的原因。

链接到我 git 的整个项目:https://github.com/fangirsan/maruszka-new

【问题讨论】:

  • 你为什么使用 WebDataBinder ?
  • 避免对该字段进行不必要的修改。这就是为什么我不能填充id 字段的原因?如果是,我该如何更改它以保留避免不必要修改的可能性?
  • 没关系。我只是想知道原因。你能发布整个堆栈跟踪吗?
  • 我现在在上班,不过是的,我马上就到家了。
  • 主线程中的完整堆栈跟踪

标签: hibernate spring-mvc jpa


【解决方案1】:

尝试在实体的 @builder 构造函数中注释掉 // super(id);。通过这样做,您的表单将明确设置 id,并且不应在 POST 调用期间这样做。它必须由持久层生成。错误是这样说的-Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "null"

您的调试跟踪也显示了这一点:

Start processing with input [id=&amp;maltName=Dupa&amp;producer.id=1&amp;country.id=1&amp;maltFilling=12&amp;maltUsage=csd&amp;maltEbc=34]

它在 ID 中传递空/空白字符串。

【讨论】:

  • 通常,我绑定一个表单 pojo 并且在 Post 调用期间不从表单传递 ID。您可以从表单中删除 并尝试一下吗?此外,将实体直接绑定到表单不是一个好主意:)
  • 嗨@Fangir,检查这个stackoverflow.com/questions/24191849/…,这正是你的问题。
  • 我已经注释掉了&lt;input type="hidden" th:field="*{id}" /&gt;@InitBinder,但错误仍然出现
【解决方案2】:

问题在于: &lt;form th:object="${malt}" th:action="@{${'/malt/' + malt.id + '/edit'}}" method="post"&gt;

Id 未设置,这导致了错误。当我更改 th:action 后,它就开始工作了。

【讨论】:

  • 你在 th:action 中做了什么改变?你把它改成这个了吗--> th:action="@{/malt/}"
  • 我不能说,我知道问题出在哪里,但现在我有了映射和正确路径的探针
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-01
  • 1970-01-01
  • 2020-06-20
  • 2017-01-24
  • 2019-08-12
  • 1970-01-01
相关资源
最近更新 更多