Spring Data JPA的多表操作

一对多关系
案例:客户和联系人(客户:公司;一个客户可以具有多个联系人,一个联系人只从属于一家公司)
步骤:
  1. 明确表关系
    一对多
  2. 确定表关系(外键|中间表)
    客户表是主表,联系人表是从表,在从表上添加外键,指向主表的主键
  3. 编写实体类,在实体类中描述表关系(包含关系)
    客户实体类:在客户的实体类中包含联系人的集合
    联系人实体类:在联系人实体类包含客户的对象
  4. 配置映射关系
    使用jpa注解配置一对多映射关系
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "customer")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;
    @Column(name = "address")
    private String address;
    @Column(name = "industry")
    private String industry;
    @Column(name = "level")
    private String level;
    @Column(name = "name")
    private String name;
    @Column(name = "phone")
    private String phone;
    @Column(name = "source")
    private String source;

    //配置客户和联系人的关系,一对多关系(包含关系)
    /*
     * 使用注解配置多表关系
     *   1.声明关系
     *      @OneToMany:一对多关系
     *         targetEntity:对方对象的字节码对象
     *   2.配置外键|中间表
     *      @JoinColumn:配置外键
     *          name:外键名称
     *          referencedColumnName:参照的主表的主键名称
     *  在客户的实体类上(一的一方)添加了外键的配置,所以对用户而言,具备了维护外键的作用
     * */
    @OneToMany(targetEntity = LinkMan.class)
    @JoinColumn(name = "fk",referencedColumnName = "id")
    private Set<LinkMan> linkMans = new HashSet<LinkMan>();

	//setter and getter
    //toString
}
import javax.persistence.*;

@Entity
@Table(name = "linkman")
public class LinkMan {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "lkm_id")
    private Integer lkmid;
    @Column(name = "lkm_name")
    private String lkmname;
    @Column(name = "lkm_gender")
    private String lkmgender;
    @Column(name = "lkm_phone")
    private String lkmphone;
    @Column(name = "lkm_mobile")
    private String lkmmobile;
    @Column(name = "lkm_email")
    private String lkmemail;
    @Column(name = "lkm_position")
    private String lkmposition;
    @Column(name = "lkm_memo")
    private String lkmmeno;

    /*
    * 配置联系人到客户的多对一关系
    *   使用注解的形式配置多对一关系
    *       1.配置表关系
    *           @ManyToOne:配置多对一关系
    *       2.配置外键|中间表
    *           name:外键名称
    *           referencedColumnName:参照的主表的主键名称
    *
    * */
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "fk",referencedColumnName = "id")
    private Customer customer;

    //setter and getter
    //toString
}

测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OneToManyTest {

    @Resource
    private CustomerDao customerDao;
    @Resource
    private LinkManDao linkManDao;

    /*
    * 保存一个客户,保存一个联系人
    * */
    @Test
    @Transactional
    @Rollback(false)  //不自动回滚
    public void testAdd(){
        //创建客户,创建联系人
        Customer customer = new Customer();
        customer.setName("百度");

        LinkMan linkMan = new LinkMan();
        linkMan.setLkmname("李彦宏");

        //配置两个实体类的关系
        customer.getLinkMans().add(linkMan);//也可以linkMan.setCustomer(customer);

        customerDao.save(customer);
        linkManDao.save(linkMan);

    }
}

当两个实体类进行相互的配置关系时(即双方都配置),会出现多余的update语句,需要一的一方放弃外键维护权

//    @OneToMany(targetEntity = LinkMan.class)
//    @JoinColumn(name = "fk",referencedColumnName = "id")
//mappedBy 对方配置的属性的名称
@OneToMany(mappedBy  = "customer")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();

级联操作:

1.需要区分操作主体

2.需要在操作主题的实体类上,添加级联属性(需要添加到多表映射关系的注解上)

3.cascade(配置级联)

级联添加

