springdatajpa处理表间关系

表间关系

一对一:一对多的特殊情况

一对多:
一的一方:主表,
多的一方:从表,
外键:需要从表上新建一列作为主键,它的取值来源于主表的主键(一的一端)

多对多:
中间表:中间表中最少应该由两个字段组成,这两个字作为外键指向两张表的主键,又组成了联合主键
在这里插入图片描述

实体类的关系

包含关系:可以通过包含关系描述表间关系
继承关系

分析步骤

1、明确表关系
2、确定表关系(描述 外键|中间表)
3、编写实体类,在实体类中描述表关系(包含关系)
4、配置映射关系

案例分析

一对多关系和多对一双向关系

分析案例:
1、明确表关系:一对多关系
2、确定表关系(描述 外键中间表) 主表:用户表 从表:订单表 在从表上添加外键
3、编写实体类:在实体类中描述表关系(包含关系);用户:在用户实体类中包含一个订单的集合;订单:在订单实体中包含一个用户的对象
4、配置映射关系:使用jpa注解配置一对多映射关系(难点)

  • person.java

//配置用户和订单之间一对多的关系
/*
* 使用注解的形式配置多表关系
* 1、声明关系
* @OneToMany(targetEntity = Orders.class)
* targetEntity:对方对象的字节码
* 2、配置外键(中间表)
* @JoinColumn(name = “person_id”,referencedColumnName = “person_id”)
* name:外键字段名称
* referencedColumnName:参照的主表的主键字段名称
*
* 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
* */

@Entity
@Data
@Table(name = "person")
public class Person {
    @Id
    @Column(name = "person_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer personId;
    @Basic
    @Column(name = "name")
    private String name;
    @Basic
    @Column(name = "gender")
    private Integer gender;
    @Basic
    @Column(name = "person_addr")
    private String personAddr;
    @Basic
    @Column(name = "birthday")
    private Date birthday;
    //配置用户和订单之间一对多的关系
    /*
    * 使用注解的形式配置多表关系
    * 1、声明关系
    *  @OneToMany(targetEntity = Orders.class)
    *  targetEntity:对方对象的字节码
    * 2、配置外键(中间表)
    * @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    * name:外键字段名称
    * referencedColumnName:参照的主表的主键字段名称
    *
    * 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
    * */
    @OneToMany(targetEntity = Orders.class)
    @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    private Set<Orders> orders=new HashSet<>();
}
  • orders.java

一定要把从表的主键给删除掉(一点要删除掉,不然会报Repeated column in mapping for entity: com.jpa.model.Orders column: person_id (should be mapped with insert=“false” update=“false”)的错误)

//配置orders和person之间多对一的关系
/*
*使用注解的形式配置多对一
* 1、声明表关系
* @ManyToOne(targetEntity =Person.class)
* targetEntity:对方对象的字节码
* 2、配置外键(多对多使用中间表来配置)
* 配置到了多的一方,就在多的一方维护外键
* */

@Entity
@Data
@Table(name = "orders")
public class Orders {
    @Id
    @Column(name = "order_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer orderId;
//    @Basic
//    @Column(name = "person_id")
//    private Integer personId;
    @Basic
    @Column(name = "total_price")
    private Double totalPrice;
    @Basic
    @Column(name = "address")
    private String address;
    //配置orders和person之间多对一的关系
    /*
    *使用注解的形式配置多对一
    * 1、声明表关系
    *   @ManyToOne(targetEntity =Person.class)
    *   targetEntity:对方对象的字节码
    * 2、配置外键(多对多使用中间表来配置)
    *   配置到了多的一方,就在多的一方维护外键
    * */
    @ManyToOne(targetEntity =Person.class)
    @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    private Person person;
}
  • 测试类

先保存一的一端在保存多的一端

//保存一个客户一个联系人
    /*
    * 效果:用户和订单作为独立的数据保存到数据库中
    * 订单的外键为空
    * 原因是实体类中没有配置关系
    * */
    @Test
    @Transactional
    @Rollback(value = false)
    public void testAdd(){
        //创建一个用户和一个订单
        Person person = new Person();
        person.setName("张豆豆");
        person.setPersonAddr("河南");
        person.setBirthday(new Date());
        person.setGender(0);
        Orders orders = new Orders();

        orders.setAddress("天水");
        orders.setTotalPrice(548.0);
        //将用户表和订单表联系起来( //将球员加到球队)
        //从用户的角度看,发送两条insert语句,发送一条更新语句更新数据库(更新外键)
        //由于我们配置了用户到订单的关系,用户可以对主键进行维护
        person.getOrders().add(orders);
        
        //放弃主键维护的方式进行关联(配置订单到用户的关系[多对一的关系])
        orders.setPerson(person);//这种方式级联不起作用

        personDao.save(person);
        ordersDao.save(orders);
    }

测试一对多查询和多对一查询

测试一对多查询和多对一查询必须是单向关联的绝对不能多向关联

