文章目录
1. Spring Data JPA多对多的关联映射案例
1.1 创建表结构
用户表的建表语句:
CREATE TABLE `sys_user` (
`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`user_code` varchar(32) NOT NULL COMMENT '用户账号',
`user_name` varchar(64) NOT NULL COMMENT '用户名称',
`user_password` varchar(32) NOT NULL COMMENT '用户密码',
`user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
角色表的建表语句:
CREATE TABLE `sys_role` (
`role_id` bigint(32) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) NOT NULL COMMENT '角色名称',
`role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
用户角色表的建表语句:
CREATE TABLE `sys_user_role` (
`role_id` bigint(32) NOT NULL COMMENT '角色id',
`user_id` bigint(32) NOT NULL COMMENT '用户id',
PRIMARY KEY (`role_id`,`user_id`),
KEY `FK_user_role_user_id` (`user_id`),
CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1.2 编写实体类
用户的实体类如下:
@Entity
@Table(name="sys_user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="user_id")
private Long userId;
@Column(name="user_code")
private String userCode;
@Column(name="user_name")
private String userName;
@Column(name="user_password")
private String userPassword;
@Column(name="user_state")
private String userState;
/**
* 配置用户和角色的多对多关系映射
* @ManyToMany:用于映射多对多关系
* targetEntity:配置目标的实体类,映射多对多的时候不用写
* mappedBy:放弃外键维护权
* cascade:配置级联操作
* fetch:配置是否采用延迟加载
* @JoinTable:针对中间表的配置
* name:中间表的名称
* joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段
* inverseJoinColumn:中间表的外键字段关联对方表的主键字段
* @JoinColumn:用于定义主键字段和外键字段的对应关系
* name:指定外键字段的名称
* referencedColumnName:指定引用主表的主键字段名称
* unique:是否唯一。默认值不唯一
* nullable:是否允许为空。默认值允许
* insertable:是否允许插入。默认值允许
* updatable:是否允许更新。默认值允许
* columnDefinition:列的定义信息
*/
@ManyToMany
@JoinTable(name="sys_user_role",
joinColumns={@JoinColumn(name="user_id", referencedColumnName="user_id")},
inverseJoinColumns={@JoinColumn(name="role_id", referencedColumnName="role_id")}
)
private Set<SysRole> roles = new HashSet<SysRole>(0);
//省略 get和 set方法
}
角色的实体类如下:
@Entity
@Table(name="sys_role")
public class Role implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="role_id")
private Long roleId;
@Column(name="role_name")
private String roleName;
@Column(name="role_memo")
private String roleMemo;
//多对多必须有一方放弃外键的维护
@ManyToMany(mappedBy="roles")
private Set<SysUser> users = new HashSet<SysUser>(0);
//省略 get和 set方法
}
2.3 编写Dao层接口
用户的Dao层接口如下:
public interface UserDao extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
角色的Dao层接口如下:
public interface RoleDao extends JpaRepository<Role, Long>, JpaSpecificationExecutor<Role> {
}
2.4 编写测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class ManyToManyTest {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
@Test
public void test1(){
User user = new User();
user.setUserName("joker");
Role role = new Role();
role.setRoleName("管理员");
user.getRoles().add(role);
role.getUsers().add(user);
roleDao.save(role);
userDao.save(user);
}
}
2. Spring Data JPA一对多的相关操作
2.1 保存
@Test
public void test1(){
User user = new User();
user.setUserName("joker");
Role role = new Role();
role.setRoleName("管理员");
user.getRoles().add(role);
role.getUsers().add(user);
roleDao.save(role);
userDao.save(user);
}
在多对多保存中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,所以报错:主键重复。解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃,配置如下:
@Entity
@Table(name="sys_role")
public class Role implements Serializable {
//放弃对中间表的维护权,解决保存中主键冲突的问题,值为对方配置关系的属性名称
@ManyToMany(mappedBy="roles")
private Set<SysUser> users = new HashSet<SysUser>(0);
}
2.2 删除
@Test
public void test2(){
userDao.delete(1l);
}
注意:在多对多的删除时,双向级联删除根本不能配置。如果配了的话,如果数据之间有相互引用关系,可能会清空所有数据。
2.3 级联操作和延迟加载
级联操作:指操作一个对象同时操作它的关联对象。使用方法:只需要在操作主体的注解上配置cascade。
延迟加载:就是当在真正需要数据的时候,才真正执行数据加载操作。使用方法:只需要在操作主体的注解上配置fetch。
/**
* cascade:配置级联操作
* CascadeType.MERGE:级联更新
* CascadeType.PERSIST:级联保存
* CascadeType.REFRESH:级联刷新
* CascadeType.REMOVE:级联删除
* CascadeType.ALL:包含所有
* fetch:配置关联对象的加载方式
* FetchType.EAGER:立即加载
* FetchType.LAZY:延迟加载
*/
@ManyToMany(mappedBy="roles", cascade=CascadeType.PERSIST, fetch=FetchType.EAGER)