通过Spring + Hibernate创建新记录的正确方法

时间:2019-05-19 18:22:31

标签: hibernate spring-mvc jpa

我正在尝试创建新对象并将其持久保存在DB中。在项目中,我有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;
}

BaseEntity:

@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"

在调试日志级别,有以下日志:

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;

我无法在google中找到任何解决方案,这就是为什么我在这里发布问题。

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

2 个答案:

答案 0 :(得分:0)

尝试在实体的// super(id);构造函数中注释掉@builder。通过这样做,您的表单将显式设置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=&maltName=Dupa&producer.id=1&country.id=1&maltFilling=12&maltUsage=csd&maltEbc=34]

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

答案 1 :(得分:0)

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

Id未设置,这导致了错误。更改th:action后,它开始起作用。