Thymeleaf:空列表不显示输入字段

时间:2017-04-13 13:25:34

标签: thymeleaf

我正在尝试创建一个html“编辑/新建”页面来编辑或添加新的发布者。其中一个细节是subPublishers的ArrayList。虽然如果发布者没有子发布者或者我添加新的发布者,ArrayList可能为空。

我的问题在于输入字段。我正在尝试按如下方式显示ArrayList ......

<form id="publisherForm" th:object="${publisherForm}" th:action="@{/publishers/publishers-edit/}" method="post" class="form-horizontal">
    <input type="hidden" th:field="*{id}" />
    <div class="row">
        <div class="col-sm-6 b-r">
            <div class="form-group">
                <label class="col-sm-3 control-label">Publisher name: </label>
                <div class="col-sm-9">
                    <input th:field="*{publisherName}" type="text" class="form-control" th:maxlength="45"/>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">Description: </label>
                <div class="col-sm-9">
                    <input th:field="*{description}" type="text" class="form-control" th:maxlength="200"/>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">Enabled: </label>
                <div class="col-sm-9">
                    <input th:field="*{status}" value="ENABLED" type="checkbox"/>  
                </div>
             </div>
             <div class="form-group">
                <label class="col-sm-3 control-label">Website URL: </label>
                <div class="col-sm-9">
                    <input th:field="*{websiteURL}" type="text" class="form-control" th:maxlength="50"/>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">Subpublishers: </label>
                <div class="col-sm-9">                                                              
                    <div class="table-responsive">
                        <table class="table table-bordered table-striped">
                            <thead>
                                <th>
                                    <button class="btn btn-white" type="submit" name="addRow">+</button>
                                </th>
                            </thead>
                            <tbody>
                                <tr th:each="subPublisher,stat : *{subPublishers}">
                                    <td>
                                        <input type="text" class="form-control" th:field="*{subPublishers[__${stat.index}__].name}" />
                                    </td>
                                    <td>

                                        <button class="btn btn-white" type="submit" name="removeRow" th:value="${stat.index}">-</button>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>                                      
    </div>
    <div class="row">
        <div class="col-sm-6">  
            <div class="form-group">
                <div class="col-sm-9 col-sm-offset-3">
                    <button class="btn btn-primary" type="submit">Save</button>                                                 
                    <a class="btn btn-white" th:href="@{/publishers}">Cancel</a>                                                 
                </div>
            </div>
        </div>
    </div>
</form>

控制器

   @MenuController(value = "/publisher", item = Item.Publishers)
public class PublisherController {

    private PublisherService publisherService;
    private PublisherConverter publisherConverter;
    private SubPublisherConverter subPublisherConverter;

    public PublisherController(PublisherService publisherService, PublisherConverter publisherConverter, SubPublisherConverter subPublisherConverter) {
        this.publisherService = publisherService;
        this.publisherConverter = publisherConverter;
        this.subPublisherConverter = subPublisherConverter;
    }

    @GetMapping
    public String newPublisher( Model model) {

        PublisherResource publisher = new PublisherResource();
        publisher.setStatus(true);
        publisher.setSubPublishers(new ArrayList<SubPublisherResource>());

        return showPage(publisher, model);
    }

    protected String showPage(PublisherResource publisher, Model model) {

        model.addAttribute("publisherForm", publisher);

        return "publishers/publishers-edit";
    }

