[英]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. 我添加了
OneToMany
和ManyToOne
关系,如下所示。 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, theid
andname
will be fetched, but because theattributes
are@OneToMany
which is, by default, LAZY, theattributes
won't be fetched from the DB until a specific call toproduct.getAttributes()
is made.例如,当请求
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
}
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
,我将Attributes
与Product
分开,以便可以对属性使用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将向数据库请求当前Attribute
的Options
。
<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.