  • person 对orders 一对多 person表作为主表,orders表作为从表
  • Person.java
@Data
@Entity
@Table(name = "person")
public class Person {
    @Id
    @Column(name = "person_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer personId;
    @Basic
    @Column(name = "name")
    private String name;
    @Basic
    @Column(name = "gender")
    private Integer gender;
    @Basic
    @Column(name = "person_addr")
    private String personAddr;
    @Basic
    @Column(name = "birthday")
    private Date birthday;
    //配置用户和订单之间一对多的关系
    /*
    * 使用注解的形式配置多表关系
    * 1、声明关系
    *  @OneToMany(targetEntity = Orders.class)
    *  targetEntity:对方对象的字节码
    * 2、配置外键(中间表)
    * @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    * name:外键字段名称
    * referencedColumnName:参照的主表的主键字段名称
    *
    * 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
    * */
    @OneToMany(targetEntity = Orders.class,fetch = FetchType.EAGER)
    @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    private Set<Orders> orders=new HashSet<>();
}

fetch = FetchType.EAGER一定要加上急加载

  • 测试代码
 @Test
    public void testgetOrder(){
        Person person = personDao.findOne(1);
        System.out.println(person);
        Set<Orders> ordersSet = person.getOrders();
        ordersSet.forEach(orders -> System.out.println(orders));
    }
  • Orders.java
@Data
@Entity
@Table(name = "orders")
public class Orders {
    @Id
    @Column(name = "order_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer orderId;
//    @Basic
//    @Column(name = "person_id")
//    private Integer personId;
    @Basic
    @Column(name = "total_price")
    private Double totalPrice;
    @Basic
    @Column(name = "address")
    private String address;
    //配置orders和person之间多对一的关系
    /*
    *使用注解的形式配置多对一
    * 1、声明表关系
    *   @ManyToOne(targetEntity =Person.class)
    *   targetEntity:对方对象的字节码
    * 2、配置外键(多对多使用中间表来配置)
    *   配置到了多的一方,就在多的一方维护外键
    * */
    @ManyToOne(targetEntity =Person.class,fetch = FetchType.EAGER)
    @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    private Person person;
}
  • 示例代码
 @Test
    public void testgetOrder(){
        Orders orders = ordersDao.findOne(7);
        System.out.println(orders.getPerson());
    }

一对多删除的说明和级联的引入

级联是对主表也就是一的一端来说的,所以相关的配置也在一的一端

  • 删除说明
    在这里插入图片描述

级联:操作一个对象的同时操作其他的关联对象;级联有级联添加和级联删除
级联添加:比如添加一个用户的同时添加一个订单
级联删除:比如删除用户的同时,在从表中删除该用户的所有订单

级联操作:
需要区分操作主体
需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
casecade(配置级联)

