javax.persistence笔记
最近在做一个项目,刚开始设计数据库的时候,遇到“一对多”和“多对一”以及“多对多”的情况,手头书上没有详细介绍,于是上网找了很多资料,虽然不是很系统的介绍,但也懂了一些。
当我调试两个类的时候,关系是一对多:
**RoleType类**
@OneToMany(cascade = {CascadeType.ALL},fetch = FetchType.EAGER)//CascadeType.ALL表示RoleType在所有情况下都级联
@JoinColumn(name = "roleID", referencedColumnName = "roleId")
private Set<Admin> adminList;
**Admin类**
@ManyToOne(cascade = {CascadeType.MERGE,CascadeType.PERSIST},fetch = FetchType.EAGER)//Admin在更新和保存的时候级联
@JoinColumn(name = "roleID", referencedColumnName = "roleId")
private RoleType roleType;
当我使用测试Controller:
@RestController
public class TestController {
@Autowired
AdminRepository adminRepository;
@Autowired
RoleRepository roleRepository;
@RequestMapping("/saveAdmin")
public Admin saveAdmin() {
RoleType r1 = new RoleType((short) 2,"ROLE_INNER", new HashSet<>());
Admin a1 = new Admin(111L,"inner", "123456", "计算机学院", r1);
r1.getAdminList().add(a1);
return adminRepository.save(a1);
}
}
可见,我在Admin中添加了一个角色类型RoleType;
又在RoleType集合中添加了此Admin管理员对象。
访问链接后:
报了如下错误:
Hibernate: select roletype0_.role_id as role_id1_3_0_, roletype0_.name as name2_3_0_, adminlist1_.roleid as roleid5_0_1_, adminlist1_.admin_id as admin_id1_0_1_, adminlist1_.admin_id as admin_id1_0_2_, adminlist1_.college as college2_0_2_, adminlist1_.password as password3_0_2_, adminlist1_.roleid as roleid5_0_2_, adminlist1_.user_name as user_nam4_0_2_ from role_type roletype0_ left outer join admin adminlist1_ on roletype0_.role_id=adminlist1_.roleid where roletype0_.role_id=?
2016-08-10 12:02:50.006 WARN 6576 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain: com.scut.cs.domain.Admin["roleType"]->com.scut.cs.domain.RoleType["adminList"]->org.hibernate.collection.internal.PersistentSet[0]->com.scut.cs.domain.Admin["roleType"]->com.scut.cs.domain.RoleType["adminList"]->org.hibernate.collection.internal.PersistentSet[0]->com.scut.cs.domain.Admin["roleType"]->com.scut.cs.domain.RoleType["adminList"]->org.hibernate.collection.internal.PersistentSet[0]->com.scut.cs.domain.Admin["roleType"]->com.scut.cs.domain.RoleType["adminList"]->org.hibernate.collection.internal.PersistentSet[0]->com.scut.cs.domain.Admin["roleType"]->com.scut.cs.domain.RoleType["adminList"]->org.hibernate.collection.internal.PersistentSet[0]->com.scut.cs.domain.Admin["roleType"]->com.scut.cs.domain.RoleType["adminList"]->org.hibernate.collection.internal.PersistentSet[0]->com.scut.cs.domain.Admin["roleType"]-
***此处省略许多许多的重复错误信息***
2016-08-10 12:02:50.010 WARN 6576 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Handling of [org.springframework.http.converter.HttpMessageNotWritableException] resulted in Exception
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
java.lang.StackOverflowError: null
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: getOutputStream() has already been called for this response
浏览器没有任何的输出
查看数据库,已经添加了相应的表项。
AdminTable:
admin_id | college | password | user_name | roleid |
---|---|---|---|---|
111 | 计算机学院 | 123456 | inner | 2 |
RoleTypeTable:
role_id | name |
---|---|
2 | ROLE_INNER |
RoleTypeAdminTable:
role_type_role_id | admin_list_admin_id |
---|---|
2 | 111 |
1. 现在情况是:RoleTypeAdminTable里记录着每一个RoleType和Admin的对应关系,而AdminTable里又有外键roleid指向RoleTypeTable,所以会造成查询某一个admin或者roletype的时候陷入两者无限嵌套查询。
2. 我第一个想到的解决方法是:将两个注解@ManyToOne 和@OneToMany,前者改为fetch=FetchType.EAGER,后者改为fetch=FetchType.LAZY。个人理解为前者快加载,查询的时候,在此例会把One一起查询。而慢加载,查询的时候,在此例不会去Many一方的Set集合查询。可是结果报一样的错
3. 于是继续查资料:API-OneToMany 和 API-ManyToOne
4. 看到在OneToMany一端不需要@JoinColumn一行。在ManyToOne一端有就可以了。另外:name = “roleID”为Many端表中外键名称roleID,referencedColumnName = “roleId”为One端表中的主键名称roleId。
5. 现在添加两个测试数据:
AdminTable:
admin_id | college | password | user_name | roleid |
---|---|---|---|---|
110 | 计算机学院 | 123456 | admin | 1 |
111 | 计算机学院 | 123456 | inner | 2 |
112 | 计算机学院 | 123456 | outer | 3 |
RoleTypeTable:
role_id | name |
---|---|
1 | ROLE_ADMIN |
2 | ROLE_INNER |
3 | ROLE_OUTER |
RoleTypeAdminTable:
role_type_role_id | admin_list_admin_id |
---|---|
1 | 110 |
2 | 111 |
3 | 112 |
6. 稍微做一个调整:将AdminTable中的admin_id=110的role_id改为null;将RoleTypeAdminTable中的第三条数据删去;
AdminTable:
admin_id | college | password | user_name | roleid |
---|---|---|---|---|
110 | 计算机学院 | 123456 | admin | null |
111 | 计算机学院 | 123456 | inner | 2 |
112 | 计算机学院 | 123456 | outer | 3 |
RoleTypeTable:
role_id | name |
---|---|
1 | ROLE_ADMIN |
2 | ROLE_INNER |
3 | ROLE_OUTER |
RoleTypeAdminTable:
role_type_role_id | admin_list_admin_id |
---|---|
1 | 110 |
2 | 111 |
7. 现在形成控制变量对照组:admin_id=110和112的数据查询成功;
{
"id": 110,
"userName": "admin",
"password": "123456",
"college": "计算机学院",
"roleType": null
}
{
"id": 112,
"userName": "outer",
"password": "123456",
"college": "计算机学院",
"roleType": {
"id": 3,
"name": "ROLE_OUTER",
"adminList": []
}
}
role_id=1和3的数据查询成功:
{
"id": 1,
"name": "ROLE_ADMIN",
"adminList": [
{
"id": 110,
"userName": "admin",
"password": "123456",
"college": "计算机学院",
"roleType": null
}
]
}
{
"id": 3,
"name": "ROLE_OUTER",
"adminList": []
}
8.. 好了现在总结一下:
1) 在@OneToMany一方设置好cascade和fetch后,在@ManyToOne一方除了设置好cascade和fetch,还要设置@JoinColumn;
2) 存对象数据的时候,只用添加一方的数据即可,比如:
@RequestMapping("/saveRole")
public RoleType saveAdmin() {
RoleType r1 = new RoleType((short) 4,"ROLE_OUTER", new HashSet<>());
Admin a1 = new Admin(113L,"outer", "123456", "计算机学院", null);
r1.getAdminList().add(a1);//只把admin对象添加进RoleType集合中,不在admin对象中添加roletype对象;如上面例子的admin_id=110
return roleRepository.save(r1);
}
@RequestMapping("/saveAdmin")
public Admin saveRole() {
RoleType r1 = new RoleType((short) 5,"ROLE_OUTER", new HashSet<>());
Admin a1 = new Admin(114L,"outer", "123456", "计算机学院", r1);//只把roletype对象添加进admin对象中,不在roletype集合中添加这个admin对象;如上面例子的admin_id=112
return adminRepository.save(a1);
}
两种情况按实际需求自己选择!!
按照我这个项目要求,会选择第二种。
本人刚开始学Spring MVC和Spring Boot,有错误烦请赐教~
2016.8.11.日更新:
今天用manyTomany处理另外两个表的时候,按照此博客
将两个表都设置为主控方,但是发现又出现上述的陷入循环取对象中,出错。后来把一方的@JoinTable(…)去掉,只保留主控方的@JoinTable(…),这样就解决了!!由于项目需要,把两边都设置成Eager。
再更新
ManyToMany 的循环嵌套误解:是由于json的序列化漏洞
所以上面8.11.的更新不用去掉一方的@JoinTable,同时要将name设成一样,这样就不会创建两个关联表了。