简体   繁体   English

Spring JPA多表关系

[英]Spring JPA multi-table relationships

I'm trying to create a product catalogue that has Product -> Attributes -> Options 我正在尝试创建具有产品->属性->选项的产品目录

So the data would look something like this: 因此数据看起来像这样:

product_one
  attribute_one
    option_one
    option_two
  attribute_two
    option_one
    option_two

The code is available on GitHub https://github.com/ccsalway/prod_info_mngr 该代码可在GitHub https://github.com/ccsalway/prod_info_mngr上找到

I have created a Class for each Entity: 我为每个实体创建了一个类:

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

I have created a Repository for each Entity: 我为每个实体创建了一个存储库:

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

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

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

I have created a Service for each Entity: 我为每个实体创建了一个服务:

@Service
public class ProductService {

    // Autowired Repositories

    // methods
}

@Service
public class AttributeService {

    // Autowired Repositories

    // methods
}

@Service
public class OptionService {

    // Autowired Repositories

    // methods
}

I have created a Controller for each Entity: 我为每个实体创建了一个控制器:

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

And (finally) I have created several views for each Controller (I won't add them here). 并且(最后)我为每个Controller创建了几个视图(我不会在这里添加它们)。

What I am trying to do in the product_view.jsp View is show a list of attributes and their associated options, something like this: 我想在product_view.jsp视图中执行的操作是显示属性及其相关选项的列表,如下所示:

<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 the table would look something like this 所以桌子看起来像这样

product_one
  attribute_one    option_one, option_two
  attribute_two    option_one, option_two

The first step was to create a @RequestMapping in ProductController : 第一步是在ProductController创建一个@RequestMapping

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

but ${attr.options} in the View doesn't recognise the options key so how do I go about making this work? 但是视图中的${attr.options}无法识别options键,那么如何进行这项工作?

I tried to add a @OneToMany association in the Product Entity but this created a table in the database with product_id|attribute_id which you then have to save the attribute, and then update the product with the new attribute, which also means when you select a product, you are pulling ALL attributes and ALL options which prevents Paging on those. 我尝试在Product实体中添加@OneToMany关联,但这在数据库中使用product_id|attribute_id创建了一个表,然后您必须保存该属性,然后使用新属性更新产品,这也意味着选择一个产品,您将提取所有属性和所有选项,以防止在这些属性上进行分页。

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

  @OneToMany
  List<Attribute> attributes;

 // getters and setters
}

I found a solution: 我找到了解决方案:

I added OneToMany and ManyToOne relationships as follows. 我添加了OneToManyManyToOne关系,如下所示。 This method also allowed the repository.delete(id) method to cascade delete. 此方法还允许repository.delete(id)方法级联删除。

FetchType.LAZY tells Spring to only fetch the underlying item(s) when requested. FetchType.LAZY告诉Spring仅在请求时获取基础项。 For example, when a request for a Product is made, the id and name will be fetched, but because the attributes are @OneToMany which is, by default, LAZY, the attributes won't be fetched from the DB until a specific call to product.getAttributes() is made. 例如,当请求Product ,将获取idname ,但是由于attributes@OneToMany (默认情况下为LAZY),因此只有在特定调用时才从数据库中获取attributes product.getAttributes()已制成。

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

In the ProductController , I separate the Attributes from the Product so that I can use Paging on the Attributes (whereas if I just called product.getAttributes() , it would fetch ALL the Attributes) ProductController ,我将AttributesProduct分开,以便可以对属性使用Paging(而如果我只是调用product.getAttributes() ,它将获取所有属性)

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

Then in the view, I remember to loop over the attributes and not the product.attributes as explained above. 然后在视图中,我记得要遍历attributes而不是如上所述的product.attributes

Because the options property is set to LAZY in the Attribute Entity, when the loop calls attr.options , Spring will make a request to the DB for the Options for the current Attribute . 因为在Attribute实体中options属性设置为LAZY ,所以当循环调用attr.options ,Spring将向数据库请求当前AttributeOptions

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM