关系分析
采用的示例为用户和角色。
用户:指的是咱们班的每一个同学。
角色:指的是咱们班同学的身份信息。
比如A同学,它是我的学生,其中有个身份就是学生,还是家里的孩子,那么他还有个身份是子女。
同时B同学,它也具有学生和子女的身份。
那么任何一个同学都可能具有多个身份。同时学生这个身份可以被多个同学所具有。
所以我们说,用户和角色之间的关系是多对多。
表关系建立
多对多的表关系建立靠的是中间表,其中用户表和中间表的关系是一对多,角色表和中间表的关系也是一对多
实体类关系建立以及映射配置
User实体
代码语言:javascript复制@Entity
@Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
@Column(name = "user_name")
private String userName;
@Column(name="age")
private Integer age;
/**
* 配置用户到角色的多对多
* 配置多对多的映射关系
* 1.声明表关系的配置
* @ManyToMany(targetEntity = Role.class)//声明多对多
* targetEntity:代表对方实体类字节码
* 2.配置中间表(包含两个外键)
* @JoinTable
* name:中间表的名称
* joinColumns:配置当前对象在中间表的外键
* 接收@inverseJoinColumns数组 name外键名 referencedColumnName:参照的主表主键名
* inverseJoinColumns:配置对方对象在中间表的外键
* 接收@inverseJoinColumns数组 name外键名 referencedColumnName:参照的主表主键名
* @return
*/
@ManyToMany(targetEntity = Role.class)
@JoinTable(name = "sys_user_role",
//joinColumns:当前对象在中间表中的外键 referencedColumnName 来源
joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
//inverseJoinColumns:对方对象在中间表中的外键
inverseJoinColumns ={@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} )
private Set<Role> roles=new HashSet<Role>();
Role实体
代码语言:javascript复制@Entity
@Table(name ="sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
//配置多对多 放弃维护
@ManyToMany(mappedBy = "roles")//配置
private Set<User> users=new HashSet<User>();
注解说明
代码语言:javascript复制@ManyToMany
作用:用于映射多对多关系
属性:
cascade:配置级联操作。
fetch:配置是否采用延迟加载。
targetEntity:配置目标的实体类。映射多对多的时候不用写。
@JoinTable
作用:针对中间表的配置
属性:
nam:配置中间表的名称
joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段
inverseJoinColumn:中间表的外键字段关联对方表的主键字段
@JoinColumn
作用:用于定义主键字段和外键字段的对应关系。
属性:
name:指定外键字段的名称
referencedColumnName:指定引用主表的主键字段名称
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
insertable:是否允许插入。默认值允许。
updatable:是否允许更新。默认值允许。
columnDefinition:列的定义信息。
测试方法
保存
代码语言:javascript复制 @Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
/**
* 保存一个用户保存一个角色
*
* 多对多放弃维护权:被动的一方放弃
*/
@Test
@Transactional
@Rollback(value = false)
public void testAdd(){
User user=new User();
user.setUserName("小张");
Role role=new Role();
role.setRoleName("程序猿");
user.getRoles().add(role);//配置用户到角色关系,可以对中间表中的数据进行维护
role.getUsers().add(user);//配置角色到用户的关系,可以对中间表的数据进行维护
userDao.save(user);
roleDao.save(role);
}
在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,所以报错,主键重复,解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃,配置如下
代码语言:javascript复制//配置多对多 放弃对中间表的维护权,解决保存中主键冲突的问题
@ManyToMany(mappedBy = "roles")//配置
private Set<User> users=new HashSet<User>();
级联操作
级联:操作一个对象的同时操作他的关联对象
代码语言:javascript复制 级联操作:
1.需要区分操作主体
2.需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
3.cascade(配置级联)
实体类上添加级联配置
代码语言:javascript复制 //cascade:配置级联操作 All级联所有操作
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
级联保存
代码语言:javascript复制 /**
* 测试级联添加(保存一个用户的同时保存用户的关联角色 )
*/
@Test
@Transactional
@Rollback(value = false)
public void testCasCadeAdd(){
User user=new User();
user.setUserName("小张");
Role role=new Role();
role.setRoleName("程序猿");
user.getRoles().add(role);//配置用户到角色关系,可以对中间表中的数据进行维护
role.getUsers().add(user);//配置角色到用户的关系,可以对中间表的数据进行维护
userDao.save(user);
}
级联删除
代码语言:javascript复制/**
* 测试级联删除
* 案例删除id唯一的客户同时删除他的关联对象
*/
@Test
@Transactional
@Rollback(value = false)
public void testCascadeRemove(){
//查询一号用户
User one = userDao.findOne(1l);
//删除一号用户
userDao.delete(one);
}
多表的查询
对象导航查询 对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。例如:我们通过ID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。对象导航查询的使用要求是:两个对象之间必须存在关联关系。
需求:查询一个客户,获取该客户下的所有联系人
代码语言:javascript复制 /**
* 测试对象导航查询(查询一个对象的时候,通过此对象查询所有的关联对象)
*/
@Test
@Transactional //解决java代码中的no session问题
@Rollback(value = false)
public void testQuery1() {
//查询id为一的客户
Customer customer = customerDao.getOne(1l);
//对象导航查询,此客户下的所有联系人
Set<LinkMan> linkMans = customer.getLinkMans();
for (LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
}
实体配置延迟加载与立即加载
代码语言:javascript复制 * fetch:配置关联对象的加载方式
* 值:EAGER 立即加载 不推荐使用立即加载
* LAZY 延迟加载
*/
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<LinkMan> linkMans=new HashSet<LinkMan>();
代码语言:javascript复制/**
* 一对一
* 从一的一方查询
* 对象导航查询默认使用的是延迟加载的形式查询的
* 调用get方法并不会立即发送查询,而是在使用关联对象的时候才会查询
* 延迟加载
* 不想用延迟加载 修改配置 将延迟加载改为立即加载即可
* fetch,需要配置到多表映射关系的注解上面 虽然可以配置立即加载但并不推荐使用
*/
@Test
@Transactional //解决java代码中的no session问题
@Rollback(value = false)
public void testQuery2() {
//查询id为一的客户 getOne延迟加载 findOne立即加载
Customer customer = customerDao.findOne(1l);
//对象导航查询,此客户下的所有联系人
Set<LinkMan> linkMans = customer.getLinkMans();
for (LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
}
代码语言:javascript复制 /**
*从一方查询多方
* 默认使用延迟加载 (推荐延迟加载!!!)
*从多方查询一方
* 默认使用立即加载
* 一对一
* 对象导航查询 从多的一方查询
* 默认使用立即加载
* 延迟加载:
*/
@Test
@Transactional //解决java代码中的no session问题
@Rollback(value = false)
public void testQuery3() {
//查询id为2的联系人
LinkMan linkMan = linkManDao.findOne(2l);
//对象导航查询所属客户
Customer customer = linkMan.getCustomer();
System.out.println(customer);
}