文章目录

  • 1. Spring Data JPA多对多的关联映射案例
  • 1.1 创建表结构
  • 1.2 编写实体类
  • 2.3 编写Dao层接口
  • 2.4 编写测试代码
  • 2. Spring Data JPA一对多的相关操作
  • 2.1 保存
  • 2.2 删除
  • 2.3 级联操作和延迟加载


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)