    @PostMapping
    public String createPublisher(@ModelAttribute("publisherForm") @Validated PublisherResource resource, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return showPage(resource, model);
        }
        return savePublisher(0, resource);
    }

    @GetMapping("/{publisherId}")
    public String editPublisher(@PathVariable int publisherId, Model model) {

        Publisher publisher = publisherService.getPublisher(publisherId);
        PublisherResource res = publisherConverter.convert(publisher);
        res.setSubPublishers(publisherService.getSubPublishers(publisher).stream()
                .map(s -> subPublisherConverter.convert(s))
                .collect(Collectors.toList())
                );

        return showPage(res, model);
    }


    @PostMapping("/{publisherId}")
    public String updatePublisher(@PathVariable int publisherId, @ModelAttribute("publisherForm") @Validated PublisherResource resource, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return showPage(resource, model);
        }
        return savePublisher(publisherId, resource);
    }

    protected String savePublisher(int publisherId, PublisherResource resource) {
        Publisher publisher = populatePublisher(publisherId, resource);
        List<SubPublisher> subPublishers = populateSubPublishers(resource);

        if (publisherId == 0) {
            publisherService.createPublisher(publisher, subPublishers);
        } else {
            publisherService.updatePublisher(publisher, subPublishers);
        }

        return "redirect:/publishers";
    }

    protected Publisher populatePublisher(int publisherId, PublisherResource resource) {
        Publisher publisher = null;
        if (publisherId == 0) {
            publisher = new Publisher();
            publisher.setTimeAdded(new Date());
        } else {
            publisher = publisherService.getPublisher(publisherId);
        }

        publisher.setPublisherName(resource.getPublisherName());
        publisher.setDescription(resource.getDescription());
        publisher.setStatus(resource.isStatus());
        publisher.setWebsiteURL(resource.getWebsiteURL());

        return publisher;
    }

    protected List<SubPublisher> populateSubPublishers(PublisherResource resource){
        if(resource.getSubPublishers() != null){
            return resource.getSubPublishers().stream()
                    .map(s -> {
                        SubPublisher subPublisher = new SubPublisher();
                        subPublisher.setName(s.getName());
                        return subPublisher;
                    })
                    .collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @PostMapping(params={"addRow"})
    public String addRow(final PublisherResource publisher, final BindingResult bindingResult, Model model) {
        publisher.getSubPublishers().add(new SubPublisherResource());
        return showPage(publisher, model);
    }

    @PostMapping(params={"removeRow"})
    public String removeRow(final PublisherResource publisher, final BindingResult bindingResult, 
            final HttpServletRequest req, Model model) {
        final Integer rowId = Integer.valueOf(req.getParameter("removeRow"));
        publisher.getSubPublishers().remove(rowId.intValue());
        return showPage(publisher, model);
    }
}

PublisherResource

    public class PublisherResource {

    private int id;
    private String publisherName;
    private String description;
    private boolean status;
    private String websiteURL;
    private List<SubPublisherResource> subPublishers = new ArrayList<>();

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getPublisherName() {
        return publisherName;
    }
    public void setPublisherName(String publisherName) {
        this.publisherName = publisherName;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public boolean isStatus() {
        return status;
    }
    public void setStatus(boolean status) {
        this.status = status;
    }
    public String getWebsiteURL() {
        return websiteURL;
    }
    public void setWebsiteURL(String websiteURL) {
        this.websiteURL = websiteURL;
    }
    public List<SubPublisherResource> getSubPublishers() {
        return subPublishers;
    }
    public void setSubPublishers(List<SubPublisherResource> subPublishers) {
        this.subPublishers = subPublishers;
    }
}

我使用来自thymeleaf文档http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#dynamic-fields

的exampel

问题是当ArrayList为空时,输入字段不会显示在页面上!此外,当编辑具有子发布者的发布者时,如果我删除所有这些,我会丢失输入字段,并且我无法添加任何其他子发布者。这几乎使得无法添加子发布者。

这是如何处理Thymeleaf的?

更新控制器

@MenuController(value = "/publisher", item = Item.Publishers)
public class PublisherController {

    private PublisherResource publisherResource;
    private PublisherService publisherService;
    private PublisherConverter publisherConverter;
    private SubPublisherConverter subPublisherConverter;


    public PublisherController(PublisherResource publisherResource, PublisherService publisherService,
            PublisherConverter publisherConverter, SubPublisherConverter subPublisherConverter) {
        this.publisherResource = publisherResource;
        this.publisherService = publisherService;
        this.publisherConverter = publisherConverter;
        this.subPublisherConverter = subPublisherConverter;
    }

    @GetMapping
    public String newPublisher( Model model) {

        PublisherResource publisher = new PublisherResource();
        publisher.setStatus(true);
        publisher.setSubPublishers(new ArrayList<SubPublisherResource>());

        return showPage(publisher, model);
    }

    protected String showPage(PublisherResource publisher, Model model) {

        model.addAttribute("publisherForm", publisher);

        return "publishers/publishers-edit";
    }

    @PostMapping
    public String createPublisher(@ModelAttribute("publisherForm") @Validated PublisherResource resource, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return showPage(resource, model);
        }
        return savePublisher(0, resource);
    }

    @GetMapping
    public String editPublisher(Model model) {

        Publisher publisher = publisherService.getPublisher(publisherResource.getId());
        PublisherResource res = publisherConverter.convert(publisher);
        res.setSubPublishers(publisherService.getSubPublishers(publisher).stream()
                .map(s -> subPublisherConverter.convert(s))
                .collect(Collectors.toList())
                );

        return showPage(res, model);
    }


    @PostMapping
    public String updatePublisher( @ModelAttribute("publisherForm") @Validated PublisherResource resource, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return showPage(resource, model);
        }
        return savePublisher(publisherResource.getId(), resource);
    }

    protected String savePublisher(int publisherId, PublisherResource resource) {
        Publisher publisher = populatePublisher(publisherId, resource);
        List<SubPublisher> subPublishers = populateSubPublishers(resource);

        if (publisherId == 0) {
            publisherService.createPublisher(publisher, subPublishers);
        } else {
            publisherService.updatePublisher(publisher, subPublishers);
        }

        return "redirect:/publishers";
    }

    protected Publisher populatePublisher(int publisherId, PublisherResource resource) {
        Publisher publisher = null;
        if (publisherId == 0) {
            publisher = new Publisher();
            publisher.setTimeAdded(new Date());
        } else {
            publisher = publisherService.getPublisher(publisherId);
        }

        publisher.setPublisherName(resource.getPublisherName());
        publisher.setDescription(resource.getDescription());
        publisher.setStatus(resource.isStatus());
        publisher.setWebsiteURL(resource.getWebsiteURL());

        return publisher;
    }

    protected List<SubPublisher> populateSubPublishers(PublisherResource resource){
        if(resource.getSubPublishers() != null){
            return resource.getSubPublishers().stream()
                    .map(s -> {
                        SubPublisher subPublisher = new SubPublisher();
                        subPublisher.setName(s.getName());
                        return subPublisher;
                    })
                    .collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @PostMapping(params={"addRow"})
    public String addRow(final PublisherResource publisher, final BindingResult bindingResult, Model model) {
        publisher.getSubPublishers().add(new SubPublisherResource());
        return showPage(publisher, model);
    }

    @PostMapping(params={"removeRow"})
    public String removeRow(final PublisherResource publisher, final BindingResult bindingResult, 
            final HttpServletRequest req, Model model) {
        final Integer rowId = Integer.valueOf(req.getParameter("removeRow"));
        publisher.getSubPublishers().remove(rowId.intValue());
        return showPage(publisher, model);
    }
}

更新2 - 所有代码:

实体发布商和子发布商:

@Entity
@Table(name = "publisher")
public class Publisher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @Column(name = "name")
    private String publisherName;

    @Column(name = "description")
    private String description;

    @Column(name = "status")
    private boolean status;

    @Column(name = "website_url")
    private String websiteURL;

    @Column(name = "time_added") 
    private Date timeAdded;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPublisherName() {
        return publisherName;
    }

    public void setPublisherName(String publisherName) {
        this.publisherName = publisherName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public String getWebsiteURL() {
        return websiteURL;
    }

    public void setWebsiteURL(String websiteURL) {
        this.websiteURL = websiteURL;
    }

    public Date getTimeAdded() {
        return timeAdded;
    }

    public void setTimeAdded(Date timeAdded) {
        this.timeAdded = timeAdded;
    }
}

@Entity
@Table(name = "sub_publisher")
public class SubPublisher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "publisher_id")
    private Publisher publisher;

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Publisher getPublisher() {
        return publisher;
    }

    public void setPublisher(Publisher publisher) {
        this.publisher = publisher;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

存储库

public interface PublisherRepository extends JpaDataTableRepository<Publisher, Integer> {

    public Publisher findById(int id);
}

public interface SubPublisherRepository extends JpaRepository<SubPublisher, Integer> {

    List<SubPublisher> findByPublisher(Publisher publisher);
    void deleteByPublisher(Publisher publisher);
}

PublisherResource

public class PublisherResource {

    private int id;
    private String publisherName;
    private String description;
    private boolean status;
    private String websiteURL;
    private List<SubPublisherResource> subPublishers = new ArrayList<>();

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getPublisherName() {
        return publisherName;
    }
    public void setPublisherName(String publisherName) {
        this.publisherName = publisherName;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public boolean isStatus() {
        return status;
    }
    public void setStatus(boolean status) {
        this.status = status;
    }
    public String getWebsiteURL() {
        return websiteURL;
    }
    public void setWebsiteURL(String websiteURL) {
        this.websiteURL = websiteURL;
    }
    public List<SubPublisherResource> getSubPublishers() {
        return subPublishers;
    }
    public void setSubPublishers(List<SubPublisherResource> subPublishers) {
        this.subPublishers = subPublishers;
    }
}

SubPublisherResource

public class SubPublisherResource {

    private String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

PublisherController

@Controller
@RequestMapping("/publisher")
public class PublisherController {

    private PublisherService publisherService;
    private PublisherConverter publisherConverter;
    private SubPublisherConverter subPublisherConverter;

    @Autowired
    public PublisherController(PublisherService publisherService, PublisherConverter publisherConverter, SubPublisherConverter subPublisherConverter) {
        this.publisherService = publisherService;
        this.publisherConverter = publisherConverter;
        this.subPublisherConverter = subPublisherConverter;
    }

    @GetMapping
    public String newPublisher(Model model) {
        PublisherResource publisher = new PublisherResource();
        publisher.setStatus(true);
        return showPage(publisher, model);
    }

    protected String showPage(PublisherResource publisher, Model model) {
        model.addAttribute("publisherForm", publisher);
        return "/publishers/publishers-edit";
    }

    @PostMapping
    public String createPublisher(@ModelAttribute("publisherForm") @Validated PublisherResource resource, BindingResult result, Model model) {
        if(result.hasErrors()) {
            return showPage(resource, model);
        }
        return savePublisher(resource);
    }

    @GetMapping("/{id}")
    public String editPublisher(@PathVariable("id") Integer id, Model model) {
        Publisher publisher = publisherService.getPublisher(id);
        PublisherResource res = publisherConverter.convert(publisher);
        res.setSubPublishers(publisherService.getSubPublishers(publisher).stream().map(s->subPublisherConverter.convert(s)).collect(Collectors.toList()));
        return showPage(res, model);
    }

    protected String savePublisher(PublisherResource resource) {
        Publisher publisher = populatePublisher(resource);
        List<SubPublisher> subPublishers = populateSubPublishers(resource);
        if(resource.getId() == 0) {
            publisherService.createPublisher(publisher, subPublishers);
        } else {
            publisherService.updatePublisher(publisher, subPublishers);
        }
        return "redirect:/publishers";
    }

    protected Publisher populatePublisher(PublisherResource resource) {
        Publisher publisher;
        if(resource.getId() == 0) {
            publisher = new Publisher();
            publisher.setTimeAdded(new Date());
        } else {
            publisher = publisherService.getPublisher(resource.getId());
        }
        publisher.setPublisherName(resource.getPublisherName());
        publisher.setDescription(resource.getDescription());
        publisher.setStatus(resource.isStatus());
        publisher.setWebsiteURL(resource.getWebsiteURL());
        return publisher;
    }

    protected List<SubPublisher> populateSubPublishers(PublisherResource resource) {
        if(resource.getSubPublishers() != null) {
            return resource.getSubPublishers().stream().map(s->{
                SubPublisher subPublisher = new SubPublisher();
                subPublisher.setName(s.getName());
                return subPublisher;
            }).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @PostMapping(params = { "addRow" })
    public String addRow(final PublisherResource publisher, Model model) {
        publisher.getSubPublishers().add(new SubPublisher());
        return showPage(publisher, model);
    }

    @PostMapping(params = { "removeRow" })
    public String removeRow(final PublisherResource publisher, final HttpServletRequest req, Model model) {
        final Integer rowId = Integer.valueOf(req.getParameter("removeRow"));
        publisher.getSubPublishers().remove(rowId.intValue());
        return showPage(publisher, model);
    }
}

PublishersController

@MenuController(value = "/publishers", item = Item.Publishers)
public class PublishersController {

    private PublisherService publisherService;
    private PublisherConverter publisherConverter;

    public PublishersController(PublisherService publisherService, PublisherConverter publisherConverter) {
        this.publisherService = publisherService;
        this.publisherConverter = publisherConverter;
    }

    @GetMapping
    public String showPage() {
        return "publishers/publishers";
    }

    @PostMapping("/data")
    public @ResponseBody DataTableResponse<PublisherResource> getPublishers(@RequestBody DataTableRequest request) {
        return publisherConverter.convertResponse(publisherService.getPublishers(request));
    }

    @DeleteMapping("/{publisherIds}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deletePublishers(@PathVariable Integer[] publisherIds) {
        for (Integer publisherId: publisherIds) {
            publisherService.deletePublisher(publisherId);
        }
    }

    @PutMapping("/{publisherIds}/disable")
    @ResponseStatus(HttpStatus.ACCEPTED)
    public void disablePublishers(@PathVariable Integer[] publisherIds) {
        changeStatus(publisherIds);
    }

    @PutMapping("/{publisherIds}/enable")
    @ResponseStatus(HttpStatus.ACCEPTED)
    public void enablePublishers(@PathVariable Integer[] publisherIds) {
        changeStatus(publisherIds);
    }

    protected void changeStatus(Integer[] publisherIds) {
        for (Integer publisherId: publisherIds) {
            publisherService.updateStatus(publisherId);
        }
    }
}

PublisherConverter

@Component
public class PublisherConverter  implements ResourceConverter<Publisher, PublisherResource> {

    public PublisherResource convert(Publisher publisher) {

        PublisherResource resource = new PublisherResource();

        resource.setId(publisher.getId());
        resource.setPublisherName(publisher.getPublisherName());
        resource.setDescription(publisher.getDescription());
        resource.setStatus(publisher.isStatus());
        resource.setWebsiteURL(publisher.getWebsiteURL());

        return resource;
    }
}

PublisherService

@Service
public class PublisherService {

    private PublisherRepository publisherRepository;
    private SubPublisherRepository subPublisherRepository;


    public PublisherService(PublisherRepository publisherRepository, SubPublisherRepository subPublisherRepository) {
        this.publisherRepository = publisherRepository;
        this.subPublisherRepository = subPublisherRepository;
    }

    public DataTableResponse<Publisher> getPublishers(DataTableRequest request) {   

        return publisherRepository.findAll(request); 
    }

    @Transactional
    public Publisher getPublisher(int publisherId) {
        Publisher publisher = publisherRepository.findById(publisherId);        

        if(publisher != null){
            List<SubPublisher> subPublishers = subPublisherRepository.findByPublisher(publisher);
            //populateSubPublishers(subPublishers);

            subPublishers.stream().forEach(s -> {
                if(s.getId() == 0){
                    s.setPublisher(publisher);
                    subPublisherRepository.save(s);
                }
            });
        }

        if(publisher == null) {
            throw new NotFoundException("Publisher " + publisherId + " not found.");
        }

        return publisher;
    }

    private void populateSubPublishers(List<SubPublisher> subPublishers) {
        for(SubPublisher subPublisher : subPublishers){
            subPublishers.add(subPublisher);
            System.out.println(subPublisher.getName());
        }
    }

    @Transactional
    public Publisher createPublisher(Publisher publisher, List<SubPublisher> subPublishers) {

        publisher = publisherRepository.save(publisher);
        createSubPublisher(publisher, subPublishers);       

        return publisher;
    }

    private void createSubPublisher(Publisher publisher, List<SubPublisher> subPublishers) {
        populateSubPublishers(subPublishers);

        for(SubPublisher subPublisher : subPublishers){
            subPublisher.setPublisher(publisher);
            subPublisher.setName(subPublisher.getName());
        }
    }

    @Transactional
    public Publisher updatePublisher(Publisher publisher, List<SubPublisher> subPublishers) {

        publisher = publisherRepository.save(publisher);
        createSubPublishers(publisher, subPublishers);
        return publisher;
    }

    private void createSubPublishers(Publisher publisher, List<SubPublisher> subPublishers) {

        subPublisherRepository.deleteByPublisher(publisher);

        for (SubPublisher sp : subPublishers) {
            sp.setPublisher(publisher);
            sp = subPublisherRepository.save(sp);
        }
    }

    @Transactional
    public void updateStatus(int publisherId) {
        Publisher publisher = publisherRepository.findOne(publisherId);
        if(publisher != null && publisher.isStatus() != false){
            publisher.setStatus(false);
            publisherRepository.save(publisher);
        }
        else if(publisher != null && publisher.isStatus() != true ){
            publisher.setStatus(true);
            publisherRepository.save(publisher);
        }
    }

    @Transactional
    public void deletePublisher(int publisherId) {
        Publisher publisher = publisherRepository.findOne(publisherId);
        if (publisher != null) {
            publisherRepository.delete(publisher);
        }
    }

    public List<SubPublisher> getSubPublishers(Publisher publisher){
        return subPublisherRepository.findByPublisher(publisher);
    }
}

SubPublisherConverter

@Component
public class SubPublisherConverter implements ResourceConverter<SubPublisher, SubPublisherResource> {

    @Override
    public SubPublisherResource convert(SubPublisher subPublisher) {

        SubPublisherResource resource = new SubPublisherResource();
        resource.setName(subPublisher.getName());

        return resource;
    }
}

错误

java.util.ConcurrentModificationException: null
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at publishers.PublisherService.populateSubPublishers(PublisherService.java:58)
    at publishers.PublisherService.createSubPublisher(PublisherService.java:74)
    at publishers.PublisherService.createPublisher(PublisherService.java:68)

1 个答案:

答案 0 :(得分:0)

所以这是一个例子。您需要将控制器(/test/test)的映射替换为/publishers/publishers-edit。控制器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/test/test")
public class PublisherController {
    private PublisherService publisherService;
    private PublisherConverter publisherConverter;
    private SubPublisherConverter subPublisherConverter;

    @Autowired
    public PublisherController(PublisherService publisherService, PublisherConverter publisherConverter, SubPublisherConverter subPublisherConverter) {
        this.publisherService = publisherService;
        this.publisherConverter = publisherConverter;
        this.subPublisherConverter = subPublisherConverter;
    }

    @GetMapping
    public String newPublisher(Model model) {
        PublisherResource publisher = new PublisherResource();
        publisher.setStatus(true);
        return showPage(publisher, model);
    }

    protected String showPage(PublisherResource publisher, Model model) {
        model.addAttribute("publisherForm", publisher);
        return "test/test";
    }

    @PostMapping
    public String createPublisher(@ModelAttribute("publisherForm") @Validated PublisherResource resource, BindingResult result, Model model) {
        if(result.hasErrors()) {
            return showPage(resource, model);
        }
        return savePublisher(resource);
    }

    @GetMapping("/{id}")
    public String editPublisher(@PathVariable("id") Integer id, Model model) {
        Publisher publisher = publisherService.getPublisher(id);
        PublisherResource res = publisherConverter.convert(publisher);
        res.setSubPublishers(publisherService.getSubPublishers(publisher).stream().map(s->subPublisherConverter.convert(s)).collect(Collectors.toList()));
        return showPage(res, model);
    }

    protected String savePublisher(PublisherResource resource) {
        Publisher publisher = populatePublisher(resource);
        List<SubPublisher> subPublishers = populateSubPublishers(resource);
        if(resource.getId() == 0) {
            publisherService.createPublisher(publisher, subPublishers);
        } else {
            publisherService.updatePublisher(publisher, subPublishers);
        }
        return "redirect:/publishers";
    }

    protected Publisher populatePublisher(PublisherResource resource) {
        Publisher publisher;
        if(resource.getId() == 0) {
            publisher = new Publisher();
            publisher.setTimeAdded(new Date());
        } else {
            publisher = publisherService.getPublisher(resource.getId());
        }
        publisher.setPublisherName(resource.getPublisherName());
        publisher.setDescription(resource.getDescription());
        publisher.setStatus(resource.isStatus());
        publisher.setWebsiteURL(resource.getWebsiteURL());
        return publisher;
    }

    protected List<SubPublisher> populateSubPublishers(PublisherResource resource) {
        if(resource.getSubPublishers() != null) {
            return resource.getSubPublishers().stream().map(s->{
                SubPublisher subPublisher = new SubPublisher();
                subPublisher.setName(s.getName());
                return subPublisher;
            }).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @PostMapping(params = { "addRow" })
    public String addRow(@ModelAttribute("publisherForm") final PublisherResource publisher, Model model) {
        publisher.getSubPublishers().add(new SubPublisher());
        return showPage(publisher, model);
    }

    @PostMapping(params = { "removeRow" })
    public String removeRow(@ModelAttribute("publisherForm") final PublisherResource publisher, final HttpServletRequest req, Model model) {
        final Integer rowId = Integer.valueOf(req.getParameter("removeRow"));
        publisher.getSubPublishers().remove(rowId.intValue());
        return showPage(publisher, model);
    }
}

<强>更新

我清理了您的PublisherService并将其拆分为PublisherService和SubPublisherService。那些服务是可能的。可选,但我更喜欢在大多数情况下在存储库和我的程序之间有另一层。

我还清理了PublisherController。 savePublisher改变了很多。它记录在案。

发布商现在还有一个包含发布服务器的所有SubPublishers的列表。上一页。你是通过代码获取这个。但JPA可以为您做到这一点。

PublisherController:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Controller
@RequestMapping("/test/test")
public class PublisherController {
    private PublisherService publisherService;
    private SubPublisherService subPublisherService;

    @Autowired
    public PublisherController(PublisherService publisherService, SubPublisherService subPublisherService) {
        this.publisherService = publisherService;
        this.subPublisherService = subPublisherService;
    }

    private ModelAndView showPage(PublisherResource publisher) {
        final ModelAndView modelAndView = new ModelAndView("test/test");
        modelAndView.addObject("publisherForm", publisher);
        return modelAndView;
    }

    @GetMapping
    public ModelAndView createPublisher() {
        PublisherResource publisher = new PublisherResource();
        publisher.setStatus(true);
        return showPage(publisher);
    }

    @GetMapping("/{id}")
    public ModelAndView editPublisher(@PathVariable("id") Integer id) {
        Publisher publisher = publisherService.getPublisher(id);
        return showPage(PublisherConverter.convert(publisher));
    }

    @PostMapping
    public ModelAndView savePublisher(@ModelAttribute("publisherForm") @Validated PublisherResource resource, BindingResult result) {
        if(result.hasErrors()) {
            return showPage(resource);
        }
        return savePublisher(resource);
    }

    private ModelAndView savePublisher(PublisherResource resource) {
        // create the publisher
        Publisher publisher;
        if(resource.getId() == 0) {
            publisher = new Publisher();
            publisher.setTimeAdded(new Date());
            publisher = publisherService.save(publisher);
        } else {
            publisher = publisherService.getPublisher(resource.getId());
        }

        /* -- this will update the SubPublishers -- */
        final List<SubPublisher> toDelete = publisher.getSubPublishers();
        final List<SubPublisher> toSave = new ArrayList<>();
        final Publisher forLambda = publisher;
        // first we will iterate over all the SubPublishers that where specified by the user (in the form)
        resource.getSubPublishers().forEach(name->{
            // we will then try to find any existing SubPublisher for the given name and publisher (to avoid duplicated database entries)
            final SubPublisher subPublisher = subPublisherService.getOrCreateSubPublisher(forLambda, name);
            // we will also save the SubPublisher reference for later
            toSave.add(subPublisher);
            // and then we will remove the SubPublisher that we just found from the List of SubPublishers from the original Publisher (that was stored in the db)
            toDelete.removeIf(s->subPublisher.getId() == s.getId());
        });
        // effectively it will leave us with a list (toDelete) that will include all SubPublishers that have to be deleted
        for(SubPublisher subPublisher : toDelete) {
            // and this is what we will to here
            subPublisherService.delete(subPublisher);
        }
        // after we cleaned up all unused references
        publisher.getSubPublishers().clear();
        // update the subPublisher list to contain all SubPublishers that where specified by the user input (the parsed ones)
        publisher.getSubPublishers().addAll(toSave);
        /* -- end -- */

        // update all remaining fields
        publisher.setPublisherName(resource.getPublisherName());
        publisher.setDescription(resource.getDescription());
        publisher.setStatus(resource.isStatus());
        publisher.setWebsiteURL(resource.getWebsiteURL());
        publisherService.save(publisher);
        return new ModelAndView("redirect:/test/list");
    }

    @PostMapping(params = { "addRow" })
    public ModelAndView addRow(@ModelAttribute("publisherForm") final PublisherResource publisher) {
        publisher.getSubPublishers().add("");
        return showPage(publisher);
    }

    @PostMapping(params = { "removeRow" })
    public ModelAndView removeRow(@ModelAttribute("publisherForm") final PublisherResource publisher, final HttpServletRequest req) {
        final Integer rowId = Integer.valueOf(req.getParameter("removeRow"));
        publisher.getSubPublishers().remove(rowId.intValue());
        return showPage(publisher);
    }
}

出版商:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Entity
@Table
public class Publisher {
    private int id;
    private String publisherName;
    private String description;
    private boolean status;
    private String websiteURL;
    private Date timeAdded;
    private List<SubPublisher> subPublishers = new ArrayList<>();

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column
    public String getPublisherName() {
        return publisherName;
    }

    public void setPublisherName(String publisherName) {
        this.publisherName = publisherName;
    }

    @Column
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Column
    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    @Column
    public String getWebsiteURL() {
        return websiteURL;
    }

    public void setWebsiteURL(String websiteURL) {
        this.websiteURL = websiteURL;
    }

    @Column
    public Date getTimeAdded() {
        return timeAdded;
    }

    public void setTimeAdded(Date timeAdded) {
        this.timeAdded = timeAdded;
    }


    @OneToMany(mappedBy = "publisher")
    public List<SubPublisher> getSubPublishers() {
        return subPublishers;
    }

    public void setSubPublishers(List<SubPublisher> subPublishers) {
        this.subPublishers = subPublishers;
    }

    @Override
    public String toString() {
        return "Publisher{" + "id=" + id + ", publisherName='" + publisherName + '\'' + ", description='" + description + '\'' + ", status=" + status + ", websiteURL='" + websiteURL + '\'' + ", timeAdded=" + timeAdded + ", subPublishers=" + subPublishers + '}';
    }
}

PublisherResource:

import java.util.ArrayList;
import java.util.List;

public class PublisherResource {
    private int id;
    private String publisherName;
    private String description;
    private boolean status;
    private String websiteURL;
    private List<String> subPublishers = new ArrayList<>();

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPublisherName() {
        return publisherName;
    }

    public void setPublisherName(String publisherName) {
        this.publisherName = publisherName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public String getWebsiteURL() {
        return websiteURL;
    }

    public void setWebsiteURL(String websiteURL) {
        this.websiteURL = websiteURL;
    }

    public List<String> getSubPublishers() {
        return subPublishers;
    }

    public void setSubPublishers(List<String> subPublishers) {
        this.subPublishers = subPublishers;
    }
}

PublisherService:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PublisherService {
    private PublisherRepository publisherRepository;

    @Autowired
    public void setPublisherRepository(PublisherRepository publisherRepository) {
        this.publisherRepository = publisherRepository;
    }

    public Publisher save(Publisher toSave) {
        return publisherRepository.save(toSave);
    }

    public void delete(Publisher toDelete) {
        publisherRepository.delete(toDelete);
    }

    public Publisher getPublisher(Integer id) {
        return publisherRepository.findOne(id);
    }

    public Iterable<Publisher> getAll() {
        return publisherRepository.findAll();
    }
}

PublisherRepository:

import org.springframework.data.repository.CrudRepository;

public interface PublisherRepository extends CrudRepository<Publisher, Integer> {}

PublisherConverter:

import java.util.stream.Collectors;

public class PublisherConverter {
    public static PublisherResource convert(Publisher publisher) {
        PublisherResource resource = new PublisherResource();
        resource.setId(publisher.getId());
        resource.setPublisherName(publisher.getPublisherName());
        resource.setDescription(publisher.getDescription());
        resource.setStatus(publisher.isStatus());
        resource.setWebsiteURL(publisher.getWebsiteURL());
        resource.getSubPublishers().addAll(publisher.getSubPublishers().stream().map(SubPublisher::getName).collect(Collectors.toList()));
        return resource;
    }
}

SubPublisher:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table
public class SubPublisher {
    private int id;
    private Publisher publisher;
    private String name;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "publisherId")
    public Publisher getPublisher() {
        return publisher;
    }

    public void setPublisher(Publisher publisher) {
        this.publisher = publisher;
    }

    @Column
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

SubPublisherRepository:

import org.springframework.data.repository.CrudRepository;

public interface SubPublisherRepository extends CrudRepository<SubPublisher, Integer> {
    SubPublisher findByPublisherAndAndName(Publisher publisher, String name);
}

SubPublisherService:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SubPublisherService {
    private SubPublisherRepository subPublisherRepository;

    @Autowired
    public void setSubPublisherRepository(SubPublisherRepository subPublisherRepository) {
        this.subPublisherRepository = subPublisherRepository;
    }

    public SubPublisher save(SubPublisher toSave) {
        return subPublisherRepository.save(toSave);
    }

    public void delete(SubPublisher toDelete) {
        subPublisherRepository.delete(toDelete);
    }

    /**
     * This method will either find any existing SubPublisher by the Name and the Publisher or create one if none existed.
     */
    public SubPublisher getOrCreateSubPublisher(Publisher publisher, String name) {
        SubPublisher subPublisher = subPublisherRepository.findByPublisherAndAndName(publisher, name);
        if(subPublisher == null) {
            subPublisher = new SubPublisher();
            subPublisher.setPublisher(publisher);
            subPublisher.setName(name);
            subPublisher = save(subPublisher);
        }
        return subPublisher;
    }

    public Iterable<SubPublisher> getAll() {
        return subPublisherRepository.findAll();
    }
}

在html中,我将<input type="text" class="form-control" th:field="*{subPublishers[__${stat.index}__].name}" />更改为<input type="text" class="form-control" th:field="*{subPublishers[__${stat.index}__].}" />