2017-12-22 6 views
0

Ich versuche, einen Produktkatalog zu erstellen, das Produkt hat -> Attribute -> OptionenFrühling JPA Multi-Table-Beziehungen

So würden die Daten wie folgt aussehen:

product_one 
    attribute_one 
    option_one 
    option_two 
    attribute_two 
    option_one 
    option_two 

Der Code ist auf GitHub https://github.com/ccsalway/prod_info_mngr

ich habe eine Klasse für jede Entität erstellt:

@Entity 
class Product { 
    @Id 
    @Generatedvalue 
    private Long id; 
    private String name; 

    // getters and setters 
} 

@Entity 
class Attribute { 
    @Id 
    @Generatedvalue 
    private Long id; 
    private String name; 
    @ManyToOne(cascade = CascadeType.REMOVE) 
    private Product product; 

    // getters and setters 
} 

@Entity 
class Option { 
    @Id 
    @Generatedvalue 
    private Long id; 
    private String name; 
    @ManyToOne(cascade = CascadeType.REMOVE) 
    private Attribute attribute; 

    // getters and setters 
} 

Ich habe ein Repository für jede Entität erstellt:

@Repository 
public interface ProductRepository extends PagingAndSortingRepository<Product, Long> { 
} 

@Repository 
public interface AttributeRepository extends PagingAndSortingRepository<Attribute, Long> { 
} 

@Repository 
public interface OptionRepository extends PagingAndSortingRepository<Option, Long> { 
} 

ich einen Dienst für jede Entität erstellt haben:

@Service 
public class ProductService { 

    // Autowired Repositories 

    // methods 
} 

@Service 
public class AttributeService { 

    // Autowired Repositories 

    // methods 
} 

@Service 
public class OptionService { 

    // Autowired Repositories 

    // methods 
} 

Ich habe für jede Entität einen Controller erstellt haben:

@Controller 
@RequestMapping("/product") 
public class ProductController { 

    @Autowired 
    private ProductService productService; 

    //methods 
} 

@Controller 
@RequestMapping("/product/{prod_id}/attribute") 
public class AttributeController{ 

    @Autowired 
    private AttributeService attributeService; 

    //methods 
} 

@Controller 
@RequestMapping("/product/{prod_id}/attribute/{attr_id}") 
public class OptionController { 

    @Autowired 
    private OptionService optionService; 

    //methods 
} 

Und (endlich) habe ich mehrere Ansichten für jeden Controller erstellt (ich werde sie hier nicht hinzufügen).

Was ich versuche, in der product_view.jsp Ansicht zu tun ist, eine Liste der Attribute zeigen und ihre zugehörigen Optionen, etwa wie folgt:

<table id="attrTable"> 
    <thead> 
    <tr> 
     <th>Name</th> 
     <th>Options</th> 
    </tr> 
    </thead> 
    <tbody> 
    <c:forEach items="${attributes}" var="attr"> 
     <tr data-id="${attr.id}"> 
      <td>${fn:htmlEscape(attr.name)}</td> 
      <td><c:forEach items="${attr.options}" var="opt" varStatus="loop"> 
       ${opt.name}<c:if test="${!loop.last}">,</c:if> 
      </c:forEach></td> 
     </tr> 
    </c:forEach> 
    </tbody> 
</table> 

So ist der Tisch so etwas wie dieses

product_one 
    attribute_one option_one, option_two 
    attribute_two option_one, option_two 
aussehen würde

Der erste Schritt war eine @RequestMapping in ProductController zu erzeugen:

@RequestMapping(value = "/{id}", method = RequestMethod.GET) 
public String view(Model model, @PathVariable Long id) { 
    Product product = productService.getProduct(id); 
    List<Attribute> attributes = productService.getAttributes(product); 
    model.addAttribute("product", product); 
    model.addAttribute("attributes", attributes); 
    return "products/product_view"; 
} 

aber ${attr.options} in der Ansicht erkennt nicht die options-Taste, also wie gehe ich über das Machen dieser Arbeit?

