Jpa除了单表操作,还有就是常见的一对多和多对多了。。
下面来两个例子。。。
1. 一对多
这个例子是一个用户有多个文章
1. 1 实体类
主表:User
@Entity
@Table(name = "t_user")
public class User implements Serializable {
private static final long serialVersionUID = -5777961600230089298L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Integer id;
private String username;
private String password;
private String nickname;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date updateTime = new Date();
/**
* 1代表删除,0代表没有删除
*/
private String deleteStatus = "0";
@OneToMany(targetEntity = Article.class)
//设置外键
@JoinColumn(name = "t_user_id",referencedColumnName = "user_id")
private List<Article> articles;
//get,set方法省略 。。。
@JoinColumn
: 是设置一个外键,name是外键的名字,referencedColumnName是主表的主键字段名称
从表:Article
@Entity
@Table(name = "t_article")
public class Article implements Serializable {
private static final long serialVersionUID = 4272333391193664556L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "article_id")
private Integer id;
private String content;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "+8:00")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "+8:00")
private Date updateTime = new Date();
/**
* 1代表删除,0代表没有删除
*/
private String deleteStatus = "0";
@ManyToOne(targetEntity = User.class)
private User user;
运行就可以得到,这就得到了两张表了。
2. 多对多
还是上面的那个User表
@Entity
@Table(name = "t_user")
public class User implements Serializable {
private static final long serialVersionUID = -5777961600230089298L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Integer id;
private String username;
private String password;
private String nickname;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date updateTime = new Date();
/**
* 1代表删除,0代表没有删除
*/
private String deleteStatus = "0";
@OneToMany(targetEntity = Article.class)
@JoinColumn //外键
private List<Article> articles;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "t_user_role",
joinColumns = {@JoinColumn(name = "t_user_id")},inverseJoinColumns = {@JoinColumn(name = "t_role_id")})
private List<Role> roles;
//get,set方法省略
@JoinTable
:多对多必须要维护一个中间表,而中间表有两个属性分别对应了主表的id和从表的id。当然这个名字可以自己取。
从表:Role
@Entity
@Table(name = "t_role")
public class Role implements Serializable {
private static final long serialVersionUID = -5711260965444546791L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Integer id;
private String roleName;
/**
* 角色具体描述
*/
private String content;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date updateTime = new Date();
/**
* 1代表删除,0代表没有删除
*/
private String deleteStatus = "0";
@ManyToMany(mappedBy = "roles")
private List<User> users;
// get,set方法省略
@ManyToMany
: 这里有一个mapperBy,值是主表对应的的属性名。同时也意味着,该表放弃维护中间表,中间表由主表User来维护。
当然,一个User对应了多个Role,一个Role也对应了多个User,
谁是主表谁是从表,取决于谁被调用。
运行一下Springboot,可以得到三张表
再加一张表试试。。。。QAQ
一个用户(User)可以有多个角色 (Role),一个 角色可以有多个权限(Permission)
所以这里加入一张权限表(Permission)
既然加入了权限表,那么角色表Role也要改动了
Role表
@Entity
@Table(name = "t_role")
public class Role implements Serializable {
private static final long serialVersionUID = -5711260965444546791L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Integer id;
private String roleName;
/**
* 角色具体描述
*/
private String content;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date updateTime = new Date();
/**
* 1代表删除,0代表没有删除
*/
private String deleteStatus = "0";
@JsonIgnore
@ManyToMany(mappedBy = "roles")
private List<User> users;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "t_role_permission",
joinColumns = {@JoinColumn(name = "t_role_id")},
inverseJoinColumns = @JoinColumn(name = "t_permission_id"))
private List<Permission> permissions;
// 忽略get,set方法
在这次角色(Role)与权限表 (Permission)中,我让Role当主表,也就是Role来维护中间表,Permission表放弃维护权。
Permission表
@Entity
@Table(name = "t_permission")
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "permission_Id")
private Integer id;
private String permissionName;
private String content;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "+8:00")
private Date updateTime = new Date();
/**
* 1代表删除,0代表没有删除
*/
private String deleteStatus = "0";
@JsonIgnore
@ManyToMany(mappedBy = "permissions")
private List<Role> roles;
//忽略get,set方法
}
@JsonIgnore
: 这个是spring的一个注解,意思是转成json的时候忽略这个字段,可以防止在controller调用的时候出现栈溢出
再次启动Springboot,
可以看到Jpa为我们创建了两张中间表、总共是五张表
3. 最后决定来测试一下
先简单的插入一些数据
@SpringBootTest
class BootjpaApplicationTests {
@Autowired
private PermissionDao permissionDao;
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
@Test
@Transactional
@Rollback(value = false)
void contextLoads() {
//添加和查找权限
Permission permission1 = new Permission();
permission1.setContent("添加");
permission1.setPermissionName("add");
permission1.setCreateTime(new Date());
Permission permission2 = new Permission();
permission2.setContent("查找");
permission2.setPermissionName("query");
permission2.setCreateTime(new Date());
ArrayList<Permission> permissions = new ArrayList<>();
permissions.add(permission1);
permissions.add(permission2);
//添加两个权限
permissionDao.saveAll(permissions);
Role role = new Role();
role.setContent("只有添加和查找的管管理");
role.setRoleName("C管理员");
role.setCreateTime(new Date());
role.setPermissions(permissions);
Role role2 = new Role();
role2.setContent("只有看看");
role2.setRoleName("游客");
role2.setCreateTime(new Date());
role2.setPermissions(new ArrayList<Permission>(){{add(permission2);}});
ArrayList<Role> roles = new ArrayList<>();
roles.add(role);
roles.add(role2);
//添加两个角色
roleDao.saveAll(roles);
User user = new User();
user.setUsername("liliya");
user.setPassword(SecureUtil.md5("123"));
user.setNickname("莉莉娅");
user.setCreateTime(new Date());
user.setRoles(roles);
//添加这个用户
userDao.save(user);
}
接下来在Controller层搞一手打印。。。
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserDao userDao;
@GetMapping("/1")
public Optional<User> queryById(){
return userDao.findById(1);
}
}
注意事项: 在springboot中事务是不会提交的,所以可以想我一样加入 @Transactional , @Rollback(value = false)
这两个注解
出现了bug,本来这个地方是两个角色,但是给我查出三个角色了。。
有两个是相同的,所以建议还是用set,不用list。。
对于同一个User查出了两个一模一样的Role,
但是查看中间表的时候是没有问题 。。
感觉这个是Jpa的bug,,
换了一个语句就好了。。。
在dao层换了这个。。
这波就OK了。。。
最后
关于要注意的
- 如果是在测试类中使用,建议加上
@Transactional @Rollback(value = false)
- 重写toString方法打印的时候,注意一下理关系,很容易就出现栈溢出了,
- 还有一个是
@JsonIgnore
,这个放到从表的集合上就好了,防止jpa反复查询带来的栈溢出。。