![](/img/trans.png)
[英]NullPointerException on multi-table primary key JPA when building application
[英]Spring JPA multi-table relationships
我正在嘗試創建具有產品->屬性->選項的產品目錄
因此數據看起來像這樣:
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
@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
第一步是在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";
}
但是視圖中的${attr.options}
無法識別options
鍵,那么如何進行這項工作?
我嘗試在Product
實體中添加@OneToMany
關聯,但這在數據庫中使用product_id|attribute_id
創建了一個表,然后您必須保存該屬性,然后使用新屬性更新產品,這也意味着選擇一個產品,您將提取所有屬性和所有選項,以防止在這些屬性上進行分頁。
@Entity
class Product {
@Id
@Generatedvalue
private Long id;
private String name;
@OneToMany
List<Attribute> attributes;
// getters and setters
}
我找到了解決方案:
我添加了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
分開,以便可以對屬性使用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";
}
然后在視圖中,我記得要遍歷attributes
而不是如上所述的product.attributes
。
因為在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.