查看执行的SQL语句,执行了3条create语句,2条insert语句
查看数据库表,中间表没有插入数据,user和role关联关系没有建立成功
新增testSave0()方法,在user一侧建立用户到角色的关联关系
@Test @Transactional @Rollback(false) public void testSave0(){ User user = new User(); user.setUserName("Thor"); Role role = new Role(); role.setRoleName("God"); user.getRoleSet().add(role); //配置用户到角色的映射 userDao.save(user); roleDao.save(role); }
后台执行SQL如下,3条create语句,3条insert语句,中间表也被插入数据
查看数据库表,3张表中都有数据,user和role关联关系建立
同时在user和role两侧建立关联关系
@Test @Transactional @Rollback(false) public void testSave1(){ User user = new User(); user.setUserName("Thor"); Role role = new Role(); role.setRoleName("God"); // 两放都配置会产生主键冲突,只需要一方配置即可 // 多对多放弃维护权,被动的一方放弃 user.getRoleSet().add(role); //配置用户到角色的映射 role.getUserSet().add(user); //配置角色到用户的映射 userDao.save(user); roleDao.save(role); }
后台执行SQL如下,摒弃饿SQL执行出现报错,因为role在执行往中间表执行insert操作时表中已经存在了user插入的数据,所以出现了主键冲突的报错
因此需要user和role一方放弃维护权,修改Role实体类中关联关系,mappedBy是指role在对方表的属性名称
//@ManyToMany(targetEntity = User.class) //@JoinTable(name = "sys_user_role", // joinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "role_id")}, // inverseJoinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")} //) // 放弃维护权 @ManyToMany(mappedBy = "roleSet") private Set<User> userSet = new HashSet<>();
级联添加操作,修改applicationContext.xml中的配置,从create改为update,这样每次执行时不会删除表在建立,而是直接更新
<!-- 注入jpa的配置信息 记载jpa的基本配置信息和jpa实现方式的配置信息--> <property name="jpaProperties"> <props> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property>
User类添加级联操作属性
@ManyToMany(targetEntity = Role.class, cascade = CascadeType.ALL) @JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "role_id")} ) private Set<Role> roleSet = new HashSet<>();
在Many2ManyTest中增加方法,执行级联添加操作
// 测试级联添加 @Test @Transactional @Rollback(false) public void testCascadeSave(){ // 操作主题为user User user = new User(); user.setUserName("Peter"); Role role = new Role(); role.setRoleName("Human"); user.getRoleSet().add(role); //配置用户到角色的映射 userDao.save(user); }
后台执行SQL如下,3张表中都执行了insert操作
数据库表中成功插入数据
测试级联删除
@Test @Transactional @Rollback(false) public void testCascadeDelete(){ User one = userDao.findOne(2L); userDao.delete(one); }
执行的SQL如下图
查看数据库表,三张表中关联数据已被删除
多表查询
对象导航查询:查询一个对象的同时,通过此对象查询他的关联对象
使用Chapter 04 中的 one2many项目,在test包中新建ObjectQueryTest测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class ObjectQueryTest { @Autowired private CustomerDao customerDao; @Autowired private LinkManDao linkManDao; ``` //测试对象导航查询,查询一个对象的时候,通过此对象查询所有的关联对象 @Test public void testQuery1(){ Customer customer = customerDao.getOne(2L); Set<LinkMan> linkManSet = customer.getLinkManSet(); for (LinkMan linkMan : linkManSet) { System.out.println(linkMan); } } }
执行testQuery1()方法,控制台报错,需要在方法上添加@Transactional
再次执行该方法,控制台显示查询成功
新增测试方法testQuery2(),使用findOne()执行查询
@Test @Transactional public void testQuery2(){ Customer customer = customerDao.findOne(2L); Set<LinkMan> linkManSet = customer.getLinkManSet(); for (LinkMan linkMan : linkManSet) { System.out.println(linkMan); } }
查询结果
对象导航查询默认使用延迟加载的形式查询,调用getOne方法不会立即发送查询,而是在使用关联对象的时候才会执行,如果将延迟加载改为立即加载,需要修改配置
fetch配置关联对象的加载方式
- FetchType.LAZY:延迟加载
- FetchType.EAGER:立即加载 修改Customer实体类,增加fetch配置
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER) private Set<LinkMan> linkManSet = new HashSet<>();
在ObjectQueryTest类中增加testQuery3(),从LinkMan查询Customer
@Test @Transactional public void testQuery3(){ LinkMan linkMan = linkManDao.getOne(2L); Customer customer = linkMan.getCustomer(); System.out.println(customer); }
控制台输出结果如下
从一方查询多方,查询结果为集合或者列表,默认使用延迟加载
从多方查询一方,默认使用立即加载
Spring Data JPA 完结 🎉🎉🎉