  • 级联添加
  • Person.java
@Data
@Entity
@Table(name = "person")
public class Person {
    @Id
    @Column(name = "person_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer personId;
    @Basic
    @Column(name = "name")
    private String name;
    @Basic
    @Column(name = "gender")
    private Integer gender;
    @Basic
    @Column(name = "person_addr")
    private String personAddr;
    @Basic
    @Column(name = "birthday")
    private Date birthday;
    //配置用户和订单之间一对多的关系
    /*
    * 使用注解的形式配置多表关系
    * 1、声明关系
    *  @OneToMany(targetEntity = Orders.class)
    *  targetEntity:对方对象的字节码
    * 2、配置外键(中间表)
    * @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    * name:外键字段名称
    * referencedColumnName:参照的主表的主键字段名称
    *
    * 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
    * */
    @OneToMany(targetEntity = Orders.class,fetch = FetchType.EAGER,cascade =CascadeType.ALL)
    @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    private Set<Orders> orders=new HashSet<>();
}
  • Orders.java
@Data
@Entity
@Table(name = "orders")
public class Orders {
    @Id
    @Column(name = "order_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer orderId;
//    @Basic
//    @Column(name = "person_id")
//    private Integer personId;
    @Basic
    @Column(name = "total_price")
    private Double totalPrice;
    @Basic
    @Column(name = "address")
    private String address;
    //配置orders和person之间多对一的关系
    /*
    *使用注解的形式配置多对一
    * 1、声明表关系
    *   @ManyToOne(targetEntity =Person.class)
    *   targetEntity:对方对象的字节码
    * 2、配置外键(多对多使用中间表来配置)
    *   配置到了多的一方,就在多的一方维护外键
    * */
    @ManyToOne(targetEntity =Person.class,fetch = FetchType.EAGER)
    @JoinColumn(name = "person_id",referencedColumnName = "person_id")
    private Person person;
}
  • 级联添加测试类
/*
    * 级联添加:保存一个用户的同时,保存用户的订单
    * */
    @Test
    @Transactional
    @Rollback(false)
    public void testCasecade(){
        Person person = new Person();
        person.setName("石破天");
        person.setGender(1);
        person.setBirthday(new Date());
        person.setPersonAddr("侠客岛");

        Orders orders = new Orders();
        orders.setAddress("中原");
        orders.setTotalPrice(56.25);
        person.getOrders().add(orders);//必须使用这种主键维护的方式级联添加才起作用

        personDao.save(person);
    }
  • 级联删除测试类
/*
     * 级联删除:删除一个用户的同时,删除用户的订单
     * */
    @Test
    @Transactional
    @Rollback(false)
    public void testCasecadeDelete(){
        Person person = personDao.findOne(3);
        personDao.delete(person);
    }

CasecadeType all :所有
MERGE :更新
PERSIST :保存
REMOVE :删除

多对多关系

  • 分析
    在这里插入图片描述

Person.java

@ManyToMany(targetEntity = Role.class,fetch = FetchType.EAGER)
@JoinTable(name = “person_role”,
joinColumns = {@JoinColumn(name = “person_id”,referencedColumnName = “person_id”)},
inverseJoinColumns = {@JoinColumn(name = “role_id”,referencedColumnName = “role_id”)})

@Data
@Entity
@Table(name = "person")
public class Person {
    @Id
    @Column(name = "person_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer personId;
    @Basic
    @Column(name = "name")
    private String name;
    @Basic
    @Column(name = "gender")
    private Integer gender;
    @Basic
    @Column(name = "person_addr")
    private String personAddr;
    @Basic
    @Column(name = "birthday")
    private Date birthday;
    //配置和角色多对多关系
    /*
    *1、声明表关系的配置
    * targetEntity:对方对象的字节码
    * 2、配置中间表
    * joinColumns:当前对象在中间表中的外键
    * inverseJoinColumns:对方对象在中间表中的外键
    * */
    @ManyToMany(targetEntity = Role.class,fetch = FetchType.EAGER)
    @JoinTable(name = "person_role",
            joinColumns = {@JoinColumn(name = "person_id",referencedColumnName = "person_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")})
    private Set<Role> roles=new HashSet<>();
}

Role.java

@Data
@Entity
@Table(name = "role")
public class Role {
    @Id
    @Column(name = "role_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int roleId;
    @Basic
    @Column(name = "role_name")
    private String roleName;
    @Basic
    @Column(name = "role_desp")
    private String roleDesp;

    @ManyToMany(targetEntity = Person.class,fetch = FetchType.EAGER)
    @JoinTable(name = "person_role",
    joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")},
    inverseJoinColumns = {@JoinColumn(name = "person_id",referencedColumnName = "person_id")}
    )
    private Set<Person> persons=new HashSet<>();
}

测试类

 //保存一个用户 一个角色
    @Test
    @Transactional
    @Rollback(false)
    public void test(){
        Person person = new Person();
        person.setName("王尔德");
        person.setGender(1);
        person.setPersonAddr("北京");
        person.setBirthday(new Date());

        Role role = new Role();
        role.setRoleName("java程序员");
        role.setRoleDesp("写java程序的");
        //配置用户到角色的关系,可以对中间表中的数据进行维护(只能配置一个,配置用户到角色或者角色到用户)
        person.getRoles().add(role);

        personDao.save(person);
        roleDao.save(role);
    }

通过用户查询角色

  • 测试类
 @Test
    public void testSelect(){
        Person person = personDao.findOne(1);
        System.out.println(person);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值