我正在尝试创建一个包含Product的产品目录 - >属性 - >选项
所以数据看起来像这样:
product_one
attribute_one
option_one
option_two
attribute_two
option_one
option_two
该代码可在GitHub https://github.com/ccsalway/prod_info_mngr
上找到我为每个实体创建了一个类:
@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
}
我为每个实体创建了一个存储库:
@Repository
public interface ProductRepository extends PagingAndSortingRepository<Product, Long> {
}
@Repository
public interface AttributeRepository extends PagingAndSortingRepository<Attribute, Long> {
}
@Repository
public interface OptionRepository extends PagingAndSortingRepository<Option, Long> {
}
我为每个实体创建了一个服务:
@Service
public class ProductService {
// Autowired Repositories
// methods
}
@Service
public class AttributeService {
// Autowired Repositories
// methods
}
@Service
public class OptionService {
// Autowired Repositories
// methods
}
我为每个实体创建了一个Controller:
@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
}
并且(最后)我为每个Controller创建了几个视图(我不会在这里添加它们)。
我在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>
所以表格看起来像这样
product_one
attribute_one option_one, option_two
attribute_two option_one, option_two
第一步是在@RequestMapping
中创建ProductController
:
@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";
}
但是视图中的${attr.options}
无法识别options
密钥,那么我该如何开展这项工作呢?
我尝试在@OneToMany
实体中添加Product
关联,但这会在数据库中创建一个包含product_id|attribute_id
的表,然后您必须保存该属性,然后更新产品使用新属性,这也意味着当您选择产品时,您将拉动所有属性和所有选项,以防止对这些属性进行分页。
@Entity
class Product {
@Id
@Generatedvalue
private Long id;
private String name;
@OneToMany
List<Attribute> attributes;
// getters and setters
}
答案 0 :(得分:0)
我找到了解决方案:
我添加了OneToMany
和ManyToOne
关系,如下所示。此方法还允许repository.delete(id)
方法级联删除。
FetchType.LAZY 告诉Spring仅在请求时获取基础项。例如,当发出
Product
请求时,系统会提取id
和name
,但由于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
}
在ProductController
中,我将Attributes
与Product
分开,以便我可以在属性上使用分页(如果我只是调用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";
}
然后在视图中,我记得如上所述循环遍历attributes
而不是product.attributes
。
因为options
实体中的LAZY
属性设置为Attribute
,所以当循环调用attr.options
时,Spring会向数据库请求Options
1}}表示当前Attribute
。
<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>