首页 > 解决方案 > Null Objects after submitting Form in Spring (H2, SpringBoot, Thymeleaf, CRUD)

问题描述

I am creating an Spring Boot App, in which the user can create licences. The user can type the name, purchaseDate, renewalDate and expirationDate.

My problem is, that when I try to save the data, it returns null for the name, the purchaseDate, renewalDate and expirationDate.

I absolutely don't know what to do anymore, please help. The "// Comment" values in the Model:Licence are those who get null when executing the post.

Model: Licence

@Entity
@Table(name = "TBL_LICENCES")
public class Licence {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long Id;
    private String licenceName;  // --> null after the post
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private String purchaseDate; // --> null after the post
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private String renewalDate; // --> null after the post
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private String expirationDate; // --> null after the post

    public Licence() {}
    public Licence(Long id, String licenceName, String purchaseDate, String renewalDate, String expirationDate) {
        Id = id;
        this.licenceName = licenceName;
        this.purchaseDate = purchaseDate;
        this.renewalDate = renewalDate;
        this.expirationDate = expirationDate;
    }

    // Getter and Setter
}

Model: LicenceRepository

@Repository
public interface LicenceRepository extends CrudRepository<Licence, Long> { }

Service: LicenceService

@Service
public class LicenceService {
    @Autowired
    private LicenceRepository licenceRepository;

    public List<Licence> getLicences() {
        return (List<Licence>) licenceRepository.findAll();
    }

    public Optional<Licence> getLicenceById(Long id) {
        return licenceRepository.findById(id);
    }

    public void addLicence(Licence licence) {
        licenceRepository.save(licence);
    }

    public void updateLicence(Licence licence) {
        licenceRepository.save(licence);
    }

    public void deleteLicenceById(Long id) {
        licenceRepository.deleteById(id);
    }
}

Controller: LicenceController

@Controller
public class LicenceController {
    @Autowired
    private LicenceService licenceService;

    @GetMapping("/licences")
    public String getLicences(Model model) {
        model.addAttribute("licences", licenceService.getLicences());
        return "licences";
    }

    @GetMapping("/onelicence")
    @ResponseBody
    public Optional<Licence> getLicenceByID(Long id, Model model) {
        model.addAttribute("onelicence", licenceService.getLicenceById(id));
        return licenceService.getLicenceById(id);
    }

    @RequestMapping(value="/save", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
    public String updateLicence(Licence licence) {
        licenceService.updateLicence(licence);
        return "redirect:/licences";
    }

    **// Is performed, when clicking "New Licence > Save"**
    @RequestMapping(value="/addNew", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
    public String addLicence(Licence licence) {
        licenceService.addLicence(licence);
        return "redirect:/licences";
    }

    @RequestMapping(value="/delete", method = {RequestMethod.DELETE, RequestMethod.PUT, RequestMethod.GET})
    public String deleteLicence(Long id) {
        licenceService.deleteLicenceById(id);
        return "redirect:/licences";
    }
}

licences.html

<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/html"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <script type ="text/javascript" src="webjars/jquery/3.4.1/jquery.min.js"></script>
    <script type ="text/javascript" src="webjars/bootstrap/4.4.1/js/bootstrap.min.js"></script>
    <link href="webjars/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet"/>
    <title>Licence</title>
</head>
<body>
<div class = "container">
    <h2>Licences</h2>
    <table class = "table table-striped">
        <thead>
            <tr>
                <td>ID</td>
                <td>Name</td>
                <td>Kaufdatum</td>
                <td>Erneuerungsdatum:</td>
                <td>Auslaufdatum:</td>
            </tr>
        </thead>
        <tbody>
            <tr th:each = "licence: ${licences}">
                <td th:text="${licence.id}">ID</td>
                <td th:text="${licence.licenceName}">Name</td>
                <td th:text="${licence.purchaseDate}">Kaufdatum</td>
                <td th:text="${licence.renewalDate}">Erneuerungsdatum</td>
                <td th:text="${licence.expirationDate}">Auslaufdatum</td>
                <td><a class="btn btn-warning">Edit</a></td>
                <td><a class="btn btn-danger">Delete</a></td>
            </tr>
        </tbody>
    </table>
    <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addModal" data-whatever="@mdo">New Licence</button>
</div>

// Bootstrap Varying Modal Content

<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <form th:action="@{/addNew}" method ="post">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="exampleModalLabel">New Licence</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>

            <div class="modal-body">
                    <div class="form-group">
                        <label for="name" class="col-form-label">Lizenz Name</label>
                        <input type="text" class="form-control" id="licenceName" name="name">
                    </div>
                    <div class="form-group">
                        <label for="purchase" class="col-form-label">purchaseDate</label>
                        <input type="date" class="form-control" id="purchaseDate" name="purchase">
                    </div>
                    <div class="form-group">
                        <label for="renewalAdd" class="col-form-label">renewalDate</label>
                        <input type="date" class="form-control" id="renewalAdd" name="renewal">
                    </div>
                    <div class="form-group">
                        <label for="expirationAdd" class="col-form-label">expirationDate</label>
                        <input type="date" class="form-control" id="expirationAdd" name="expiration">
                    </div>
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                <button type="submit" class="btn btn-primary">Save</button>
            </div>

        </div>
    </div>
    </form>
</div>
</body>
</html>

schema.sql

DROP TABLE IF EXISTS TBL_LICENCES;

CREATE TABLE TBL_LICENCES (
  id INT AUTO_INCREMENT  PRIMARY KEY,
  licence_name VARCHAR(250),
  purchase_date VARCHAR(250),
  renewal_date VARCHAR(250),
  expiration_date VARCHAR(250)
);

data.sql --> This works and the data is shown.

INSERT INTO
    TBL_LICENCES (licence_name, purchase_date, renewal_date, expiration_date)
VALUES
    ('Test1', '2020-01-31', '2020-06-31', '2020-12-31'),
    ('Test', '2021-01-31', '2021-06-31', '2021-12-31');

Properties: application.properties

spring.h2.console.enabled=true
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:<dbLicences>
spring.jpa.hibernate.ddl-auto=update

标签: javaspring-bootthymeleafh2

解决方案


The name attribute of the input control should match the name of the fields in the Licence class. Currently your id attribute is matching the name of the fields, but when a form is submitted it uses the name attribute to build the request parameters.

Update your HTML to match something like:

<input type="text" class="form-control" id="licenceName" name="licenceName" />

Fix the name for other fields and you will have the Licence object populated with data from the form.

Also, I see that

@DateTimeFormat(pattern = "yyyy-MM-dd")
private String licenceName;

licenceName is annotated with @DateTimeFormat. I guess this is a mistake, please correct that as well.


推荐阅读