Ich versuchte, einen @OneToMany Verein in dem Product Entity hinzuzufügen, aber diese erstellt, eine Tabelle in der Datenbank mit product_id|attribute_id denen Sie dann das Attribute speichern müssen und dann das Produkt mit dem neuen Attribute aktualisieren, was auch bedeutet, wenn Sie wählen ein Produkt, Sie ziehen ALLE Attribute und ALLE Optionen, die Paging auf denen verhindert.

@Entity 
class Product { 
    @Id 
    @Generatedvalue 
    private Long id; 
    private String name; 

    @OneToMany 
    List<Attribute> attributes; 

// getters and setters 
} 
+0

Haben die Entitäten sogar Beziehungen oder einfach 'id' und 'name'? – Synch

+0

Entschuldigung, ja, ich werde die hinzufügen, da ich sie vermisst habe. – Christian

+0

In Ihrem Controller sehe ich 'model.addAttribute (" Attribute ", Attribute);' während in Ihrem JSP '$ {attr.options}' ist es ein Tippfehler? Andernfalls denke ich, dass Sie '$ {attributes.options}' –

Antwort

0

fand ich eine Lösung:

I OneToMany und ManyToOne Beziehungen wie folgt hinzugefügt. Diese Methode ermöglichte auch die Methode repository.delete(id) zum kaskadierenden Löschen.

FetchType.LAZY erzählt Spring nur den zugrunde liegenden Punkt zu holen (n), wenn angefordert. Zum Beispiel, wenn eine Anforderung für ein Product gemacht wird, werden die id und name geholt werden, sondern weil die attributes sind @OneToMany, die ist in der Standardeinstellung LAZY, wird die attributes nicht von der DB bis zu einem bestimmten Anruf zu product.getAttributes() abgeholt werden ist gemacht.

@Entity 
public class Product { 

    @Id 
    @GeneratedValue 
    private Long id; 
    private String name; 

    // OneToMany is, by default, a LAZY fetch 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "product") 
    private Set<Attribute> attributes = new HashSet<>(); 

    // getters and setters 
} 

@Entity 
public class Attribute { 

    @Id 
    @GeneratedValue 
    private Long id; 
    private String name; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "product_id", nullable = false) 
    private Product product; 

    // OneToMany is, by default, a LAZY fetch 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "attribute") 
    private Set<Option> options = new HashSet<>(); 

    // getters and setters 
} 

@Entity 
public class Option { 

    @Id 
    @GeneratedValue 
    private Long id; 

    @NotEmpty 
    @Size(min = 1, max = 32) 
    private String name; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "attribute_id", nullable = false) 
    private Attribute attribute; 

    // getters and setters 
} 

Im ProductController, trenne ich die Attributes vom Product so, dass ich Paging auf die Attribute verwenden können (während, wenn ich es nur product.getAttributes() genannt, alle Attribute holen würde)

@RequestMapping(value = "/{id}", method = RequestMethod.GET) 
public String view(Model model, @PathVariable Long id, @RequestParam(name = "page", defaultValue = "0") int page) throws ProductNotFoundException { 
    Product product = productService.getProduct(id); 
    model.addAttribute("product", product); 
    // requesting attributes separately (as opposed to using LAZY) allows you to use paging 
    Page<Attribute> attributes = productService.getAttributes(product, new PageRequest(page, 10)); 
    model.addAttribute("attributes", attributes); 
    return "products/product_view"; 
} 

Dann in der Ansicht, ich erinnere mich, Schleife über die attributes und nicht die wie oben erläutert.

Da die options Eigenschaft LAZY im Attribute Entity gesetzt wird, wenn die Schleife ruft attr.options, Frühling wird für die Options für die aktuellen Attribute eine Anfrage an die DB machen.

<table id="attrTable" class="table is-hoverable is-striped is-fullwidth" style="cursor:pointer;"> 
    <thead> 
    <tr> 
     <th>Name</th> 
     <th>Options</th> 
    </tr> 
    </thead> 
    <tbody> 
    <c:forEach items="${attributes}" var="attr"> 
     <tr data-id="${attr.id}"> 
      <td>${fn:htmlEscape(attr.name)}</td> 
      <td> 
       <c:forEach items="${attr.options}" var="opt" varStatus="loop"> 
        ${opt.name}<c:if test="${!loop.last}">,</c:if> 
       </c:forEach> 
      </td> 
     </tr> 
    </c:forEach> 
    </tbody> 
</table>