Spring 选择多对多x的所有元素将导致多对多y中的重复

Spring 选择多对多x的所有元素将导致多对多y中的重复,spring,hibernate,spring-data-jpa,Spring,Hibernate,Spring Data Jpa,我遇到了这个奇怪的问题,希望有人能帮我解决。 也许我做错了什么或者忽略了什么 每当我选择所有n种材质时,我选择的模型都会列出n次。 反之亦然,当我选择所有n个模型时,材料会列出n次 例如:材料1已选定 材料2选定 材料3选定 模型1已选定 模型2 模型3 结果: 材料1 材质2 材料3 模型1 模型1 模型1 我使用的是Spring4.0.3、SpringDataJPA1.4.3、Hibernate4.3.1、MySQL 5.1.28和Thymeleaf2.1.2 发布的DTO是正确的,仅包含选

我遇到了这个奇怪的问题,希望有人能帮我解决。 也许我做错了什么或者忽略了什么

每当我选择所有n种材质时,我选择的模型都会列出n次。 反之亦然,当我选择所有n个模型时,材料会列出n次

例如:材料1已选定 材料2选定 材料3选定

模型1已选定 模型2 模型3

结果:

材料1 材质2 材料3

模型1 模型1 模型1

我使用的是Spring4.0.3、SpringDataJPA1.4.3、Hibernate4.3.1、MySQL 5.1.28和Thymeleaf2.1.2 发布的DTO是正确的,仅包含选定的材料和型号。 数据库中的数据也是正确的

@Getter
@ToString
@Entity
@Table(name = "SKUS")
public class Sku extends BaseEntityAudit {

    /**
    * 
    */
    private static final long serialVersionUID = 1L;

    @Column(name = "CODE", nullable = false)
    protected String code;

    @Column(name = "NAME", nullable = false)
    protected String name;

    @Column(name = "RETAIL_PRICE", nullable = true)
    protected BigDecimal retailPrice;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
    @JoinTable(name = "SKU_MODELS",
            joinColumns = {@JoinColumn(name = "SKU_ID", referencedColumnName = "ID")},
            inverseJoinColumns = {@JoinColumn(name = "MODEL_ID", referencedColumnName = "ID")})
    protected List<SkuModel> availableModels;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
    @JoinTable(name = "SKU_MATERIALS",
            joinColumns = {@JoinColumn(name = "SKU_ID", referencedColumnName = "ID")},
            inverseJoinColumns = {@JoinColumn(name = "MATERIAL_ID", referencedColumnName = "ID")})
    protected List<Material> availableMaterials;

    public static Builder getBuilder(String code, String name) {
        return new Builder(code, name);
    }

    public void update(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public void updatePrice(BigDecimal retailPrice) {
        this.retailPrice = retailPrice;
    }

    public void updateMaterials(List<Material> materials) {
        if (!materials.equals(availableMaterials)) {
            this.availableMaterials.clear();
            this.availableMaterials = materials;
        }
    }

    public void updateModels(List<SkuModel> models) {
        if (!models.equals(availableModels)) {
            this.availableModels.clear();
            this.availableModels = models;
        }
    }

    public static class Builder {
        private Sku built;

        public Builder(String code, String name) {
            built = new Sku();
            built.code = code;
            built.name = name;
        }

        public Builder retailPrice(BigDecimal retailPrice) {
            built.retailPrice = retailPrice;
            return this;
        }

        public Builder models(List<SkuModel> models) {
            built.availableModels = models;
            return this;
        }

        public Builder materials(List<Material> materials) {
            built.availableMaterials = materials;
            return this;
        }

        public Sku build() {
            return built;
        }
    }
}

public interface SkuRepository extends JpaRepository<Sku, Long> {}

// SkuService
@Transactional(readOnly = true, rollbackFor = {NotFoundException.class})
@Override
public Sku findById(Long id) throws NotFoundException {
    LOGGER.debug("Finding a sku entry with id: {}", id);

    Sku found = skuRepository.findOne(id);
    LOGGER.debug("Found sku entry: {}", found);

    if (found == null) {
        throw new NotFoundException("No sku found with id: " + id);
    }

    return found;
}

@Transactional(rollbackFor = {NotFoundException.class})
@Override
public Sku update(SkuDTO updated) throws NotFoundException {
    LOGGER.debug("Updating sku with request information: {}", updated);

    Sku model = findById(updated.getId());

    model.update(updated.getCode(), updated.getName());
    model.updatePrice(updated.getRetailPrice());
    model.updateMaterials(updated.getMaterials());
    model.updateModels(updated.getModels());

    LOGGER.debug("Updating sku with information: {}", model);
    skuRepository.save(model);
    return model;
}

@RequestMapping(value = REQUEST_MAPPING_SKU_DETAILS, method = RequestMethod.GET)
public String findById(@PathVariable(PARAMETER_SKU_ID) Long id, Model uiModel) throws NotFoundException {
    LOGGER.debug("Rendering sku page for sku entry with id: {}", id);

    Sku found = skuService.findById(id);
    LOGGER.debug("Found sku entry with information: {}", found);

    uiModel.addAttribute(MODEL_ATTRIBUTE_SKU, found);

    return VIEW_SKU_DETAILS;
}

@RequestMapping(value = REQUEST_MAPPING_SKU_EDIT, method = RequestMethod.POST)
public String processUpdateSkuForm(@Valid @ModelAttribute(MODEL_ATTRIBUTE_SKU) SkuDTO dto, BindingResult result, RedirectAttributes attributes) throws NotFoundException {
    LOGGER.debug("Updating a sku entry with information: {}", dto);

    if (result.hasErrors()) {
        LOGGER.debug("Update sku entry form was submitted with validation errors. Redirecting back to form view.");
        attributes.addFlashAttribute("org.springframework.validation.BindingResult." + MODEL_ATTRIBUTE_SKU, result);
        attributes.addFlashAttribute(MODEL_ATTRIBUTE_SKU, dto);
        attributes.addAttribute(PARAMETER_SKU_ID, dto.getId());
        return createRedirectViewPath(REQUEST_MAPPING_SKU_EDIT);
    }

    Sku updated = skuService.update(dto);
    LOGGER.debug("Updated the information of a sku entry to: {}", updated);

    attributes.addAttribute(PARAMETER_SKU_ID, updated.getId());
    return createRedirectViewPath(REQUEST_MAPPING_SKU_DETAILS);
}

我也有同样的问题


我相信这是因为Spring生成的查询在进行连接时不使用“DISTINCT”,但我不知道解决方案。您可以使用集合而不是列表

这是否意味着要创建3个model1对象?或者它只列出了模型1 3次?它列出了模型1 3次,而它只是可接合SKU_模型SKU_id、模型_id中的一行SKU_物料表包含3行SKU_id,物料id。检索Sku模型会在availableModels中生成3个model1对象,在AvailableMaterials中生成3个material1、2和3。除了使用Set之外,还有其他解决方案吗