/*
    * 级联添加,保存一个客户的同时,保存客户的所有联系人
    *   需要在操作主题的实体类上配置cascade属性
    * */
@Test
@Transactional
@Rollback(false)  //不自动回滚
public void testCasCade(){
    //创建客户,创建联系人
    Customer customer = new Customer();
    customer.setName("百度1");

    LinkMan linkMan = new LinkMan();
    linkMan.setLkmname("李彦宏1");

    //配置两个实体类的关系
    customer.getLinkMans().add(linkMan);  //也可以linkMan.setCustomer(customer);
    linkMan.setCustomer(customer);

    customerDao.save(customer);
    linkManDao.save(linkMan);

}

客户实体类

@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)  //级联所有操作
private Set<LinkMan> linkMans = new HashSet<LinkMan>();

级联删除

*
     * 级联删除,删除1号客户的同时,删除1号客户的所有联系人
     *   需要在操作主题的实体类上配置cascade属性
     * */
    @Test
    @Transactional
    @Rollback(false)  //不自动回滚
    public void testCasCadeRemove(){
    //查询1号客户
    Customer customer = customerDao.findOne(1);
    //删除1号客户
    customerDao.delete(customer);

}

客户实体类

@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)  //级联所有操作private Set<LinkMan> linkMans = new HashSet<LinkMan>();
注意:配置文件中注入jpa的配置信息
<!--注入jpa的配置信息-->
<property name="jpaProperties">
    <props>
        <prop key="hibernate.hbm2ddl.auto">update</prop>
    </props>
</property>
多对多关系
案例:用户和角色以及级联的添加
@Entity
@Table(name = "sys_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "userid")
    private Integer userid;
    @Column(name = "username")
    private String username;
    @Column(name = "userage")
    private Integer userage;
    /*
    * 配置用户到角色的多对多关系
    *   配置多对多关系
    *       1.声明表关系配置
    *           @ManyToMany 多对多
    *               targetEntity:代表对方的实体类字节码
    *       2.配置中间表(包含两个外键)
    *           @JoinTable
    *               name:中间表名
    *
    * */
    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @JoinTable(name = "sys_user_role",
            //joinColumns:当前对象在中间表的外键;referencedColumnName:外键来源于user实体类的主键userid
            joinColumns = {@JoinColumn(name = "fk_userid",referencedColumnName = "userid")},
            //inverseJoinColumns中间对象在中间表的外键
            inverseJoinColumns = {@JoinColumn(name = "fk_roleid",referencedColumnName = "roleid")})
    private Set<Role> roles = new HashSet<Role>();

    //setter and getter
    //toString
}
@Entity
@Table(name = "sys_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "roleid")
    private Integer roleid;
    @Column(name = "rolename")
    private String rolename;
    /*
    * 配置角色到用户的多对多关系
    *  放弃维护权:mappedBy对方配置映射关系的属性名称
    * */
    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<User>();

    //setter and getter
    //toString
}

接口

public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
}
public interface RoleDao extends JpaRepository<Role,Integer>, JpaSpecificationExecutor<Role> {
}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ManyToManyTest {

    @Resource
    UserDao userDao;
    @Resource
    RoleDao roleDao;

    /*
    * 多对多放弃维护权,被动的一方放弃
    * */
    @Test
    @Transactional
    @Rollback(false)
    public void manyToMany(){
        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);
    }

    /*
     *测试级联的添加(保存用户的同时保存用户的关联角色)
     * */
    @Test
    @Transactional
    @Rollback(false)
    public void casCade(){
        User user = new User();
        user.setUsername("老王1");

        Role role = new Role();
        role.setRolename("程序员1");

        //配置用户到角色的关系,可以对中间表的数据进行维护
        user.getRoles().add(role);
        //配置角色到用户的关系,可以对中间表的数据进行维护
        role.getUsers().add(user);

        userDao.save(user);
    }
}